[
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# node-waf configuration\n.lock-wscript\n\n# Dependency directory\nnode_modules\n\n# Optional npm cache directory\n.npm\n\n# Optional REPL history\n.node_repl_history\n\n# intellij\n.idea\n\n#yarn\nyarn.lock\n\n# OSX .DS_Store\n.DS_Store\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Thanks for taking the time to contribute to gpu.js. Follow these guidelines to make the process smoother:\n\n1. One feature per pull request. Each PR should have one focus, and all the code changes should be supporting that one feature or bug fix. Using a [separate branch](https://guides.github.com/introduction/flow/index.html) for each feature should help you manage developing multiple features at once.\n\n2. Follow the style of the file when it comes to syntax like curly braces and indents.\n\n3. Add a test for the feature or fix, if possible. See the `test` directory for existing tests and README describing how to run these tests.\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "<!-- If you don't mind add a fun gif or meme, but no pressure -->\n![A GIF or MEME to give some spice of the internet](url)\n\n## *What* is wrong?\n<!-- Ex. run takes really long -->\n\n## *Where* does it happen?\n<!-- Ex. In GPU.js when trying to run a math function in node.js on my mac -->\n\n## *How* do we replicate the issue?\n<!-- Please be specific as possible. Use dashes (-) or numbers (1.) to create a list of steps -->\n\n## *How* important is this (1-5)?\n<!-- On a scale from 1-5 where 5 is the most important how would you rate it? -->\n\n## Expected behavior (i.e. solution)\n<!-- What do you think should have happened? -->\n\n\n## Other Comments\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2019 gpu.js Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[<img width=\"100\" alt=\"Logo\" src=\"http://gpu.rocks/static/media/jelly.3587de60.png\">](http://gpu.rocks/)\n# GPU.js\nGPU.js is a JavaScript Acceleration library for GPGPU (General purpose computing on GPUs) in JavaScript for Web and Node.\nGPU.js automatically transpiles simple JavaScript functions into shader language and compiles them so they run on your GPU.\nIn case a GPU is not available, the functions will still run in regular JavaScript.\nFor some more quick concepts, see [Quick Concepts](https://github.com/gpujs/gpu.js/wiki/Quick-Concepts) on the wiki.\n\n\n[![Join the chat at https://gitter.im/gpujs/gpu.js](https://badges.gitter.im/gpujs/gpu.js.svg)](https://gitter.im/gpujs/gpu.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![Slack](https://slack.bri.im/badge.svg)](https://slack.bri.im)\n\n# What is this sorcery?\n\nCreates a GPU accelerated kernel transpiled from a javascript function that computes a single element in the 512 x 512 matrix (2D array).\nThe kernel functions are ran in tandem on the GPU often resulting in very fast computations!\nYou can run a benchmark of this [here](http://gpu.rocks). Typically, it will run 1-15x faster depending on your hardware.\nMatrix multiplication (perform matrix multiplication on 2 matrices of size 512 x 512) written in GPU.js:\n\n## Browser\n```html\n<script src=\"dist/gpu-browser.min.js\"></script>\n<script>\n    // GPU is a constructor and namespace for browser\n    const gpu = new GPU();\n    const multiplyMatrix = gpu.createKernel(function(a, b) {\n        let sum = 0;\n        for (let i = 0; i < 512; i++) {\n            sum += a[this.thread.y][i] * b[i][this.thread.x];\n        }\n        return sum;\n    }).setOutput([512, 512]);\n\n    const c = multiplyMatrix(a, b);\n</script>\n```\n\n## CDN\n``` \nhttps://unpkg.com/gpu.js@latest/dist/gpu-browser.min.js\nhttps://cdn.jsdelivr.net/npm/gpu.js@latest/dist/gpu-browser.min.js\n```\n\n## Node\n```js\nconst { GPU } = require('gpu.js');\nconst gpu = new GPU();\nconst multiplyMatrix = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < 512; i++) {\n        sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n}).setOutput([512, 512]);\n\nconst c = multiplyMatrix(a, b);\n```\n\n## Typescript\n```typescript\nimport { GPU } from 'gpu.js';\nconst gpu = new GPU();\nconst multiplyMatrix = gpu.createKernel(function(a: number[][], b: number[][]) {\n  let sum = 0;\n  for (let i = 0; i < 512; i++) {\n    sum += a[this.thread.y][i] * b[i][this.thread.x];\n  }\n  return sum;\n}).setOutput([512, 512]);\n\nconst c = multiplyMatrix(a, b) as number[][];\n```\n\n[Click here](/examples) for more typescript examples.\n\n# Table of Contents\n\nNotice documentation is off?  We do try our hardest, but if you find something,\n  [please bring it to our attention](https://github.com/gpujs/gpu.js/issues), or _[become a contributor](#contributors)_!\n\n* [Demos](#demos)\n* [Installation](#installation)\n* [`GPU` Settings](#gpu-settings)\n* [`gpu.createKernel` Settings](#gpucreatekernel-settings)\n  * [Declaring variables/functions within kernels](#declaring-variablesfunctions-within-kernels)\n* [Creating and Running Functions](#creating-and-running-functions)\n* [Debugging](#debugging)\n* [Accepting Input](#accepting-input)\n* [Graphical Output](#graphical-output)\n* [Combining Kernels](#combining-kernels)\n* [Create Kernel Map](#create-kernel-map)\n* [Adding Custom Functions](#adding-custom-functions)\n* [Adding Custom Functions Directly to Kernel](#adding-custom-functions-directly-to-kernel)\n* [Types](#types)\n* [Loops](#loops)\n* [Pipelining](#pipelining)\n  * [Cloning Textures](#cloning-textures-new-in-v2)\n  * [Cleanup pipeline texture memory](#cleanup-pipeline-texture-memory-new-in-v24)\n* [Offscreen Canvas](#offscreen-canvas)\n* [Cleanup](#cleanup)\n* [Flattened typed array support](#flattened-typed-array-support)\n* [Precompiled and Lighter Weight Kernels](#precompiled-and-lighter-weight-kernels)\n  * [using JSON](#using-json)\n  * [Exporting kernel](#exporting-kernel)\n* [Supported Math functions](#supported-math-functions)\n* [How to check what is supported](#how-to-check-what-is-supported)\n* [Typescript Typings](#typescript-typings)\n* [Destructured Assignments](#destructured-assignments-new-in-v2)\n* [Dealing With Transpilation](#dealing-with-transpilation)\n* [Full API reference](#full-api-reference)\n* [How possible in node](#how-possible-in-node)\n* [Testing](#testing)\n* [Building](#building)\n* [Contributors](#contributors)\n* [Contributing](#contributing)\n* [Terms Explained](#terms-explained)\n* [License](#license)\n\n## Demos\nGPU.js in the wild, all around the net.  Add yours here!\n* [Temperature interpolation using GPU.js](https://observablehq.com/@rveciana/temperature-interpolation-using-gpu-js)\n* [Julia Set Fractal using GPU.js](https://observablehq.com/@ukabuer/julia-set-fractal-using-gpu-js)\n* [Hello, gpu.js v2](https://observablehq.com/@fil/hello-gpu-js-v2)\n* [Basic gpu.js canvas example](https://observablehq.com/@rveciana/basic-gpu-js-canvas-example)\n* [Raster projection with GPU.js](https://observablehq.com/@fil/raster-projection-with-gpu-js)\n* [GPU.js Example: Slow Fade](https://observablehq.com/@robertleeplummerjr/gpu-js-example-slow-fade)\n* [GPU.JS CA Proof of Concept](https://observablehq.com/@alexlamb/gpu-js-ca-proof-of-concept)\n* [Image Convolution using GPU.js](https://observablehq.com/@ukabuer/image-convolution-using-gpu-js)\n* [Leaflet + gpu.js canvas](https://observablehq.com/@rveciana/leaflet-gpu-js-canvas)\n* [Image to GPU.js](https://observablehq.com/@fil/image-to-gpu)\n* [GPU Accelerated Heatmap using gpu.js](https://observablehq.com/@tracyhenry/gpu-accelerated-heatmap-using-gpu-js)\n* [Dijkstra’s algorithm in gpu.js](https://observablehq.com/@fil/dijkstras-algorithm-in-gpu-js)\n* [Voronoi with gpu.js](https://observablehq.com/@fil/voronoi-with-gpu-js)\n* [The gpu.js loop](https://observablehq.com/@fil/the-gpu-js-loop)\n* [GPU.js Example: Mandelbrot Set](https://observablehq.com/@robertleeplummerjr/gpu-js-example-mandelbrot-set)\n* [GPU.js Example: Mandelbulb](https://observablehq.com/@robertleeplummerjr/gpu-js-example-mandelbulb)\n* [Inverse of the distance with gpu.js](https://observablehq.com/@rveciana/inverse-of-the-distance-with-gpu-js)\n* [gpu.js laser detection v2](https://observablehq.com/@robertleeplummerjr/gpu-js-laser-detection-v2)\n* [GPU.js Canvas](https://observablehq.com/@hubgit/gpu-js-canvas)\n* [Video Convolution using GPU.js](https://observablehq.com/@robertleeplummerjr/video-convolution-using-gpu-js)\n* [GPU Rock Paper Scissors](https://observablehq.com/@alexlamb/gpu-rock-paper-scissors)\n* [Shaded relief with gpujs and d3js](https://observablehq.com/@rveciana/shaded-relief-with-gpujs-and-d3js/2)\n* [Caesar Cipher GPU.js Example](https://observablehq.com/@robertleeplummerjr/caesar-cipher-gpu-js-example)\n* [Matrix Multiplication GPU.js + Angular Example](https://ng-gpu.surge.sh/)\n* [Conway's game of life](https://observablehq.com/@brakdag/conway-game-of-life-gpu-js)\n\n## Installation\nOn Linux, ensure you have the correct header files installed: `sudo apt install mesa-common-dev libxi-dev` (adjust for your distribution)\n\n### npm\n\n```bash\nnpm install gpu.js --save\n```\n\n### yarn\n\n```bash\nyarn add gpu.js\n```\n\n[npm package](https://www.npmjs.com/package/gpu.js)\n### Node\n```js\nconst { GPU } = require('gpu.js');\nconst gpu = new GPU();\n```\n\n### Node Typescript **New in V2!**\n```js\nimport { GPU } from 'gpu.js';\nconst gpu = new GPU();\n```\n\n### Browser\n\nDownload the latest version of GPU.js and include the files in your HTML page using the following tags:\n\n```html\n<script src=\"dist/gpu-browser.min.js\"></script>\n<script>\n    const gpu = new GPU();\n</script>\n```\n\n## `GPU` Settings\nSettings are an object used to create an instance of `GPU`.  Example: `new GPU(settings)`\n* `canvas`: `HTMLCanvasElement`.  Optional.  For sharing canvas.  Example: use THREE.js and GPU.js on same canvas.\n* `context`: `WebGL2RenderingContext` or `WebGLRenderingContext`.  For sharing rendering context.  Example: use THREE.js and GPU.js on same rendering context.\n* `mode`: Defaults to 'gpu', other values generally for debugging:\n  * 'dev' **New in V2!**: VERY IMPORTANT!  Use this so you can breakpoint and debug your kernel!  This wraps your javascript in loops but DOES NOT transpile your code, so debugging is much easier.\n  * 'webgl': Use the `WebGLKernel` for transpiling a kernel\n  * 'webgl2': Use the `WebGL2Kernel` for transpiling a kernel\n  * 'headlessgl' **New in V2!**: Use the `HeadlessGLKernel` for transpiling a kernel\n  * 'cpu': Use the `CPUKernel` for transpiling a kernel\n* `onIstanbulCoverageVariable`: Removed in v2.11.0, use v8 coverage\n* `removeIstanbulCoverage`: Removed in v2.11.0, use v8 coverage\n\n## `gpu.createKernel` Settings\nSettings are an object used to create a `kernel` or `kernelMap`.  Example: `gpu.createKernel(settings)`\n* `output` or `kernel.setOutput(output)`: `array` or `object` that describes the output of kernel.  When using `kernel.setOutput()` you _can_ call it after the kernel has compiled if `kernel.dynamicOutput` is `true`, to resize your output.  Example:\n  * as array: `[width]`, `[width, height]`, or `[width, height, depth]`\n  * as object: `{ x: width, y: height, z: depth }`\n* `pipeline` or `kernel.setPipeline(true)` **New in V2!**: boolean, default = `false`\n  * Causes `kernel()` calls to output a `Texture`.  To get array's from a `Texture`, use:\n  ```js\n  const result = kernel();\n  result.toArray();\n  ```\n  * Can be passed _directly_ into kernels, and is preferred:\n  ```js\n  kernel(texture);\n  ```\n* `graphical` or `kernel.setGraphical(boolean)`: boolean, default = `false`\n* `loopMaxIterations` or `kernel.setLoopMaxIterations(number)`: number, default = 1000\n* `constants` or `kernel.setConstants(object)`: object, default = null\n* `dynamicOutput` or `kernel.setDynamicOutput(boolean)`: boolean, default = false - turns dynamic output on or off\n* `dynamicArguments` or `kernel.setDynamicArguments(boolean)`: boolean, default = false - turns dynamic arguments (use different size arrays and textures) on or off\n* `optimizeFloatMemory` or `kernel.setOptimizeFloatMemory(boolean)` **New in V2!**: boolean - causes a float32 texture to use all 4 channels rather than 1, using less memory, but consuming more GPU.\n* `precision` or `kernel.setPrecision('unsigned' | 'single')` **New in V2!**: 'single' or 'unsigned' - if 'single' output texture uses float32 for each colour channel rather than 8\n* `fixIntegerDivisionAccuracy` or `kernel.setFixIntegerDivisionAccuracy(boolean)` : boolean - some cards have accuracy issues dividing by factors of three and some other primes (most apple kit?). Default on for affected cards, disable if accuracy not required.\n* `functions` or `kernel.setFunctions(array)`: array, array of functions to be used inside kernel.  If undefined, inherits from `GPU` instance. Can also be an array of `{ source: function, argumentTypes: object, returnType: string }`.\n* `nativeFunctions` or `kernel.setNativeFunctions(array)`: object, defined as: `{ name: string, source: string, settings: object }`.  This is generally set via using GPU.addNativeFunction()\n  * VERY IMPORTANT! - Use this to add special native functions to your environment when you need specific functionality is needed.\n* `injectedNative` or `kernel.setInjectedNative(string)` **New in V2!**: string, defined as: `{ functionName: functionSource }`.  This is for injecting native code before translated kernel functions.\n* `subKernels` or `kernel.setSubKernels(array)`: array, generally inherited from `GPU` instance.\n* `immutable` or `kernel.setImmutable(boolean)`: boolean, default = `false`\n  * VERY IMPORTANT! - This was removed in v2.4.0 - v2.7.0, and brought back in v2.8.0 [by popular demand](https://github.com/gpujs/gpu.js/issues/572), please upgrade to get the feature\n* `strictIntegers` or `kernel.setStrictIntegers(boolean)`: boolean, default = `false` - allows undefined argumentTypes and function return values to use strict integer declarations.\n* `useLegacyEncoder` or `kernel.setUseLegacyEncoder(boolean)`: boolean, default `false` - more info [here](https://github.com/gpujs/gpu.js/wiki/Encoder-details).\n* `tactic` or `kernel.setTactic('speed' | 'balanced' | 'precision')` **New in V2!**: Set the kernel's tactic for compilation.  Allows for compilation to better fit how GPU.js is being used (internally uses `lowp` for 'speed', `mediump` for 'balanced', and `highp` for 'precision').  Default is lowest resolution supported for output.\n\n\n## Creating and Running Functions\nDepending on your output type, specify the intended size of your output.\nYou cannot have an accelerated function that does not specify any output size.\n\nOutput size   |  How to specify output size   |  How to reference in kernel\n--------------|-------------------------------|--------------------------------\n 1D           | `[length]`                    |  `value[this.thread.x]`\n 2D           | `[width, height]`             |  `value[this.thread.y][this.thread.x]`\n 3D           | `[width, height, depth]`      |  `value[this.thread.z][this.thread.y][this.thread.x]`\n\n```js\nconst settings = {\n    output: [100]\n};\n```\n\nor\n\n```js\n// You can also use x, y, and z\nconst settings = {\n    output: { x: 100 }\n};\n```\n\nCreate the function you want to run on the GPU. The first input parameter to `createKernel` is a kernel function which will compute a single number in the output. The thread identifiers, `this.thread.x`, `this.thread.y` or `this.thread.z` will allow you to specify the appropriate behavior of the kernel function at specific positions of the output.\n\n```js\nconst kernel = gpu.createKernel(function() {\n    return this.thread.x;\n}, settings);\n```\n\nThe created function is a regular JavaScript function, and you can use it like one.\n\n```js\nkernel();\n// Result: Float32Array[0, 1, 2, 3, ... 99]\n```\n\nNote: Instead of creating an object, you can use the chainable shortcut methods as a neater way of specifying settings.\n\n```js\nconst kernel = gpu.createKernel(function() {\n    return this.thread.x;\n}).setOutput([100]);\n\nkernel();\n// Result: Float32Array[0, 1, 2, 3, ... 99]\n```\n\n### Declaring variables/functions within kernels\n\nGPU.js makes variable declaration inside kernel functions easy.  Variable types supported are:\n* `Number` (Integer or Number), example: `let value = 1` or `let value = 1.1` \n* `Boolean`, example: `let value = true`\n* `Array(2)`, example: `let value = [1, 1]`\n* `Array(3)`, example: `let value = [1, 1, 1]`\n* `Array(4)`, example: `let value = [1, 1, 1, 1]`\n* `private Function`, example: `function myFunction(value) { return value + 1; }`\n\n`Number` kernel example:\n```js\nconst kernel = gpu.createKernel(function() {\n const i = 1;\n const j = 0.89;\n return i + j;\n}).setOutput([100]);\n```\n\n`Boolean` kernel example:\n```js\nconst kernel = gpu.createKernel(function() {\n  const i = true;\n  if (i) return 1;\n  return 0;\n}).setOutput([100]);\n```\n\n`Array(2)` kernel examples:\nUsing declaration\n```js\nconst kernel = gpu.createKernel(function() {\n const array2 = [0.08, 2];\n return array2;\n}).setOutput([100]);\n```\n\nDirectly returned\n```js\nconst kernel = gpu.createKernel(function() {\n return [0.08, 2];\n}).setOutput([100]);\n```\n\n`Array(3)` kernel example:\nUsing declaration\n```js\nconst kernel = gpu.createKernel(function() {\n const array2 = [0.08, 2, 0.1];\n return array2;\n}).setOutput([100]);\n```\n\nDirectly returned\n```js\nconst kernel = gpu.createKernel(function() {\n return [0.08, 2, 0.1];\n}).setOutput([100]);\n```\n\n`Array(4)` kernel example:\nUsing declaration\n```js\nconst kernel = gpu.createKernel(function() {\n const array2 = [0.08, 2, 0.1, 3];\n return array2;\n}).setOutput([100]);\n```\n\nDirectly returned\n```js\nconst kernel = gpu.createKernel(function() {\n return [0.08, 2, 0.1, 3];\n}).setOutput([100]);\n```\n\n`private Function` kernel example:\n```js\nconst kernel = gpu.createKernel(function() {\n  function myPrivateFunction() {\n    return [0.08, 2, 0.1, 3];\n  }\n  \n  return myPrivateFunction(); // <-- type inherited here\n}).setOutput([100]);\n```\n\n## Debugging\nDebugging can be done in a variety of ways, and there are different levels of debugging.\n* Debugging kernels with breakpoints can be done with `new GPU({ mode: 'dev' })`\n  * This puts `GPU.js` into development mode.  Here you can insert breakpoints, and be somewhat liberal in how your kernel is developed.\n  * This mode _does not_ actually \"compile\" (parse, and eval) a kernel, it simply iterates on your code.\n  * You can break a lot of rules here, because your kernel's function still has context of the state it came from.\n  * PLEASE NOTE: Mapped kernels are not supported in this mode.  They simply cannot work because of context.\n  * Example:\n    ```js\n    const gpu = new GPU({ mode: 'dev' });\n    const kernel = gpu.createKernel(function(arg1, time) {\n        // put a breakpoint on the next line, and watch it get hit\n        const v = arg1[this.thread.y][this.thread.x * time];\n        return v;\n    }, { output: [100, 100] });\n    ```\n* Debugging actual kernels on CPU with `debugger`:\n  * This will cause \"breakpoint\" like behaviour, but in an actual CPU kernel.  You'll peer into the compiled kernel here, for a CPU.\n  * Example:\n    ```js\n    const gpu = new GPU({ mode: 'cpu' });\n    const kernel = gpu.createKernel(function(arg1, time) {\n        debugger; // <--NOTICE THIS, IMPORTANT!\n        const v = arg1[this.thread.y][this.thread.x * time];\n        return v;\n    }, { output: [100, 100] });\n    ```\n* Debugging an actual GPU kernel:\n  * There are no breakpoints available on the GPU, period.  By providing the same level of abstraction and logic, the above methods should give you enough insight to debug, but sometimes we just need to see what is on the GPU.\n  * Be VERY specific and deliberate, and use the kernel to your advantage, rather than just getting frustrated or giving up.\n  * Example:\n    ```js\n    const gpu = new GPU({ mode: 'cpu' });\n    const kernel = gpu.createKernel(function(arg1, time) {\n      const x = this.thread.x * time;\n      return x; // <--NOTICE THIS, IMPORTANT!\n      const v = arg1[this.thread.y][x];\n      return v;\n    }, { output: [100, 100] });\n    ```\n    In this example, we return early the value of x, to see exactly what it is.  The rest of the logic is ignored, but now you can see the value that is calculated from `x`, and debug it.\n    This is an overly simplified problem.\n  * Sometimes you need to solve graphical problems, that can be done similarly.\n  * Example:\n    ```js\n    const gpu = new GPU({ mode: 'cpu' });\n    const kernel = gpu.createKernel(function(arg1, time) {\n      const x = this.thread.x * time;\n      if (x < 4 || x > 2) {\n        // RED\n        this.color(1, 0, 0); // <--NOTICE THIS, IMPORTANT!\n        return;\n      }\n      if (x > 6 && x < 12) {\n        // GREEN\n        this.color(0, 1, 0); // <--NOTICE THIS, IMPORTANT!\n        return;\n      }\n      const v = arg1[this.thread.y][x];\n      return v;\n    }, { output: [100, 100], graphical: true });\n    ```\n    Here we are making the canvas red or green depending on the value of `x`.\n\n## Accepting Input\n### Supported Input Types\n* Numbers\n* 1d,2d, or 3d Array of numbers\n  * Arrays of `Array`, `Float32Array`, `Int16Array`, `Int8Array`, `Uint16Array`, `uInt8Array`\n* Pre-flattened 2d or 3d Arrays using 'Input', for faster upload of arrays\n  * Example:\n  ```js\n  const { input } = require('gpu.js');\n  const value = input(flattenedArray, [width, height, depth]);\n  ```\n* HTML Image\n* Array of HTML Images\n* Video Element **New in V2!**\nTo define an argument, simply add it to the kernel function like regular JavaScript.\n\n### Input Examples\n```js\nconst kernel = gpu.createKernel(function(x) {\n    return x;\n}).setOutput([100]);\n\nkernel(42);\n// Result: Float32Array[42, 42, 42, 42, ... 42]\n```\n\nSimilarly, with array inputs:\n\n```js\nconst kernel = gpu.createKernel(function(x) {\n    return x[this.thread.x % 3];\n}).setOutput([100]);\n\nkernel([1, 2, 3]);\n// Result: Float32Array[1, 2, 3, 1, ... 1 ]\n```\n\nAn HTML Image:\n\n```js\nconst kernel = gpu.createKernel(function(image) {\n    const pixel = image[this.thread.y][this.thread.x];\n    this.color(pixel[0], pixel[1], pixel[2], pixel[3]);\n})\n  .setGraphical(true)\n  .setOutput([100, 100]);\n\nconst image = document.createElement('img');\nimage.src = 'my/image/source.png';\nimage.onload = () => {\n  kernel(image);\n  // Result: colorful image\n  \n  document.getElementsByTagName('body')[0].appendChild(kernel.canvas);\n};\n```\n\nAn Array of HTML Images:\n\n```js\nconst kernel = gpu.createKernel(function(image) {\n    const pixel = image[this.thread.z][this.thread.y][this.thread.x];\n    this.color(pixel[0], pixel[1], pixel[2], pixel[3]);\n})\n  .setGraphical(true)\n  .setOutput([100, 100]);\n\nconst image1 = document.createElement('img');\nimage1.src = 'my/image/source1.png';\nimage1.onload = onload;\nconst image2 = document.createElement('img');\nimage2.src = 'my/image/source2.png';\nimage2.onload = onload;\nconst image3 = document.createElement('img');\nimage3.src = 'my/image/source3.png';\nimage3.onload = onload;\nconst totalImages = 3;\nlet loadedImages = 0;\nfunction onload() {\n  loadedImages++;\n  if (loadedImages === totalImages) {\n    kernel([image1, image2, image3]);\n    // Result: colorful image composed of many images\n\n     document.getElementsByTagName('body')[0].appendChild(kernel.canvas);\n  }\n};\n```\n\nAn HTML Video: **New in V2!**\n\n```js\nconst kernel = gpu.createKernel(function(videoFrame) {\n    const pixel = videoFrame[this.thread.y][this.thread.x];\n    this.color(pixel[0], pixel[1], pixel[2], pixel[3]);\n})\n  .setGraphical(true)\n  .setOutput([100, 100]);\n\nconst video = new document.createElement('video');\nvideo.src = 'my/video/source.webm';\nkernel(image); //note, try and use requestAnimationFrame, and the video should be ready or playing\n// Result: video frame\n```\n\n## Graphical Output\n\nSometimes, you want to produce a `canvas` image instead of doing numeric computations. To achieve this, set the `graphical` flag to `true` and the output dimensions to `[width, height]`. The thread identifiers will now refer to the `x` and `y` coordinate of the pixel you are producing. Inside your kernel function, use `this.color(r,g,b)` or `this.color(r,g,b,a)` to specify the color of the pixel.\n\nFor performance reasons, the return value of your function will no longer be anything useful. Instead, to display the image, retrieve the `canvas` DOM node and insert it into your page.\n\n```js\nconst render = gpu.createKernel(function() {\n    this.color(0, 0, 0, 1);\n})\n  .setOutput([20, 20])\n  .setGraphical(true);\n\nrender();\n\nconst canvas = render.canvas;\ndocument.getElementsByTagName('body')[0].appendChild(canvas);\n```\n\nNote: To animate the rendering, use `requestAnimationFrame` instead of `setTimeout` for optimal performance. For more information, see [this](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).\n\n\n### .getPixels() **New in V2!**\nTo make it easier to get pixels from a context, use `kernel.getPixels()`, which returns a flat array similar to what you get from WebGL's `readPixels` method.\nA note on why: webgl's `readPixels` returns an array ordered differently from javascript's `getImageData`.\nThis makes them behave similarly.\nWhile the values may be somewhat different, because of graphical precision available in the kernel, and alpha, this allows us to easily get pixel data in unified way.\n\nExample:\n```js\nconst render = gpu.createKernel(function() {\n    this.color(0, 0, 0, 1);\n})\n  .setOutput([20, 20])\n  .setGraphical(true);\n\nrender();\nconst pixels = render.getPixels();\n// [r,g,b,a, r,g,b,a...\n```\n\n### Alpha\n\nCurrently, if you need alpha do something like enabling `premultipliedAlpha` with your own gl context:\n```js\nconst canvas = DOM.canvas(500, 500);\nconst gl = canvas.getContext('webgl2', { premultipliedAlpha: false });\n\nconst gpu = new GPU({\n  canvas,\n  context: gl\n});\nconst krender = gpu.createKernel(function(x) {\n  this.color(this.thread.x / 500, this.thread.y / 500, x[0], x[1]);\n})\n  .setOutput([500, 500])\n  .setGraphical(true);\n ```\n\n## Combining kernels\n\nSometimes you want to do multiple math operations on the gpu without the round trip penalty of data transfer from cpu to gpu to cpu to gpu, etc.  To aid this there is the `combineKernels` method.\n_**Note:**_ Kernels can have different output sizes.\n```js\nconst add = gpu.createKernel(function(a, b) {\n  return a[this.thread.x] + b[this.thread.x];\n}).setOutput([20]);\n\nconst multiply = gpu.createKernel(function(a, b) {\n  return a[this.thread.x] * b[this.thread.x];\n}).setOutput([20]);\n\nconst superKernel = gpu.combineKernels(add, multiply, function(a, b, c) {\n  return multiply(add(a, b), c);\n});\n\nsuperKernel(a, b, c);\n```\nThis gives you the flexibility of using multiple transformations but without the performance penalty, resulting in a much much MUCH faster operation.\n\n## Create Kernel Map\n\nSometimes you want to do multiple math operations in one kernel, and save the output of each of those operations. An example is **Machine Learning** where the previous output is required for back propagation. To aid this there is the `createKernelMap` method.\n\n### object outputs\n```js\nconst megaKernel = gpu.createKernelMap({\n  addResult: function add(a, b) {\n    return a + b;\n  },\n  multiplyResult: function multiply(a, b) {\n    return a * b;\n  },\n}, function(a, b, c) {\n  return multiply(add(a[this.thread.x], b[this.thread.x]), c[this.thread.x]);\n}, { output: [10] });\n\nmegaKernel(a, b, c);\n// Result: { addResult: Float32Array, multiplyResult: Float32Array, result: Float32Array }\n```\n### array outputs\n```js\nconst megaKernel = gpu.createKernelMap([\n  function add(a, b) {\n    return a + b;\n  },\n  function multiply(a, b) {\n    return a * b;\n  }\n], function(a, b, c) {\n  return multiply(add(a[this.thread.x], b[this.thread.x]), c[this.thread.x]);\n}, { output: [10] });\n\nmegaKernel(a, b, c);\n// Result: { 0: Float32Array, 1: Float32Array, result: Float32Array }\n```\nThis gives you the flexibility of using parts of a single transformation without the performance penalty, resulting in much much _MUCH_ faster operation.\n\n## Adding custom functions\n### To `GPU` instance\nuse `gpu.addFunction(function() {}, settings)` for adding custom functions to all kernels.  Needs to be called BEFORE `gpu.createKernel`. Example:\n\n\n```js\ngpu.addFunction(function mySuperFunction(a, b) {\n  return a - b;\n});\nfunction anotherFunction(value) {\n  return value + 1;\n}\ngpu.addFunction(anotherFunction);\nconst kernel = gpu.createKernel(function(a, b) {\n  return anotherFunction(mySuperFunction(a[this.thread.x], b[this.thread.x]));\n}).setOutput([20]);\n```\n\n### To `Kernel` instance\nuse `kernel.addFunction(function() {}, settings)` for adding custom functions to all kernels.  Example:\n\n\n```js\nkernel.addFunction(function mySuperFunction(a, b) {\n  return a - b;\n});\nfunction anotherFunction(value) {\n  return value + 1;\n}\nkernel.addFunction(anotherFunction);\nconst kernel = gpu.createKernel(function(a, b) {\n  return anotherFunction(mySuperFunction(a[this.thread.x], b[this.thread.x]));\n}).setOutput([20]);\n```\n\n### Adding strongly typed functions\n\nTo manually strongly type a function you may use settings.\nBy setting this value, it makes the build step of the kernel less resource intensive.\nSettings take an optional hash values:\n* `returnType`: optional, defaults to inference from `FunctionBuilder`, the value you'd like to return from the function.\n* `argumentTypes`: optional, defaults to inference from `FunctionBuilder` for each param, a hash of param names with values of the return types.\n\nExample on `GPU` instance:\n```js\ngpu.addFunction(function mySuperFunction(a, b) {\n  return [a - b[1], b[0] - a];\n}, { argumentTypes: { a: 'Number', b: 'Array(2)'}, returnType: 'Array(2)' });\n```\n\nExample on `Kernel` instance:\n```js\nkernel.addFunction(function mySuperFunction(a, b) {\n  return [a - b[1], b[0] - a];\n}, { argumentTypes: { a: 'Number', b: 'Array(2)'}, returnType: 'Array(2)' });\n```\n\nNOTE: GPU.js infers types if they are not defined and is generally able to detect the types you need, however\n'Array(2)', 'Array(3)', and 'Array(4)' are exceptions, at least on the kernel level.  Also, it is nice to have power\nover the automatic type inference system.\n\n## Adding custom functions directly to kernel\n```js\nfunction mySuperFunction(a, b) {\n  return a - b;\n}\nconst kernel = gpu.createKernel(function(a, b) {\n  return mySuperFunction(a[this.thread.x], b[this.thread.x]);\n})\n  .setOutput([20])\n  .setFunctions([mySuperFunction]);\n\n```\n\n\n## Types\nGPU.js does type inference when types are not defined, so even if you code weak type, you are typing strongly typed.\nThis is needed because c++, which glsl is a subset of, is, of course, strongly typed.\nTypes that can be used with GPU.js are as follows:\n\n### Argument Types\n* 'Array'\n* 'Array(2)' **New in V2!**\n* 'Array(3)' **New in V2!**\n* 'Array(4)' **New in V2!**\n* 'Array1D(2)' **New in V2!**\n* 'Array1D(3)' **New in V2!**\n* 'Array1D(4)' **New in V2!**\n* 'Array2D(2)' **New in V2!**\n* 'Array2D(3)' **New in V2!**\n* 'Array2D(4)' **New in V2!**\n* 'Array3D(2)' **New in V2!**\n* 'Array3D(3)' **New in V2!**\n* 'Array3D(4)' **New in V2!**\n* 'HTMLCanvas' **New in V2.6**\n* 'OffscreenCanvas' **New in V2.13**\n* 'HTMLImage'\n* 'ImageBitmap' **New in V2.14**\n* 'ImageData' **New in V2.15**\n* 'HTMLImageArray'\n* 'HTMLVideo' **New in V2!**\n* 'Number'\n* 'Float'\n* 'Integer'\n* 'Boolean' **New in V2!**\n\n### Return Types\nNOTE: These refer the the return type of the kernel function, the actual result will always be a collection in the size of the defined `output`\n* 'Array(2)'\n* 'Array(3)'\n* 'Array(4)'\n* 'Number'\n* 'Float'\n* 'Integer'\n\n### Internal Types\nTypes generally used in the `Texture` class, for #pipelining or for advanced usage.\n* 'ArrayTexture(1)' **New in V2!**\n* 'ArrayTexture(2)' **New in V2!**\n* 'ArrayTexture(3)' **New in V2!**\n* 'ArrayTexture(4)' **New in V2!**\n* 'NumberTexture'\n* 'MemoryOptimizedNumberTexture' **New in V2!**\n\n## Loops\n* Any loops defined inside the kernel must have a maximum iteration count defined by the loopMaxIterations setting.\n* Other than defining the iterations by a constant or fixed value as shown [Dynamic sized via constants](dynamic-sized-via-constants), you can also simply pass the number of iterations as a variable to the kernel\n\n### Dynamic sized via constants\n```js\nconst matMult = gpu.createKernel(function(a, b) {\n    var sum = 0;\n    for (var i = 0; i < this.constants.size; i++) {\n        sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n}, {\n  constants: { size: 512 },\n  output: [512, 512],\n});\n```\n\n### Fixed sized\n```js\nconst matMult = gpu.createKernel(function(a, b) {\n    var sum = 0;\n    for (var i = 0; i < 512; i++) {\n        sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n}).setOutput([512, 512]);\n```\n\n## Pipelining\n[Pipeline](https://en.wikipedia.org/wiki/Pipeline_(computing)) is a feature where values are sent directly from kernel to kernel via a texture.\nThis results in extremely fast computing.  This is achieved with the kernel setting `pipeline: boolean` or by calling `kernel.setPipeline(true)`\nIn an effort to make the CPU and GPU work similarly, pipeline on CPU and GPU modes causes the kernel result to be reused when `immutable: false` (which is default).\nIf you'd like to keep kernel results around, use `immutable: true` and ensure you cleanup memory:\n* In gpu mode using `texture.delete()` when appropriate.\n* In cpu mode allowing values to go out of context\n\n### Cloning Textures **New in V2!**\nWhen using pipeline mode the outputs from kernels can be cloned using `texture.clone()`.\n\n```js\nconst kernel1 = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n})\n  .setPipeline(true)\n  .setOutput([100]);\n\nconst kernel2 = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n})\n  .setOutput([100]);\n\nconst result1 = kernel1(array);\n// Result: Texture\nconsole.log(result1.toArray());\n// Result: Float32Array[0, 1, 2, 3, ... 99]\n\nconst result2 = kernel2(result1);\n// Result: Float32Array[0, 1, 2, 3, ... 99]\n```\n\n### Cleanup pipeline texture memory **New in V2.4!**\nWhen using `kernel.immutable = true` recycling GPU memory is handled internally, but a good practice is to clean up memory you no longer need it.\nCleanup kernel outputs by using `texture.delete()` to keep GPU memory as small as possible.\n\nNOTE: Internally textures will only release from memory if there are no references to them.\nWhen using pipeline mode on a kernel `K` the output for each call will be a newly allocated texture `T`.\nIf, after getting texture `T` as an output, `T.delete()` is called, the next call to K will reuse `T` as its output texture.\n\nAlternatively, if you'd like to clear out a `texture` and yet keep it in memory, you may use `texture.clear()`, which\nwill cause the `texture` to persist in memory, but its internal values to become all zeros.\n\n## Offscreen Canvas\nGPU.js supports offscreen canvas where available.  Here is an example of how to use it with two files, `gpu-worker.js`, and `index.js`:\n\nfile: `gpu-worker.js`\n```js\nimportScripts('path/to/gpu.js');\nonmessage = function() {\n  // define gpu instance\n  const gpu = new GPU();\n\n  // input values\n  const a = [1,2,3];\n  const b = [3,2,1];\n\n  // setup kernel\n  const kernel = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] - b[this.thread.x];\n  })\n    .setOutput([3]);\n\n  // output some results!\n  postMessage(kernel(a, b));\n};\n```\n\nfile: `index.js`\n```js\nvar worker = new Worker('gpu-worker.js');\nworker.onmessage = function(e) {\n  var result = e.data;\n  console.log(result);\n};\n```\n\n## Cleanup\n* for instances of `GPU` use the `destroy` method.  Example: `gpu.destroy()`\n* for instances of `Kernel` use the `destroy` method.  Example: `kernel.destroy()`\n* for instances of `Texture` use the `delete` method. Example: `texture.delete()`\n* for instances of `Texture` that you might want to reuse/reset to zeros, use the `clear` method. Example: `texture.clear()`\n\n## Flattened typed array support\nTo use the useful `x`, `y`, `z` `thread` lookup api inside of GPU.js, and yet use flattened arrays, there is the `Input` type.\nThis is generally much faster for when sending values to the gpu, especially with larger data sets.  Usage example:\n```js\nconst { GPU, input, Input } = require('gpu.js');\nconst gpu = new GPU();\nconst kernel = gpu.createKernel(function(a, b) {\n  return a[this.thread.y][this.thread.x] + b[this.thread.y][this.thread.x];\n}).setOutput([3,3]);\n\n\nkernel(\n  input(\n    new Float32Array([1,2,3,4,5,6,7,8,9]),\n    [3, 3]\n  ),\n  input(\n    new Float32Array([1,2,3,4,5,6,7,8,9]),\n    [3, 3]\n  )\n);\n```\n\nNote: `input(value, size)` is a simple pointer for `new Input(value, size)`\n\n## Precompiled and Lighter Weight Kernels\n\n### using JSON\nGPU.js packs a lot of functionality into a single file, such as a complete javascript parse, which may not be needed in some cases.\nTo aid in keeping your kernels lightweight, the `kernel.toJSON()` method was added.\nThis allows you to reuse a previously built kernel, without the need to re-parse the javascript.\nHere is an example:\n\n```js\nconst gpu = new GPU();\nconst kernel = gpu.createKernel(function() {\n  return [1,2,3,4];\n}, { output: [1] });\nconsole.log(kernel()); // [Float32Array([1,2,3,4])];\nconst json = kernel.toJSON();\nconst newKernelFromJson = gpu.createKernel(json);\nconsole.log(newKernelFromJSON()); // [Float32Array([1,2,3,4])];\n```\n\nNOTE: There is lighter weight, pre-built, version of GPU.js to assist with serializing from to and from json in the dist folder of the project, which include:\n* [dist/gpu-browser-core.js](dist/gpu-browser-core.js)\n* [dist/gpu-browser-core.min.js](dist/gpu-browser-core.min.js)\n\n### Exporting kernel\nGPU.js supports seeing exactly how it is interacting with the graphics processor by means of the `kernel.toString(...)` method.\nThis method, when called, creates a kernel that executes _exactly the instruction set given to the GPU (or CPU)_ *as a\nvery tiny reusable function* that instantiates a kernel.\n\nNOTE: When exporting a kernel and using `constants` the following constants are *not changeable*:\n* `Array(2)`\n* `Array(3)`\n* `Array(4)`\n* `Integer`\n* `Number`\n* `Float`\n* `Boolean`\n\nHere is an example used to/from file:\n```js\nimport { GPU } from 'gpu.js';\nimport * as fs from 'fs';\nconst gpu = new GPU();\nconst kernel = gpu.createKernel(function(v) {\n  return this.thread.x + v + this.constants.v1;\n}, { output: [10], constants: { v1: 100 } });\nconst result = kernel(1);\nconst kernelString = kernel.toString(1);\nfs.writeFileSync('./my-exported-kernel.js', 'module.exports = ' + kernelString);\nimport * as MyExportedKernel from './my-exported-kernel';\nimport gl from 'gl';\nconst myExportedKernel = MyExportedKernel({ context: gl(1,1), constants: { v1: 100 } });\n```\n\n\nHere is an example for just-in-time function creation:\n\n```js\nconst gpu = new GPU();\nconst kernel = gpu.createKernel(function(a) {\n  let sum = 0;\n  for (let i = 0; i < 6; i++) {\n    sum += a[this.thread.x][i];\n  }\n  return sum;\n  }, { output: [6] });\nkernel(input(a, [6, 6]));\nconst kernelString = kernel.toString(input(a, [6, 6]));\nconst newKernel = new Function('return ' + kernelString)()({ context });\nnewKernel(input(a, [6, 6]));\n```\n\n#### using constants with `kernel.toString(...args)`\nYou can assign _some_ new constants when using the function output from `.toString()`,\n\n## Supported Math functions\n\nSince the code running in the kernel is actually compiled to GLSL code, not all functions from the JavaScript Math module are supported.\n\nThis is a list of the supported ones:\n\n* `Math.abs()`\n* `Math.acos()`\n* `Math.acosh()`\n* `Math.asin()`\n* `Math.asinh()`\n* `Math.atan()`\n* `Math.atanh()`\n* `Math.atan2()`\n* `Math.cbrt()`\n* `Math.ceil()`\n* `Math.cos()`\n* `Math.cosh()`\n* `Math.exp()`\n* `Math.expm1()`\n* `Math.floor()`\n* `Math.fround()`\n* `Math.imul()`\n* `Math.log()`\n* `Math.log10()`\n* `Math.log1p()`\n* `Math.log2()`\n* `Math.max()`\n* `Math.min()`\n* `Math.pow()`\n* `Math.random()`\n  * A note on random.  We use [a plugin](src/plugins/math-random-uniformly-distributed.js) to generate random.\n  Random seeded _and_ generated, _both from the GPU_, is not as good as random from the CPU as there are more things that the CPU can seed random from.\n  However, we seed random on the GPU, _from a random value in the CPU_.\n  We then seed the subsequent randoms from the previous random value.\n  So we seed from CPU, and generate from GPU.\n  Which is still not as good as CPU, but closer.\n  While this isn't perfect, it should suffice in most scenarios.\n  In any case, we must give thanks to [RandomPower](https://www.randompower.eu/), and this [issue](https://github.com/gpujs/gpu.js/issues/498), for assisting in improving our implementation of random.\n* `Math.round()`\n* `Math.sign()`\n* `Math.sin()`\n* `Math.sinh()`\n* `Math.sqrt()`\n* `Math.tan()`\n* `Math.tanh()`\n* `Math.trunc()`\n\nThis is a list and reasons of unsupported ones:\n*  `Math.clz32` - bits directly are hard\n*  `Math.hypot` - dynamically sized\n\n## How to check what is supported\n\nTo assist with mostly unit tests, but perhaps in scenarios outside of GPU.js, there are the following logical checks to determine what support level the system executing a GPU.js kernel may have:\n* `GPU.disableValidation()` - turn off all kernel validation\n* `GPU.enableValidation()` - turn on all kernel validation\n* `GPU.isGPUSupported`: `boolean` - checks if GPU is in-fact supported\n* `GPU.isKernelMapSupported`: `boolean` - checks if kernel maps are supported\n* `GPU.isOffscreenCanvasSupported`: `boolean` - checks if offscreen canvas is supported\n* `GPU.isWebGLSupported`: `boolean` - checks if WebGL v1 is supported\n* `GPU.isWebGL2Supported`: `boolean` - checks if WebGL v2 is supported\n* `GPU.isHeadlessGLSupported`: `boolean` - checks if headlessgl is supported\n* `GPU.isCanvasSupported`: `boolean` - checks if canvas is supported\n* `GPU.isGPUHTMLImageArraySupported`: `boolean` - checks if the platform supports HTMLImageArray's\n* `GPU.isSinglePrecisionSupported`: `boolean` - checks if the system supports single precision float 32 values\n\n## Typescript Typings\nTypescript is supported!  Typings can be found [here](src/index.d.ts)!\nFor strongly typed kernels:\n```typescript\nimport { GPU, IKernelFunctionThis } from 'gpu.js';\nconst gpu = new GPU();\n\nfunction kernelFunction(this: IKernelFunctionThis): number {\n  return 1 + this.thread.x;\n}\n\nconst kernelMap = gpu.createKernel<typeof kernelFunction>(kernelFunction)\n  .setOutput([3,3,3]);\n\nconst result = kernelMap();\n\nconsole.log(result as number[][][]);\n```\n\nFor strongly typed mapped kernels:\n```typescript\nimport { GPU, Texture, IKernelFunctionThis } from 'gpu.js';\nconst gpu = new GPU();\n\nfunction kernelFunction(this: IKernelFunctionThis): [number, number] {\n  return [1, 1];\n}\n\nfunction subKernel(): [number, number] {\n  return [1, 1];\n}\n\nconst kernelMap = gpu.createKernelMap<typeof kernelFunction>({\n  test: subKernel,\n}, kernelFunction)\n  .setOutput([1])\n  .setPipeline(true);\n\nconst result = kernelMap();\n\nconsole.log((result.test as Texture).toArray() as [number, number][]);\n```\n\nFor extending constants:\n```typescript\nimport { GPU, IKernelFunctionThis } from 'gpu.js';\nconst gpu = new GPU();\n\ninterface IConstants {\n  screen: [number, number];\n}\n\ntype This = {\n  constants: IConstants\n} & IKernelFunctionThis;\n\nfunction kernelFunction(this: This): number {\n  const { screen } = this.constants;\n  return 1 + screen[0];\n}\n\nconst kernelMap = gpu.createKernel<typeof kernelFunction>(kernelFunction)\n  .setOutput([3,3,3])\n  .setConstants<IConstants>({\n    screen: [1, 1]\n  });\n\nconst result = kernelMap();\n\nconsole.log(result as number[][][]);\n```\n\n[Click here](/examples) for more typescript examples.\n\n## Destructured Assignments **New in V2!**\nDestructured Objects and Arrays work in GPU.js.\n* Object destructuring\n  ```js\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {\n    const { thread: {x, y} } = this;\n    return x + y;\n  }, { output: [2] });\n  console.log(kernel());\n  ```\n* Array destructuring\n  ```js\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function(array) {\n    const [first, second] = array;\n    return first + second; \n  }, {\n    output: [2],\n    argumentTypes: { array: 'Array(2)' }\n  });\n  console.log(kernel([1, 2]));\n  ```\n\n## Dealing With Transpilation\nTranspilation doesn't do the best job of keeping code beautiful.  To aid in this endeavor GPU.js can handle some scenarios to still aid you harnessing the GPU in less than ideal circumstances.\nHere is a list of a few things that GPU.js does to fix transpilation:\n\n* When a transpiler such as [Babel](https://babeljs.io/) changes `myCall()` to `(0, _myCall.myCall)`, it is gracefully handled.\n\n## Full API Reference\n\nYou can find a [complete API reference here](https://doxdox.org/gpujs/gpu.js/).\n\n## How possible in node?\nGPU.js uses [HeadlessGL](https://github.com/stackgl/headless-gl) in node for GPU acceleration.\nGPU.js is written in such a way, you can introduce your own backend.  Have a suggestion?  We'd love to hear it!\n\n## Terms Explained\n* Kernel - A function that is tightly coupled to program that runs on the Graphic Processor\n* Texture - A graphical artifact that is packed with data, in the case of GPU.js, bit shifted parts of a 32 bit floating point decimal\n\n## Testing\nTesting is done (right now) manually, (help wanted [here](https://github.com/gpujs/gpu.js/issues/515) if you can!), using the following:\n* For browser, setup a webserver on the root of the gpu.js project and visit http://url/test/all.html\n* For node, run either of the 3 commands:\n  * `yarn test test/features`\n  * `yarn test test/internal`\n  * `yarn test test/issues`\n\n## Building\nBuilding isn't required on node, but is for browser.  To build the browser's files, run: `yarn make`\n\n# Get Involved!\n\n## Contributing\n\nContributors are welcome! Create a merge request to the `develop` branch and we\nwill gladly review it. If you wish to get write access to the repository,\nplease email us and we will review your application and grant you access to\nthe `develop` branch.\n\nWe promise never to pass off your code as ours.\n\n### Issues\n\nIf you have an issue, either a bug or a feature you think would benefit your project let us know and we will do our best.\n\nCreate issues [here](https://github.com/gpujs/gpu.js/issues) and follow the template.\n\n### Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/gpujs/gpu.js/graphs/contributors\"><img src=\"https://opencollective.com/gpujs/contributors.svg?width=890&button=false\" /></a>\n\n\n### Backers\n\nThank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gpujs#backer)]\n\n<a href=\"https://opencollective.com/gpujs#backers\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/backers.svg?width=890\"></a>\n\n\n### Sponsors\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gpujs#sponsor)]\n\n![](https://www.leadergpu.com/assets/main/logo_leadergpu-a8cacac0c90d204b7f7f6c8420c6a149e71ebe53f3f28f3fc94a01cd05c0bd93.png)\nSponsored NodeJS GPU environment from [LeaderGPU](https://www.leadergpu.com) - These guys rock!\n\n![](https://3fxtqy18kygf3on3bu39kh93-wpengine.netdna-ssl.com/wp-content/themes/browserstack/img/browserstack-logo.svg)\nSponsored Browser GPU environment's from [BrowserStack](https://browserstack.com) - Second to none!\n\n<a href=\"https://opencollective.com/gpujs/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/gpujs/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/gpujs/sponsor/9/avatar.svg\"></a>\n\n## [License](LICENSE)\n"
  },
  {
    "path": "dist/gpu-browser-core.js",
    "content": "/**\n * gpu.js\n * http://gpu.rocks/\n *\n * GPU Accelerated JavaScript\n *\n * @version 2.16.0\n * @date Thu Feb 13 2025 11:46:48 GMT-0800 (Pacific Standard Time)\n *\n * @license MIT\n * The MIT License\n *\n * Copyright (c) 2025 gpu.js Team\n */(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.GPU = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n\n},{}],2:[function(require,module,exports){\nfunction glWiretap(gl, options = {}) {\n  const {\n    contextName = 'gl',\n    throwGetError,\n    useTrackablePrimitives,\n    readPixelsFile,\n    recording = [],\n    variables = {},\n    onReadPixels,\n    onUnrecognizedArgumentLookup,\n  } = options;\n  const proxy = new Proxy(gl, { get: listen });\n  const contextVariables = [];\n  const entityNames = {};\n  let imageCount = 0;\n  let indent = '';\n  let readPixelsVariableName;\n  return proxy;\n  function listen(obj, property) {\n    switch (property) {\n      case 'addComment': return addComment;\n      case 'checkThrowError': return checkThrowError;\n      case 'getReadPixelsVariableName': return readPixelsVariableName;\n      case 'insertVariable': return insertVariable;\n      case 'reset': return reset;\n      case 'setIndent': return setIndent;\n      case 'toString': return toString;\n      case 'getContextVariableName': return getContextVariableName;\n    }\n    if (typeof gl[property] === 'function') {\n      return function() { \n        switch (property) {\n          case 'getError':\n            if (throwGetError) {\n              recording.push(`${indent}if (${contextName}.getError() !== ${contextName}.NONE) throw new Error('error');`);\n            } else {\n              recording.push(`${indent}${contextName}.getError();`); \n            }\n            return gl.getError();\n          case 'getExtension': {\n            const variableName = `${contextName}Variables${contextVariables.length}`;\n            recording.push(`${indent}const ${variableName} = ${contextName}.getExtension('${arguments[0]}');`);\n            const extension = gl.getExtension(arguments[0]);\n            if (extension && typeof extension === 'object') {\n              const tappedExtension = glExtensionWiretap(extension, {\n                getEntity,\n                useTrackablePrimitives,\n                recording,\n                contextName: variableName,\n                contextVariables,\n                variables,\n                indent,\n                onUnrecognizedArgumentLookup,\n              });\n              contextVariables.push(tappedExtension);\n              return tappedExtension;\n            } else {\n              contextVariables.push(null);\n            }\n            return extension;\n          }\n          case 'readPixels':\n            const i = contextVariables.indexOf(arguments[6]);\n            let targetVariableName;\n            if (i === -1) {\n              const variableName = getVariableName(arguments[6]);\n              if (variableName) {\n                targetVariableName = variableName;\n                recording.push(`${indent}${variableName}`);\n              } else {\n                targetVariableName = `${contextName}Variable${contextVariables.length}`;\n                contextVariables.push(arguments[6]);\n                recording.push(`${indent}const ${targetVariableName} = new ${arguments[6].constructor.name}(${arguments[6].length});`);\n              }\n            } else {\n              targetVariableName = `${contextName}Variable${i}`;\n            }\n            readPixelsVariableName = targetVariableName;\n            const argumentAsStrings = [\n              arguments[0],\n              arguments[1],\n              arguments[2],\n              arguments[3],\n              getEntity(arguments[4]),\n              getEntity(arguments[5]),\n              targetVariableName\n            ];\n            recording.push(`${indent}${contextName}.readPixels(${argumentAsStrings.join(', ')});`);\n            if (readPixelsFile) {\n              writePPM(arguments[2], arguments[3]);\n            }\n            if (onReadPixels) {\n              onReadPixels(targetVariableName, argumentAsStrings);\n            }\n            return gl.readPixels.apply(gl, arguments);\n          case 'drawBuffers':\n            recording.push(`${indent}${contextName}.drawBuffers([${argumentsToString(arguments[0], { contextName, contextVariables, getEntity, addVariable, variables, onUnrecognizedArgumentLookup } )}]);`);\n            return gl.drawBuffers(arguments[0]);\n        }\n        let result = gl[property].apply(gl, arguments);\n        switch (typeof result) {\n          case 'undefined':\n            recording.push(`${indent}${methodCallToString(property, arguments)};`);\n            return;\n          case 'number':\n          case 'boolean':\n            if (useTrackablePrimitives && contextVariables.indexOf(trackablePrimitive(result)) === -1) {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result = trackablePrimitive(result));\n              break;\n            }\n          default:\n            if (result === null) {\n              recording.push(`${methodCallToString(property, arguments)};`);\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n            }\n\n            contextVariables.push(result);\n        }\n        return result;\n      }\n    }\n    entityNames[gl[property]] = property;\n    return gl[property];\n  }\n  function toString() {\n    return recording.join('\\n');\n  }\n  function reset() {\n    while (recording.length > 0) {\n      recording.pop();\n    }\n  }\n  function insertVariable(name, value) {\n    variables[name] = value;\n  }\n  function getEntity(value) {\n    const name = entityNames[value];\n    if (name) {\n      return contextName + '.' + name;\n    }\n    return value;\n  }\n  function setIndent(spaces) {\n    indent = ' '.repeat(spaces);\n  }\n  function addVariable(value, source) {\n    const variableName = `${contextName}Variable${contextVariables.length}`;\n    recording.push(`${indent}const ${variableName} = ${source};`);\n    contextVariables.push(value);\n    return variableName;\n  }\n  function writePPM(width, height) {\n    const sourceVariable = `${contextName}Variable${contextVariables.length}`;\n    const imageVariable = `imageDatum${imageCount}`;\n    recording.push(`${indent}let ${imageVariable} = [\"P3\\\\n# ${readPixelsFile}.ppm\\\\n\", ${width}, ' ', ${height}, \"\\\\n255\\\\n\"].join(\"\");`);\n    recording.push(`${indent}for (let i = 0; i < ${imageVariable}.length; i += 4) {`);\n    recording.push(`${indent}  ${imageVariable} += ${sourceVariable}[i] + ' ' + ${sourceVariable}[i + 1] + ' ' + ${sourceVariable}[i + 2] + ' ';`);\n    recording.push(`${indent}}`);\n    recording.push(`${indent}if (typeof require !== \"undefined\") {`);\n    recording.push(`${indent}  require('fs').writeFileSync('./${readPixelsFile}.ppm', ${imageVariable});`);\n    recording.push(`${indent}}`);\n    imageCount++;\n  }\n  function addComment(value) {\n    recording.push(`${indent}// ${value}`);\n  }\n  function checkThrowError() {\n    recording.push(`${indent}(() => {\n${indent}const error = ${contextName}.getError();\n${indent}if (error !== ${contextName}.NONE) {\n${indent}  const names = Object.getOwnPropertyNames(gl);\n${indent}  for (let i = 0; i < names.length; i++) {\n${indent}    const name = names[i];\n${indent}    if (${contextName}[name] === error) {\n${indent}      throw new Error('${contextName} threw ' + name);\n${indent}    }\n${indent}  }\n${indent}}\n${indent}})();`);\n  }\n  function methodCallToString(method, args) {\n    return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity, addVariable, variables, onUnrecognizedArgumentLookup })})`;\n  }\n\n  function getVariableName(value) {\n    if (variables) {\n      for (const name in variables) {\n        if (variables[name] === value) {\n          return name;\n        }\n      }\n    }\n    return null;\n  }\n\n  function getContextVariableName(value) {\n    const i = contextVariables.indexOf(value);\n    if (i !== -1) {\n      return `${contextName}Variable${i}`;\n    }\n    return null;\n  }\n}\n\nfunction glExtensionWiretap(extension, options) {\n  const proxy = new Proxy(extension, { get: listen });\n  const extensionEntityNames = {};\n  const {\n    contextName,\n    contextVariables,\n    getEntity,\n    useTrackablePrimitives,\n    recording,\n    variables,\n    indent,\n    onUnrecognizedArgumentLookup,\n  } = options;\n  return proxy;\n  function listen(obj, property) {\n    if (typeof obj[property] === 'function') {\n      return function() {\n        switch (property) {\n          case 'drawBuffersWEBGL':\n            recording.push(`${indent}${contextName}.drawBuffersWEBGL([${argumentsToString(arguments[0], { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables, onUnrecognizedArgumentLookup })}]);`);\n            return extension.drawBuffersWEBGL(arguments[0]);\n        }\n        let result = extension[property].apply(extension, arguments);\n        switch (typeof result) {\n          case 'undefined':\n            recording.push(`${indent}${methodCallToString(property, arguments)};`);\n            return;\n          case 'number':\n          case 'boolean':\n            if (useTrackablePrimitives && contextVariables.indexOf(trackablePrimitive(result)) === -1) {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result = trackablePrimitive(result));\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result);\n            }\n            break;\n          default:\n            if (result === null) {\n              recording.push(`${methodCallToString(property, arguments)};`);\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n            }\n            contextVariables.push(result);\n        }\n        return result;\n      };\n    }\n    extensionEntityNames[extension[property]] = property;\n    return extension[property];\n  }\n\n  function getExtensionEntity(value) {\n    if (extensionEntityNames.hasOwnProperty(value)) {\n      return `${contextName}.${extensionEntityNames[value]}`;\n    }\n    return getEntity(value);\n  }\n\n  function methodCallToString(method, args) {\n    return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables, onUnrecognizedArgumentLookup })})`;\n  }\n\n  function addVariable(value, source) {\n    const variableName = `${contextName}Variable${contextVariables.length}`;\n    contextVariables.push(value);\n    recording.push(`${indent}const ${variableName} = ${source};`);\n    return variableName;\n  }\n}\n\nfunction argumentsToString(args, options) {\n  const { variables, onUnrecognizedArgumentLookup } = options;\n  return (Array.from(args).map((arg) => {\n    const variableName = getVariableName(arg);\n    if (variableName) {\n      return variableName;\n    }\n    return argumentToString(arg, options);\n  }).join(', '));\n\n  function getVariableName(value) {\n    if (variables) {\n      for (const name in variables) {\n        if (!variables.hasOwnProperty(name)) continue;\n        if (variables[name] === value) {\n          return name;\n        }\n      }\n    }\n    if (onUnrecognizedArgumentLookup) {\n      return onUnrecognizedArgumentLookup(value);\n    }\n    return null;\n  }\n}\n\nfunction argumentToString(arg, options) {\n  const { contextName, contextVariables, getEntity, addVariable, onUnrecognizedArgumentLookup } = options;\n  if (typeof arg === 'undefined') {\n    return 'undefined';\n  }\n  if (arg === null) {\n    return 'null';\n  }\n  const i = contextVariables.indexOf(arg);\n  if (i > -1) {\n    return `${contextName}Variable${i}`;\n  }\n  switch (arg.constructor.name) {\n    case 'String':\n      const hasLines = /\\n/.test(arg);\n      const hasSingleQuotes = /'/.test(arg);\n      const hasDoubleQuotes = /\"/.test(arg);\n      if (hasLines) {\n        return '`' + arg + '`';\n      } else if (hasSingleQuotes && !hasDoubleQuotes) {\n        return '\"' + arg + '\"';\n      } else if (!hasSingleQuotes && hasDoubleQuotes) {\n        return \"'\" + arg + \"'\";\n      } else {\n        return '\\'' + arg + '\\'';\n      }\n    case 'Number': return getEntity(arg);\n    case 'Boolean': return getEntity(arg);\n    case 'Array':\n      return addVariable(arg, `new ${arg.constructor.name}([${Array.from(arg).join(',')}])`);\n    case 'Float32Array':\n    case 'Uint8Array':\n    case 'Uint16Array':\n    case 'Int32Array':\n      return addVariable(arg, `new ${arg.constructor.name}(${JSON.stringify(Array.from(arg))})`);\n    default:\n      if (onUnrecognizedArgumentLookup) {\n        const instantiationString = onUnrecognizedArgumentLookup(arg);\n        if (instantiationString) {\n          return instantiationString;\n        }\n      }\n      throw new Error(`unrecognized argument type ${arg.constructor.name}`);\n  }\n}\n\nfunction trackablePrimitive(value) {\n  return new value.constructor(value);\n}\n\nif (typeof module !== 'undefined') {\n  module.exports = { glWiretap, glExtensionWiretap };\n}\n\nif (typeof window !== 'undefined') {\n  glWiretap.glExtensionWiretap = glExtensionWiretap;\n  window.glWiretap = glWiretap;\n}\n\n},{}],3:[function(require,module,exports){\nfunction setupArguments(args) {\n  const newArguments = new Array(args.length);\n  for (let i = 0; i < args.length; i++) {\n    const arg = args[i];\n    if (arg.toArray) {\n      newArguments[i] = arg.toArray();\n    } else {\n      newArguments[i] = arg;\n    }\n  }\n  return newArguments;\n}\n\nfunction mock1D() {\n  const args = setupArguments(arguments);\n  const row = new Float32Array(this.output.x);\n  for (let x = 0; x < this.output.x; x++) {\n    this.thread.x = x;\n    this.thread.y = 0;\n    this.thread.z = 0;\n    row[x] = this._fn.apply(this, args);\n  }\n  return row;\n}\n\nfunction mock2D() {\n  const args = setupArguments(arguments);\n  const matrix = new Array(this.output.y);\n  for (let y = 0; y < this.output.y; y++) {\n    const row = new Float32Array(this.output.x);\n    for (let x = 0; x < this.output.x; x++) {\n      this.thread.x = x;\n      this.thread.y = y;\n      this.thread.z = 0;\n      row[x] = this._fn.apply(this, args);\n    }\n    matrix[y] = row;\n  }\n  return matrix;\n}\n\nfunction mock2DGraphical() {\n  const args = setupArguments(arguments);\n  for (let y = 0; y < this.output.y; y++) {\n    for (let x = 0; x < this.output.x; x++) {\n      this.thread.x = x;\n      this.thread.y = y;\n      this.thread.z = 0;\n      this._fn.apply(this, args);\n    }\n  }\n}\n\nfunction mock3D() {\n  const args = setupArguments(arguments);\n  const cube = new Array(this.output.z);\n  for (let z = 0; z < this.output.z; z++) {\n    const matrix = new Array(this.output.y);\n    for (let y = 0; y < this.output.y; y++) {\n      const row = new Float32Array(this.output.x);\n      for (let x = 0; x < this.output.x; x++) {\n        this.thread.x = x;\n        this.thread.y = y;\n        this.thread.z = z;\n        row[x] = this._fn.apply(this, args);\n      }\n      matrix[y] = row;\n    }\n    cube[z] = matrix;\n  }\n  return cube;\n}\n\nfunction apiDecorate(kernel) {\n  kernel.setOutput = (output) => {\n    kernel.output = setupOutput(output);\n    if (kernel.graphical) {\n      setupGraphical(kernel);\n    }\n  };\n  kernel.toJSON = () => {\n    throw new Error('Not usable with gpuMock');\n  };\n  kernel.setConstants = (flag) => {\n    kernel.constants = flag;\n    return kernel;\n  };\n  kernel.setGraphical = (flag) => {\n    kernel.graphical = flag;\n    return kernel;\n  };\n  kernel.setCanvas = (flag) => {\n    kernel.canvas = flag;\n    return kernel;\n  };\n  kernel.setContext = (flag) => {\n    kernel.context = flag;\n    return kernel;\n  };\n  kernel.destroy = () => {};\n  kernel.validateSettings = () => {};\n  if (kernel.graphical && kernel.output) {\n    setupGraphical(kernel);\n  }\n  kernel.exec = function() {\n    return new Promise((resolve, reject) => {\n      try {\n        resolve(kernel.apply(kernel, arguments));\n      } catch(e) {\n        reject(e);\n      }\n    });\n  };\n  kernel.getPixels = (flip) => {\n    const {x, y} = kernel.output;\n    return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0);\n  };\n  kernel.color = function(r, g, b, a) {\n    if (typeof a === 'undefined') {\n      a = 1;\n    }\n\n    r = Math.floor(r * 255);\n    g = Math.floor(g * 255);\n    b = Math.floor(b * 255);\n    a = Math.floor(a * 255);\n\n    const width = kernel.output.x;\n    const height = kernel.output.y;\n\n    const x = kernel.thread.x;\n    const y = height - kernel.thread.y - 1;\n\n    const index = x + y * width;\n\n    kernel._colorData[index * 4 + 0] = r;\n    kernel._colorData[index * 4 + 1] = g;\n    kernel._colorData[index * 4 + 2] = b;\n    kernel._colorData[index * 4 + 3] = a;\n  };\n\n  const mockMethod = () => kernel;\n  const methods = [\n    'setWarnVarUsage',\n    'setArgumentTypes',\n    'setTactic',\n    'setOptimizeFloatMemory',\n    'setDebug',\n    'setLoopMaxIterations',\n    'setConstantTypes',\n    'setFunctions',\n    'setNativeFunctions',\n    'setInjectedNative',\n    'setPipeline',\n    'setPrecision',\n    'setOutputToTexture',\n    'setImmutable',\n    'setStrictIntegers',\n    'setDynamicOutput',\n    'setHardcodeConstants',\n    'setDynamicArguments',\n    'setUseLegacyEncoder',\n    'setWarnVarUsage',\n    'addSubKernel',\n  ];\n  for (let i = 0; i < methods.length; i++) {\n    kernel[methods[i]] = mockMethod;\n  }\n  return kernel;\n}\n\nfunction setupGraphical(kernel) {\n  const {x, y} = kernel.output;\n  if (kernel.context && kernel.context.createImageData) {\n    const data = new Uint8ClampedArray(x * y * 4);\n    kernel._imageData = kernel.context.createImageData(x, y);\n    kernel._colorData = data;\n  } else {\n    const data = new Uint8ClampedArray(x * y * 4);\n    kernel._imageData = { data };\n    kernel._colorData = data;\n  }\n}\n\nfunction setupOutput(output) {\n  let result = null;\n  if (output.length) {\n    if (output.length === 3) {\n      const [x,y,z] = output;\n      result = { x, y, z };\n    } else if (output.length === 2) {\n      const [x,y] = output;\n      result = { x, y };\n    } else {\n      const [x] = output;\n      result = { x };\n    }\n  } else {\n    result = output;\n  }\n  return result;\n}\n\nfunction gpuMock(fn, settings = {}) {\n  const output = settings.output ? setupOutput(settings.output) : null;\n  function kernel() {\n    if (kernel.output.z) {\n      return mock3D.apply(kernel, arguments);\n    } else if (kernel.output.y) {\n      if (kernel.graphical) {\n        return mock2DGraphical.apply(kernel, arguments);\n      }\n      return mock2D.apply(kernel, arguments);\n    } else {\n      return mock1D.apply(kernel, arguments);\n    }\n  }\n  kernel._fn = fn;\n  kernel.constants = settings.constants || null;\n  kernel.context = settings.context || null;\n  kernel.canvas = settings.canvas || null;\n  kernel.graphical = settings.graphical || false;\n  kernel._imageData = null;\n  kernel._colorData = null;\n  kernel.output = output;\n  kernel.thread = {\n    x: 0,\n    y: 0,\n    z: 0\n  };\n  return apiDecorate(kernel);\n}\n\nfunction flipPixels(pixels, width, height) {\n  const halfHeight = height / 2 | 0; \n  const bytesPerRow = width * 4;\n  const temp = new Uint8ClampedArray(width * 4);\n  const result = pixels.slice(0);\n  for (let y = 0; y < halfHeight; ++y) {\n    const topOffset = y * bytesPerRow;\n    const bottomOffset = (height - y - 1) * bytesPerRow;\n\n    temp.set(result.subarray(topOffset, topOffset + bytesPerRow));\n\n    result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);\n\n    result.set(temp, bottomOffset);\n  }\n  return result;\n}\n\nmodule.exports = {\n  gpuMock\n};\n\n},{}],4:[function(require,module,exports){\nconst { utils } = require('./utils');\n\nfunction alias(name, source) {\n  const fnString = source.toString();\n  return new Function(`return function ${ name } (${ utils.getArgumentNamesFromString(fnString).join(', ') }) {\n  ${ utils.getFunctionBodyFromString(fnString) }\n}`)();\n}\n\nmodule.exports = {\n  alias\n};\n},{\"./utils\":113}],5:[function(require,module,exports){\nconst { FunctionNode } = require('../function-node');\n\nclass CPUFunctionNode extends FunctionNode {\n  astFunction(ast, retArr) {\n\n    if (!this.isRootKernel) {\n      retArr.push('function');\n      retArr.push(' ');\n      retArr.push(this.name);\n      retArr.push('(');\n\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        retArr.push('user_');\n        retArr.push(argumentName);\n      }\n\n      retArr.push(') {\\n');\n    }\n\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    if (!this.isRootKernel) {\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astReturnStatement(ast, retArr) {\n    const type = this.returnType || this.getType(ast.argument);\n\n    if (!this.returnType) {\n      this.returnType = type;\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(this.leadingReturnStatement);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';\\n');\n      retArr.push(this.followingReturnStatement);\n      retArr.push('continue;\\n');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = `);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push('return ');\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  astLiteral(ast, retArr) {\n\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    retArr.push(ast.value);\n\n    return retArr;\n  }\n\n  astBinaryExpression(ast, retArr) {\n    retArr.push('(');\n    this.astGeneric(ast.left, retArr);\n    retArr.push(ast.operator);\n    this.astGeneric(ast.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    switch (idtNode.name) {\n      case 'Infinity':\n        retArr.push('Infinity');\n        break;\n      default:\n        if (this.constants && this.constants.hasOwnProperty(idtNode.name)) {\n          retArr.push('constants_' + idtNode.name);\n        } else {\n          retArr.push('user_' + idtNode.name);\n        }\n    }\n\n    return retArr;\n  }\n\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      this.pushState('in-for-loop-init');\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < initArr.length; i++) {\n        if (initArr[i].includes && initArr[i].includes(',')) {\n          isSafe = false;\n        }\n      }\n      this.popState('in-for-loop-init');\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), ';\\n');\n      }\n      retArr.push(`for (let ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        whileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    retArr.push('if (');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') {\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('} else {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        doWhileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n\n  }\n\n  astAssignmentExpression(assNode, retArr) {\n    const declaration = this.getDeclaration(assNode.left);\n    if (declaration && !declaration.assignable) {\n      throw this.astErrorOutput(`Variable ${assNode.left.name} is not assignable here`, assNode);\n    }\n    this.astGeneric(assNode.left, retArr);\n    retArr.push(assNode.operator);\n    this.astGeneric(assNode.right, retArr);\n    return retArr;\n  }\n\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); \n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astVariableDeclaration(varDecNode, retArr) {\n    retArr.push(`${varDecNode.kind} `);\n    const { declarations } = varDecNode;\n    for (let i = 0; i < declarations.length; i++) {\n      if (i > 0) {\n        retArr.push(',');\n      }\n      const declaration = declarations[i];\n      const info = this.getDeclaration(declaration.id);\n      if (!info.valueType) {\n        info.valueType = this.getType(declaration.init);\n      }\n      this.astGeneric(declaration, retArr);\n    }\n    if (!this.isState('in-for-loop-init')) {\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n\n  }\n\n  astSwitchStatement(ast, retArr) {\n    const { discriminant, cases } = ast;\n    retArr.push('switch (');\n    this.astGeneric(discriminant, retArr);\n    retArr.push(') {\\n');\n    for (let i = 0; i < cases.length; i++) {\n      if (cases[i].test === null) {\n        retArr.push('default:\\n');\n        this.astGeneric(cases[i].consequent, retArr);\n        if (cases[i].consequent && cases[i].consequent.length > 0) {\n          retArr.push('break;\\n');\n        }\n        continue;\n      }\n      retArr.push('case ');\n      this.astGeneric(cases[i].test, retArr);\n      retArr.push(':\\n');\n      if (cases[i].consequent && cases[i].consequent.length > 0) {\n        this.astGeneric(cases[i].consequent, retArr);\n        retArr.push('break;\\n');\n      }\n    }\n    retArr.push('\\n}');\n  }\n\n  astThisExpression(tNode, retArr) {\n    retArr.push('_this');\n    return retArr;\n  }\n\n  astMemberExpression(mNode, retArr) {\n    const {\n      signature,\n      type,\n      property,\n      xProperty,\n      yProperty,\n      zProperty,\n      name,\n      origin\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'this.thread.value':\n        retArr.push(`_this.thread.${ name }`);\n        return retArr;\n      case 'this.output.value':\n        switch (name) {\n          case 'x':\n            retArr.push('outputX');\n            break;\n          case 'y':\n            retArr.push('outputY');\n            break;\n          case 'z':\n            retArr.push('outputZ');\n            break;\n          default:\n            throw this.astErrorOutput('Unexpected expression', mNode);\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ name }[0]`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ name }[1]`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ name }[2]`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ name }[3]`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n      case 'this.constants.value[]':\n      case 'this.constants.value[][]':\n      case 'this.constants.value[][][]':\n        break;\n      case 'fn()[]':\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      case 'fn()[][]':\n        this.astGeneric(mNode.object.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.object.property, retArr);\n        retArr.push(']');\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      default:\n        throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (!mNode.computed) {\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${name}`);\n          return retArr;\n      }\n    }\n\n    const markupName = `${origin}_${name}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'HTMLImageArray':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n      case 'HTMLImage':\n      default:\n        let size;\n        let isInput;\n        if (origin === 'constants') {\n          const constant = this.constants[name];\n          isInput = this.constantTypes[name] === 'Input';\n          size = isInput ? constant.size : null;\n        } else {\n          isInput = this.isInput(name);\n          size = isInput ? this.argumentSizes[this.argumentNames.indexOf(name)] : null;\n        }\n        retArr.push(`${ markupName }`);\n        if (zProperty && yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? '(outputY * outputX)' : size[1] * size[0] })+(`);\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (typeof xProperty !== 'undefined') {\n          retArr.push('[');\n          this.astGeneric(xProperty, retArr);\n          retArr.push(']');\n        }\n    }\n    return retArr;\n  }\n\n  astCallExpression(ast, retArr) {\n    if (ast.type !== 'CallExpression') {\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n    let functionName = this.astMemberExpressionUnroll(ast.callee);\n\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    retArr.push(functionName);\n\n    retArr.push('(');\n    const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n    for (let i = 0; i < ast.arguments.length; ++i) {\n      const argument = ast.arguments[i];\n\n      let argumentType = this.getType(argument);\n      if (!targetTypes[i]) {\n        this.triggerImplyArgumentType(functionName, i, argumentType, this);\n      }\n\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      this.astGeneric(argument, retArr);\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n    const arrLen = arrNode.elements.length;\n    const elements = [];\n    for (let i = 0; i < arrLen; ++i) {\n      const element = [];\n      this.astGeneric(arrNode.elements[i], element);\n      elements.push(element.join(''));\n    }\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`[${elements.join(', ')}]`);\n        break;\n      default:\n        retArr.push(`new Float32Array([${elements.join(', ')}])`);\n    }\n    return retArr;\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    retArr.push('debugger;');\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  CPUFunctionNode\n};\n},{\"../function-node\":9}],6:[function(require,module,exports){\nconst { utils } = require('../../utils');\n\nfunction constantsToString(constants, types) {\n  const results = [];\n  for (const name in types) {\n    if (!types.hasOwnProperty(name)) continue;\n    const type = types[name];\n    const constant = constants[name];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n        results.push(`${name}:${constant}`);\n        break;\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        results.push(`${name}:new ${constant.constructor.name}(${JSON.stringify(Array.from(constant))})`);\n        break;\n    }\n  }\n  return `{ ${ results.join() } }`;\n}\n\nfunction cpuKernelString(cpuKernel, name) {\n  const header = [];\n  const thisProperties = [];\n  const beforeReturn = [];\n\n  const useFunctionKeyword = !/^function/.test(cpuKernel.color.toString());\n\n  header.push(\n    '  const { context, canvas, constants: incomingConstants } = settings;',\n    `  const output = new Int32Array(${JSON.stringify(Array.from(cpuKernel.output))});`,\n    `  const _constantTypes = ${JSON.stringify(cpuKernel.constantTypes)};`,\n    `  const _constants = ${constantsToString(cpuKernel.constants, cpuKernel.constantTypes)};`\n  );\n\n  thisProperties.push(\n    '    constants: _constants,',\n    '    context,',\n    '    output,',\n    '    thread: {x: 0, y: 0, z: 0},'\n  );\n\n  if (cpuKernel.graphical) {\n    header.push(`  const _imageData = context.createImageData(${cpuKernel.output[0]}, ${cpuKernel.output[1]});`);\n    header.push(`  const _colorData = new Uint8ClampedArray(${cpuKernel.output[0]} * ${cpuKernel.output[1]} * 4);`);\n\n    const colorFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.color.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: (object, name) => {\n        return null;\n      }\n    });\n\n    const getPixelsFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.getPixels.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: () => {\n        return null;\n      }\n    });\n\n    thisProperties.push(\n      '    _imageData,',\n      '    _colorData,',\n      `    color: ${colorFn},`\n    );\n\n    beforeReturn.push(\n      `  kernel.getPixels = ${getPixelsFn};`\n    );\n  }\n\n  const constantTypes = [];\n  const constantKeys = Object.keys(cpuKernel.constantTypes);\n  for (let i = 0; i < constantKeys.length; i++) {\n    constantTypes.push(cpuKernel.constantTypes[constantKeys]);\n  }\n  if (cpuKernel.argumentTypes.indexOf('HTMLImageArray') !== -1 || constantTypes.indexOf('HTMLImageArray') !== -1) {\n    const flattenedImageTo3DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._imageTo3DArray.toString(), {\n      doNotDefine: ['canvas'],\n      findDependency: (object, name) => {\n        if (object === 'this') {\n          return (useFunctionKeyword ? 'function ' : '') + cpuKernel[name].toString();\n        }\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return;\n          case 'context':\n            return 'context';\n        }\n      }\n    });\n    beforeReturn.push(flattenedImageTo3DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n    thisProperties.push(`    _imageTo3DArray,`);\n  } else if (cpuKernel.argumentTypes.indexOf('HTMLImage') !== -1 || constantTypes.indexOf('HTMLImage') !== -1) {\n    const flattenedImageTo2DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._mediaTo2DArray.toString(), {\n      findDependency: (object, name) => {\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return 'settings.canvas';\n          case 'context':\n            return 'settings.context';\n        }\n        throw new Error('unhandled thisLookup');\n      }\n    });\n    beforeReturn.push(flattenedImageTo2DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n  }\n\n  return `function(settings) {\n${ header.join('\\n') }\n  for (const p in _constantTypes) {\n    if (!_constantTypes.hasOwnProperty(p)) continue;\n    const type = _constantTypes[p];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        if (incomingConstants.hasOwnProperty(p)) {\n          console.warn('constant ' + p + ' of type ' + type + ' cannot be resigned');\n        }\n        continue;\n    }\n    if (!incomingConstants.hasOwnProperty(p)) {\n      throw new Error('constant ' + p + ' not found');\n    }\n    _constants[p] = incomingConstants[p];\n  }\n  const kernel = (function() {\n${cpuKernel._kernelString}\n  })\n    .apply({ ${thisProperties.join('\\n')} });\n  ${ beforeReturn.join('\\n') }\n  return kernel;\n}`;\n}\n\nmodule.exports = {\n  cpuKernelString\n};\n},{\"../../utils\":113}],7:[function(require,module,exports){\nconst { Kernel } = require('../kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { CPUFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst { cpuKernelString } = require('./kernel-string');\n\nclass CPUKernel extends Kernel {\n  static getFeatures() {\n    return this.features;\n  }\n  static get features() {\n    return Object.freeze({\n      kernelMap: true,\n      isIntegerDivisionAccurate: true\n    });\n  }\n  static get isSupported() {\n    return true;\n  }\n  static isContextMatch(context) {\n    return false;\n  }\n  static get mode() {\n    return 'cpu';\n  }\n\n  static nativeFunctionArguments() {\n    return null;\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`Looking up native function return type not supported on ${this.name}`);\n  }\n\n  static combineKernels(combinedKernel) {\n    return combinedKernel;\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return 'cpu' + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.mergeSettings(source.settings || settings);\n\n    this._imageData = null;\n    this._colorData = null;\n    this._kernelString = null;\n    this._prependedString = [];\n    this.thread = {\n      x: 0,\n      y: 0,\n      z: 0\n    };\n    this.translatedSources = null;\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      return document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  initContext() {\n    if (!this.canvas) return null;\n    return this.canvas.getContext('2d');\n  }\n\n  initPlugins(settings) {\n    return [];\n  }\n\n  validateSettings(args) {\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      if (argType === 'Array') {\n        this.output = utils.getDimensions(argType);\n      } else if (argType === 'NumberTexture' || argType === 'ArrayTexture(4)') {\n        this.output = args[0].output;\n      } else {\n        throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n    }\n\n    this.checkOutput();\n  }\n\n  translateSource() {\n    this.leadingReturnStatement = this.output.length > 1 ? 'resultX[x] = ' : 'result[x] = ';\n    if (this.subKernels) {\n      const followingReturnStatement = [];\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const {\n          name\n        } = this.subKernels[i];\n        followingReturnStatement.push(this.output.length > 1 ? `resultX_${ name }[x] = subKernelResult_${ name };\\n` : `result_${ name }[x] = subKernelResult_${ name };\\n`);\n      }\n      this.followingReturnStatement = followingReturnStatement.join('');\n    }\n    const functionBuilder = FunctionBuilder.fromKernel(this, CPUFunctionNode);\n    this.translatedSources = functionBuilder.getPrototypes('kernel');\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n  }\n\n  build() {\n    if (this.built) return;\n    this.setupConstants();\n    this.setupArguments(arguments);\n    this.validateSettings(arguments);\n    this.translateSource();\n\n    if (this.graphical) {\n      const {\n        canvas,\n        output\n      } = this;\n      if (!canvas) {\n        throw new Error('no canvas available for using graphical output');\n      }\n      const width = output[0];\n      const height = output[1] || 1;\n      canvas.width = width;\n      canvas.height = height;\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n\n    const kernelString = this.getKernelString();\n    this.kernelString = kernelString;\n\n    if (this.debug) {\n      console.log('Function output:');\n      console.log(kernelString);\n    }\n\n    try {\n      this.run = new Function([], kernelString).bind(this)();\n    } catch (e) {\n      console.error('An error occurred compiling the javascript: ', e);\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  color(r, g, b, a) {\n    if (typeof a === 'undefined') {\n      a = 1;\n    }\n\n    r = Math.floor(r * 255);\n    g = Math.floor(g * 255);\n    b = Math.floor(b * 255);\n    a = Math.floor(a * 255);\n\n    const width = this.output[0];\n    const height = this.output[1];\n\n    const x = this.thread.x;\n    const y = height - this.thread.y - 1;\n\n    const index = x + y * width;\n\n    this._colorData[index * 4 + 0] = r;\n    this._colorData[index * 4 + 1] = g;\n    this._colorData[index * 4 + 2] = b;\n    this._colorData[index * 4 + 3] = a;\n  }\n\n  getKernelString() {\n    if (this._kernelString !== null) return this._kernelString;\n\n    let kernelThreadString = null;\n    let {\n      translatedSources\n    } = this;\n    if (translatedSources.length > 1) {\n      translatedSources = translatedSources.filter(fn => {\n        if (/^function/.test(fn)) return fn;\n        kernelThreadString = fn;\n        return false;\n      });\n    } else {\n      kernelThreadString = translatedSources.shift();\n    }\n    return this._kernelString = `  const LOOP_MAX = ${ this._getLoopMaxString() };\n  ${ this.injectedNative || '' }\n  const _this = this;\n  ${ this._resultKernelHeader() }\n  ${ this._processConstants() }\n  return (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) => {\n    ${ this._prependedString.join('') }\n    ${ this._earlyThrows() }\n    ${ this._processArguments() }\n    ${ this.graphical ? this._graphicalKernelBody(kernelThreadString) : this._resultKernelBody(kernelThreadString) }\n    ${ translatedSources.length > 0 ? translatedSources.join('\\n') : '' }\n  };`;\n  }\n\n  toString() {\n    return cpuKernelString(this);\n  }\n\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${ parseInt(this.loopMaxIterations) };` :\n      ' 1000;'\n    );\n  }\n\n  _processConstants() {\n    if (!this.constants) return '';\n\n    const result = [];\n    for (let p in this.constants) {\n      const type = this.constantTypes[p];\n      switch (type) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    const constants_${p} = this._mediaTo2DArray(this.constants.${p});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    const constants_${p} = this._imageTo3DArray(this.constants.${p});\\n`);\n          break;\n        case 'Input':\n          result.push(`    const constants_${p} = this.constants.${p}.value;\\n`);\n          break;\n        default:\n          result.push(`    const constants_${p} = this.constants.${p};\\n`);\n      }\n    }\n    return result.join('');\n  }\n\n  _earlyThrows() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    const arrayArguments = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      if (this.argumentTypes[i] === 'Array') {\n        arrayArguments.push(this.argumentNames[i]);\n      }\n    }\n    if (arrayArguments.length === 0) return '';\n    const checks = [];\n    for (let i = 0; i < arrayArguments.length; i++) {\n      const argumentName = arrayArguments[i];\n      const checkSubKernels = this._mapSubKernels(subKernel => `user_${argumentName} === result_${subKernel.name}`).join(' || ');\n      checks.push(`user_${argumentName} === result${checkSubKernels ? ` || ${checkSubKernels}` : ''}`);\n    }\n    return `if (${checks.join(' || ')}) throw new Error('Source and destination arrays are the same.  Use immutable = true');`;\n  }\n\n  _processArguments() {\n    const result = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      const variableName = `user_${this.argumentNames[i]}`;\n      switch (this.argumentTypes[i]) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    ${variableName} = this._mediaTo2DArray(${variableName});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    ${variableName} = this._imageTo3DArray(${variableName});\\n`);\n          break;\n        case 'Input':\n          result.push(`    ${variableName} = ${variableName}.value;\\n`);\n          break;\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n          result.push(`\n    if (${variableName}.toArray) {\n      if (!_this.textureCache) {\n        _this.textureCache = [];\n        _this.arrayCache = [];\n      }\n      const textureIndex = _this.textureCache.indexOf(${variableName});\n      if (textureIndex !== -1) {\n        ${variableName} = _this.arrayCache[textureIndex];\n      } else {\n        _this.textureCache.push(${variableName});\n        ${variableName} = ${variableName}.toArray();\n        _this.arrayCache.push(${variableName});\n      }\n    }`);\n          break;\n      }\n    }\n    return result.join('');\n  }\n\n  _mediaTo2DArray(media) {\n    const canvas = this.canvas;\n    const width = media.width > 0 ? media.width : media.videoWidth;\n    const height = media.height > 0 ? media.height : media.videoHeight;\n    if (canvas.width < width) {\n      canvas.width = width;\n    }\n    if (canvas.height < height) {\n      canvas.height = height;\n    }\n    const ctx = this.context;\n    let pixelsData;\n    if (media.constructor === ImageData) {\n      pixelsData = media.data;\n    } else {\n      ctx.drawImage(media, 0, 0, width, height);\n      pixelsData = ctx.getImageData(0, 0, width, height).data;\n    }\n    const imageArray = new Array(height);\n    let index = 0;\n    for (let y = height - 1; y >= 0; y--) {\n      const row = imageArray[y] = new Array(width);\n      for (let x = 0; x < width; x++) {\n        const pixel = new Float32Array(4);\n        pixel[0] = pixelsData[index++] / 255; \n        pixel[1] = pixelsData[index++] / 255; \n        pixel[2] = pixelsData[index++] / 255; \n        pixel[3] = pixelsData[index++] / 255; \n        row[x] = pixel;\n      }\n    }\n    return imageArray;\n  }\n\n  getPixels(flip) {\n    const [width, height] = this.output;\n    return flip ? utils.flipPixels(this._imageData.data, width, height) : this._imageData.data.slice(0);\n  }\n\n  _imageTo3DArray(images) {\n    const imagesArray = new Array(images.length);\n    for (let i = 0; i < images.length; i++) {\n      imagesArray[i] = this._mediaTo2DArray(images[i]);\n    }\n    return imagesArray;\n  }\n\n  _resultKernelHeader() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    switch (this.output.length) {\n      case 1:\n        return this._mutableKernel1DResults();\n      case 2:\n        return this._mutableKernel2DResults();\n      case 3:\n        return this._mutableKernel3DResults();\n    }\n  }\n\n  _resultKernelBody(kernelString) {\n    switch (this.output.length) {\n      case 1:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel1DLoop(kernelString) : this._resultImmutableKernel1DLoop(kernelString)) + this._kernelOutput();\n      case 2:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel2DLoop(kernelString) : this._resultImmutableKernel2DLoop(kernelString)) + this._kernelOutput();\n      case 3:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel3DLoop(kernelString) : this._resultImmutableKernel3DLoop(kernelString)) + this._kernelOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalKernelBody(kernelThreadString) {\n    switch (this.output.length) {\n      case 2:\n        return this._graphicalKernel2DLoop(kernelThreadString) + this._graphicalOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalOutput() {\n    return `\n    this._imageData.data.set(this._colorData);\n    this.context.putImageData(this._imageData, 0, 0);\n    return;`\n  }\n\n  _getKernelResultTypeConstructorString() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return 'Float32Array';\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        return 'Array';\n      default:\n        if (this.graphical) {\n          return 'Float32Array';\n        }\n        throw new Error(`unhandled returnType ${ this.returnType }`);\n    }\n  }\n\n  _resultImmutableKernel1DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _mutableKernel1DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }`;\n  }\n\n  _resultMutableKernel1DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _resultImmutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _mutableKernel2DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n    }`;\n  }\n\n  _resultMutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y];\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _graphicalKernel2DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _resultImmutableKernel3DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _mutableKernel3DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n      }\n    }`;\n  }\n\n  _resultMutableKernel3DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z];\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y];\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _kernelOutput() {\n    if (!this.subKernels) {\n      return '\\n    return result;';\n    }\n    return `\\n    return {\n      result: result,\n      ${ this.subKernels.map(subKernel => `${ subKernel.property }: result_${ subKernel.name }`).join(',\\n      ') }\n    };`;\n  }\n\n  _mapSubKernels(fn) {\n    return this.subKernels === null ? [''] :\n      this.subKernels.map(fn);\n  }\n\n  destroy(removeCanvasReference) {\n    if (removeCanvasReference) {\n      delete this.canvas;\n    }\n  }\n\n  static destroyContext(context) {}\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, CPUFunctionNode).toJSON();\n    return json;\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    const [width, height] = this.output;\n    if (this.graphical) {\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n  }\n\n  prependString(value) {\n    if (this._kernelString) throw new Error('Kernel already built');\n    this._prependedString.push(value);\n  }\n\n  hasPrependString(value) {\n    return this._prependedString.indexOf(value) > -1;\n  }\n}\n\nmodule.exports = {\n  CPUKernel\n};\n},{\"../../utils\":113,\"../function-builder\":8,\"../kernel\":35,\"./function-node\":5,\"./kernel-string\":6}],8:[function(require,module,exports){\nclass FunctionBuilder {\n  static fromKernel(kernel, FunctionNode, extraNodeOptions) {\n    const {\n      kernelArguments,\n      kernelConstants,\n      argumentNames,\n      argumentSizes,\n      argumentBitRatios,\n      constants,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      nativeFunctions,\n      output,\n      optimizeFloatMemory,\n      precision,\n      plugins,\n      source,\n      subKernels,\n      functions,\n      leadingReturnStatement,\n      followingReturnStatement,\n      dynamicArguments,\n      dynamicOutput,\n    } = kernel;\n\n    const argumentTypes = new Array(kernelArguments.length);\n    const constantTypes = {};\n\n    for (let i = 0; i < kernelArguments.length; i++) {\n      argumentTypes[i] = kernelArguments[i].type;\n    }\n\n    for (let i = 0; i < kernelConstants.length; i++) {\n      const kernelConstant = kernelConstants[i];\n      constantTypes[kernelConstant.name] = kernelConstant.type;\n    }\n\n    const needsArgumentType = (functionName, index) => {\n      return functionBuilder.needsArgumentType(functionName, index);\n    };\n\n    const assignArgumentType = (functionName, index, type) => {\n      functionBuilder.assignArgumentType(functionName, index, type);\n    };\n\n    const lookupReturnType = (functionName, ast, requestingNode) => {\n      return functionBuilder.lookupReturnType(functionName, ast, requestingNode);\n    };\n\n    const lookupFunctionArgumentTypes = (functionName) => {\n      return functionBuilder.lookupFunctionArgumentTypes(functionName);\n    };\n\n    const lookupFunctionArgumentName = (functionName, argumentIndex) => {\n      return functionBuilder.lookupFunctionArgumentName(functionName, argumentIndex);\n    };\n\n    const lookupFunctionArgumentBitRatio = (functionName, argumentName) => {\n      return functionBuilder.lookupFunctionArgumentBitRatio(functionName, argumentName);\n    };\n\n    const triggerImplyArgumentType = (functionName, i, argumentType, requestingNode) => {\n      functionBuilder.assignArgumentType(functionName, i, argumentType, requestingNode);\n    };\n\n    const triggerImplyArgumentBitRatio = (functionName, argumentName, calleeFunctionName, argumentIndex) => {\n      functionBuilder.assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex);\n    };\n\n    const onFunctionCall = (functionName, calleeFunctionName, args) => {\n      functionBuilder.trackFunctionCall(functionName, calleeFunctionName, args);\n    };\n\n    const onNestedFunction = (ast, source) => {\n      const argumentNames = [];\n      for (let i = 0; i < ast.params.length; i++) {\n        argumentNames.push(ast.params[i].name);\n      }\n      const nestedFunction = new FunctionNode(source, Object.assign({}, nodeOptions, {\n        returnType: null,\n        ast,\n        name: ast.id.name,\n        argumentNames,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n      }));\n      nestedFunction.traceFunctionAST(ast);\n      functionBuilder.addFunctionNode(nestedFunction);\n    };\n\n    const nodeOptions = Object.assign({\n      isRootKernel: false,\n      onNestedFunction,\n      lookupReturnType,\n      lookupFunctionArgumentTypes,\n      lookupFunctionArgumentName,\n      lookupFunctionArgumentBitRatio,\n      needsArgumentType,\n      assignArgumentType,\n      triggerImplyArgumentType,\n      triggerImplyArgumentBitRatio,\n      onFunctionCall,\n      optimizeFloatMemory,\n      precision,\n      constants,\n      constantTypes,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      output,\n      plugins,\n      dynamicArguments,\n      dynamicOutput,\n    }, extraNodeOptions || {});\n\n    const rootNodeOptions = Object.assign({}, nodeOptions, {\n      isRootKernel: true,\n      name: 'kernel',\n      argumentNames,\n      argumentTypes,\n      argumentSizes,\n      argumentBitRatios,\n      leadingReturnStatement,\n      followingReturnStatement,\n    });\n\n    if (typeof source === 'object' && source.functionNodes) {\n      return new FunctionBuilder().fromJSON(source.functionNodes, FunctionNode);\n    }\n\n    const rootNode = new FunctionNode(source, rootNodeOptions);\n\n    let functionNodes = null;\n    if (functions) {\n      functionNodes = functions.map((fn) => new FunctionNode(fn.source, {\n        returnType: fn.returnType,\n        argumentTypes: fn.argumentTypes,\n        output,\n        plugins,\n        constants,\n        constantTypes,\n        constantBitRatios,\n        optimizeFloatMemory,\n        precision,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n        onNestedFunction,\n      }));\n    }\n\n    let subKernelNodes = null;\n    if (subKernels) {\n      subKernelNodes = subKernels.map((subKernel) => {\n        const { name, source } = subKernel;\n        return new FunctionNode(source, Object.assign({}, nodeOptions, {\n          name,\n          isSubKernel: true,\n          isRootKernel: false,\n        }));\n      });\n    }\n\n    const functionBuilder = new FunctionBuilder({\n      kernel,\n      rootNode,\n      functionNodes,\n      nativeFunctions,\n      subKernelNodes\n    });\n\n    return functionBuilder;\n  }\n\n  constructor(settings) {\n    settings = settings || {};\n    this.kernel = settings.kernel;\n    this.rootNode = settings.rootNode;\n    this.functionNodes = settings.functionNodes || [];\n    this.subKernelNodes = settings.subKernelNodes || [];\n    this.nativeFunctions = settings.nativeFunctions || [];\n    this.functionMap = {};\n    this.nativeFunctionNames = [];\n    this.lookupChain = [];\n    this.functionNodeDependencies = {};\n    this.functionCalls = {};\n\n    if (this.rootNode) {\n      this.functionMap['kernel'] = this.rootNode;\n    }\n\n    if (this.functionNodes) {\n      for (let i = 0; i < this.functionNodes.length; i++) {\n        this.functionMap[this.functionNodes[i].name] = this.functionNodes[i];\n      }\n    }\n\n    if (this.subKernelNodes) {\n      for (let i = 0; i < this.subKernelNodes.length; i++) {\n        this.functionMap[this.subKernelNodes[i].name] = this.subKernelNodes[i];\n      }\n    }\n\n    if (this.nativeFunctions) {\n      for (let i = 0; i < this.nativeFunctions.length; i++) {\n        const nativeFunction = this.nativeFunctions[i];\n        this.nativeFunctionNames.push(nativeFunction.name);\n      }\n    }\n  }\n\n  addFunctionNode(functionNode) {\n    if (!functionNode.name) throw new Error('functionNode.name needs set');\n    this.functionMap[functionNode.name] = functionNode;\n    if (functionNode.isRootKernel) {\n      this.rootNode = functionNode;\n    }\n  }\n\n  traceFunctionCalls(functionName, retList) {\n    functionName = functionName || 'kernel';\n    retList = retList || [];\n\n    if (this.nativeFunctionNames.indexOf(functionName) > -1) {\n      const nativeFunctionIndex = retList.indexOf(functionName);\n      if (nativeFunctionIndex === -1) {\n        retList.push(functionName);\n      } else {\n        const dependantNativeFunctionName = retList.splice(nativeFunctionIndex, 1)[0];\n        retList.push(dependantNativeFunctionName);\n      }\n      return retList;\n    }\n\n    const functionNode = this.functionMap[functionName];\n    if (functionNode) {\n      const functionIndex = retList.indexOf(functionName);\n      if (functionIndex === -1) {\n        retList.push(functionName);\n        functionNode.toString(); \n        for (let i = 0; i < functionNode.calledFunctions.length; ++i) {\n          this.traceFunctionCalls(functionNode.calledFunctions[i], retList);\n        }\n      } else {\n        const dependantFunctionName = retList.splice(functionIndex, 1)[0];\n        retList.push(dependantFunctionName);\n      }\n    }\n\n    return retList;\n  }\n\n  getPrototypeString(functionName) {\n    return this.getPrototypes(functionName).join('\\n');\n  }\n\n  getPrototypes(functionName) {\n    if (this.rootNode) {\n      this.rootNode.toString();\n    }\n    if (functionName) {\n      return this.getPrototypesFromFunctionNames(this.traceFunctionCalls(functionName, []).reverse());\n    }\n    return this.getPrototypesFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  getStringFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const node = this.functionMap[functionList[i]];\n      if (node) {\n        ret.push(this.functionMap[functionList[i]].toString());\n      }\n    }\n    return ret.join('\\n');\n  }\n\n  getPrototypesFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const functionName = functionList[i];\n      const functionIndex = this.nativeFunctionNames.indexOf(functionName);\n      if (functionIndex > -1) {\n        ret.push(this.nativeFunctions[functionIndex].source);\n        continue;\n      }\n      const node = this.functionMap[functionName];\n      if (node) {\n        ret.push(node.toString());\n      }\n    }\n    return ret;\n  }\n\n  toJSON() {\n    return this.traceFunctionCalls(this.rootNode.name).reverse().map(name => {\n      const nativeIndex = this.nativeFunctions.indexOf(name);\n      if (nativeIndex > -1) {\n        return {\n          name,\n          source: this.nativeFunctions[nativeIndex].source\n        };\n      } else if (this.functionMap[name]) {\n        return this.functionMap[name].toJSON();\n      } else {\n        throw new Error(`function ${ name } not found`);\n      }\n    });\n  }\n\n  fromJSON(jsonFunctionNodes, FunctionNode) {\n    this.functionMap = {};\n    for (let i = 0; i < jsonFunctionNodes.length; i++) {\n      const jsonFunctionNode = jsonFunctionNodes[i];\n      this.functionMap[jsonFunctionNode.settings.name] = new FunctionNode(jsonFunctionNode.ast, jsonFunctionNode.settings);\n    }\n    return this;\n  }\n\n  getString(functionName) {\n    if (functionName) {\n      return this.getStringFromFunctionNames(this.traceFunctionCalls(functionName).reverse());\n    }\n    return this.getStringFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  lookupReturnType(functionName, ast, requestingNode) {\n    if (ast.type !== 'CallExpression') {\n      throw new Error(`expected ast type of \"CallExpression\", but is ${ ast.type }`);\n    }\n    if (this._isNativeFunction(functionName)) {\n      return this._lookupNativeFunctionReturnType(functionName);\n    } else if (this._isFunction(functionName)) {\n      const node = this._getFunction(functionName);\n      if (node.returnType) {\n        return node.returnType;\n      } else {\n        for (let i = 0; i < this.lookupChain.length; i++) {\n          if (this.lookupChain[i].ast === ast) {\n            if (node.argumentTypes.length === 0 && ast.arguments.length > 0) {\n              const args = ast.arguments;\n              for (let j = 0; j < args.length; j++) {\n                this.lookupChain.push({\n                  name: requestingNode.name,\n                  ast: args[i],\n                  requestingNode\n                });\n                node.argumentTypes[j] = requestingNode.getType(args[j]);\n                this.lookupChain.pop();\n              }\n              return node.returnType = node.getType(node.getJsAST());\n            }\n\n            throw new Error('circlical logic detected!');\n          }\n        }\n        this.lookupChain.push({\n          name: requestingNode.name,\n          ast,\n          requestingNode\n        });\n        const type = node.getType(node.getJsAST());\n        this.lookupChain.pop();\n        return node.returnType = type;\n      }\n    }\n\n    return null;\n  }\n\n  _getFunction(functionName) {\n    if (!this._isFunction(functionName)) {\n      new Error(`Function ${functionName} not found`);\n    }\n    return this.functionMap[functionName];\n  }\n\n  _isFunction(functionName) {\n    return Boolean(this.functionMap[functionName]);\n  }\n\n  _getNativeFunction(functionName) {\n    for (let i = 0; i < this.nativeFunctions.length; i++) {\n      if (this.nativeFunctions[i].name === functionName) return this.nativeFunctions[i];\n    }\n    return null;\n  }\n\n  _isNativeFunction(functionName) {\n    return Boolean(this._getNativeFunction(functionName));\n  }\n\n  _lookupNativeFunctionReturnType(functionName) {\n    let nativeFunction = this._getNativeFunction(functionName);\n    if (nativeFunction) {\n      return nativeFunction.returnType;\n    }\n    throw new Error(`Native function ${ functionName } not found`);\n  }\n\n  lookupFunctionArgumentTypes(functionName) {\n    if (this._isNativeFunction(functionName)) {\n      return this._getNativeFunction(functionName).argumentTypes;\n    } else if (this._isFunction(functionName)) {\n      return this._getFunction(functionName).argumentTypes;\n    }\n    return null;\n  }\n\n  lookupFunctionArgumentName(functionName, argumentIndex) {\n    return this._getFunction(functionName).argumentNames[argumentIndex];\n  }\n\n  lookupFunctionArgumentBitRatio(functionName, argumentName) {\n    if (!this._isFunction(functionName)) {\n      throw new Error('function not found');\n    }\n    if (this.rootNode.name === functionName) {\n      const i = this.rootNode.argumentNames.indexOf(argumentName);\n      if (i !== -1) {\n        return this.rootNode.argumentBitRatios[i];\n      }\n    }\n    const node = this._getFunction(functionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error('argument not found');\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error('argument bit ratio not found');\n    }\n    return bitRatio;\n  }\n\n  needsArgumentType(functionName, i) {\n    if (!this._isFunction(functionName)) return false;\n    const fnNode = this._getFunction(functionName);\n    return !fnNode.argumentTypes[i];\n  }\n\n  assignArgumentType(functionName, i, argumentType, requestingNode) {\n    if (!this._isFunction(functionName)) return;\n    const fnNode = this._getFunction(functionName);\n    if (!fnNode.argumentTypes[i]) {\n      fnNode.argumentTypes[i] = argumentType;\n    }\n  }\n\n  assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex) {\n    const node = this._getFunction(functionName);\n    if (this._isNativeFunction(calleeFunctionName)) return null;\n    const calleeNode = this._getFunction(calleeFunctionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error(`Argument ${argumentName} not found in arguments from function ${functionName}`);\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error(`Bit ratio for argument ${argumentName} not found in function ${functionName}`);\n    }\n    if (!calleeNode.argumentBitRatios) {\n      calleeNode.argumentBitRatios = new Array(calleeNode.argumentNames.length);\n    }\n    const calleeBitRatio = calleeNode.argumentBitRatios[i];\n    if (typeof calleeBitRatio === 'number') {\n      if (calleeBitRatio !== bitRatio) {\n        throw new Error(`Incompatible bit ratio found at function ${functionName} at argument ${argumentName}`);\n      }\n      return calleeBitRatio;\n    }\n    calleeNode.argumentBitRatios[i] = bitRatio;\n    return bitRatio;\n  }\n\n  trackFunctionCall(functionName, calleeFunctionName, args) {\n    if (!this.functionNodeDependencies[functionName]) {\n      this.functionNodeDependencies[functionName] = new Set();\n      this.functionCalls[functionName] = [];\n    }\n    this.functionNodeDependencies[functionName].add(calleeFunctionName);\n    this.functionCalls[functionName].push(args);\n  }\n\n  getKernelResultType() {\n    return this.rootNode.returnType || this.rootNode.getType(this.rootNode.ast);\n  }\n\n  getSubKernelResultType(index) {\n    const subKernelNode = this.subKernelNodes[index];\n    let called = false;\n    for (let functionCallIndex = 0; functionCallIndex < this.rootNode.functionCalls.length; functionCallIndex++) {\n      const functionCall = this.rootNode.functionCalls[functionCallIndex];\n      if (functionCall.ast.callee.name === subKernelNode.name) {\n        called = true;\n      }\n    }\n    if (!called) {\n      throw new Error(`SubKernel ${ subKernelNode.name } never called by kernel`);\n    }\n    return subKernelNode.returnType || subKernelNode.getType(subKernelNode.getJsAST());\n  }\n\n  getReturnTypes() {\n    const result = {\n      [this.rootNode.name]: this.rootNode.getType(this.rootNode.ast),\n    };\n    const list = this.traceFunctionCalls(this.rootNode.name);\n    for (let i = 0; i < list.length; i++) {\n      const functionName = list[i];\n      const functionNode = this.functionMap[functionName];\n      result[functionName] = functionNode.getType(functionNode.ast);\n    }\n    return result;\n  }\n}\n\nmodule.exports = {\n  FunctionBuilder\n};\n},{}],9:[function(require,module,exports){\nconst acorn = require('acorn');\nconst { utils } = require('../utils');\nconst { FunctionTracer } = require('./function-tracer');\n\nclass FunctionNode {\n  constructor(source, settings) {\n    if (!source && !settings.ast) {\n      throw new Error('source parameter is missing');\n    }\n    settings = settings || {};\n    this.source = source;\n    this.ast = null;\n    this.name = typeof source === 'string' ? settings.isRootKernel ?\n      'kernel' :\n      (settings.name || utils.getFunctionNameFromString(source)) : null;\n    this.calledFunctions = [];\n    this.constants = {};\n    this.constantTypes = {};\n    this.constantBitRatios = {};\n    this.isRootKernel = false;\n    this.isSubKernel = false;\n    this.debug = null;\n    this.functions = null;\n    this.identifiers = null;\n    this.contexts = null;\n    this.functionCalls = null;\n    this.states = [];\n    this.needsArgumentType = null;\n    this.assignArgumentType = null;\n    this.lookupReturnType = null;\n    this.lookupFunctionArgumentTypes = null;\n    this.lookupFunctionArgumentBitRatio = null;\n    this.triggerImplyArgumentType = null;\n    this.triggerImplyArgumentBitRatio = null;\n    this.onNestedFunction = null;\n    this.onFunctionCall = null;\n    this.optimizeFloatMemory = null;\n    this.precision = null;\n    this.loopMaxIterations = null;\n    this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null);\n    this.argumentTypes = [];\n    this.argumentSizes = [];\n    this.argumentBitRatios = null;\n    this.returnType = null;\n    this.output = [];\n    this.plugins = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.dynamicOutput = null;\n    this.dynamicArguments = null;\n    this.strictTypingChecking = false;\n    this.fixIntegerDivisionAccuracy = null;\n\n    if (settings) {\n      for (const p in settings) {\n        if (!settings.hasOwnProperty(p)) continue;\n        if (!this.hasOwnProperty(p)) continue;\n        this[p] = settings[p];\n      }\n    }\n\n    this.literalTypes = {};\n\n    this.validate();\n    this._string = null;\n    this._internalVariableNames = {};\n  }\n\n  validate() {\n    if (typeof this.source !== 'string' && !this.ast) {\n      throw new Error('this.source not a string');\n    }\n\n    if (!this.ast && !utils.isFunctionString(this.source)) {\n      throw new Error('this.source not a function string');\n    }\n\n    if (!this.name) {\n      throw new Error('this.name could not be set');\n    }\n\n    if (this.argumentTypes.length > 0 && this.argumentTypes.length !== this.argumentNames.length) {\n      throw new Error(`argumentTypes count of ${ this.argumentTypes.length } exceeds ${ this.argumentNames.length }`);\n    }\n\n    if (this.output.length < 1) {\n      throw new Error('this.output is not big enough');\n    }\n  }\n\n  isIdentifierConstant(name) {\n    if (!this.constants) return false;\n    return this.constants.hasOwnProperty(name);\n  }\n\n  isInput(argumentName) {\n    return this.argumentTypes[this.argumentNames.indexOf(argumentName)] === 'Input';\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.state !== state) {\n      throw new Error(`Cannot popState ${ state } when in ${ this.state }`);\n    }\n    this.states.pop();\n  }\n\n  isState(state) {\n    return this.state === state;\n  }\n\n  get state() {\n    return this.states[this.states.length - 1];\n  }\n\n  astMemberExpressionUnroll(ast) {\n    if (ast.type === 'Identifier') {\n      return ast.name;\n    } else if (ast.type === 'ThisExpression') {\n      return 'this';\n    }\n\n    if (ast.type === 'MemberExpression') {\n      if (ast.object && ast.property) {\n        if (ast.object.hasOwnProperty('name') && ast.object.name !== 'Math') {\n          return this.astMemberExpressionUnroll(ast.property);\n        }\n\n        return (\n          this.astMemberExpressionUnroll(ast.object) +\n          '.' +\n          this.astMemberExpressionUnroll(ast.property)\n        );\n      }\n    }\n\n    if (ast.hasOwnProperty('expressions')) {\n      const firstExpression = ast.expressions[0];\n      if (firstExpression.type === 'Literal' && firstExpression.value === 0 && ast.expressions.length === 2) {\n        return this.astMemberExpressionUnroll(ast.expressions[1]);\n      }\n    }\n\n    throw this.astErrorOutput('Unknown astMemberExpressionUnroll', ast);\n  }\n\n  getJsAST(inParser) {\n    if (this.ast) {\n      return this.ast;\n    }\n    if (typeof this.source === 'object') {\n      this.traceFunctionAST(this.source);\n      return this.ast = this.source;\n    }\n\n    inParser = inParser || acorn;\n    if (inParser === null) {\n      throw new Error('Missing JS to AST parser');\n    }\n\n    const ast = Object.freeze(inParser.parse(`const parser_${ this.name } = ${ this.source };`, {\n      locations: true\n    }));\n    const functionAST = ast.body[0].declarations[0].init;\n    this.traceFunctionAST(functionAST);\n\n    if (!ast) {\n      throw new Error('Failed to parse JS code');\n    }\n\n    return this.ast = functionAST;\n  }\n\n  traceFunctionAST(ast) {\n    const { contexts, declarations, functions, identifiers, functionCalls } = new FunctionTracer(ast);\n    this.contexts = contexts;\n    this.identifiers = identifiers;\n    this.functionCalls = functionCalls;\n    this.functions = functions;\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const { ast, inForLoopInit, inForLoopTest } = declaration;\n      const { init } = ast;\n      const dependencies = this.getDependencies(init);\n      let valueType = null;\n\n      if (inForLoopInit && inForLoopTest) {\n        valueType = 'Integer';\n      } else {\n        if (init) {\n          const realType = this.getType(init);\n          switch (realType) {\n            case 'Integer':\n            case 'Float':\n            case 'Number':\n              if (init.type === 'MemberExpression') {\n                valueType = realType;\n              } else {\n                valueType = 'Number';\n              }\n              break;\n            case 'LiteralInteger':\n              valueType = 'Number';\n              break;\n            default:\n              valueType = realType;\n          }\n        }\n      }\n      declaration.valueType = valueType;\n      declaration.dependencies = dependencies;\n      declaration.isSafe = this.isSafeDependencies(dependencies);\n    }\n\n    for (let i = 0; i < functions.length; i++) {\n      this.onNestedFunction(functions[i], this.source);\n    }\n  }\n\n  getDeclaration(ast) {\n    for (let i = 0; i < this.identifiers.length; i++) {\n      const identifier = this.identifiers[i];\n      if (ast === identifier.ast) {\n        return identifier.declaration;\n      }\n    }\n    return null;\n  }\n\n  getVariableType(ast) {\n    if (ast.type !== 'Identifier') {\n      throw new Error(`ast of ${ast.type} not \"Identifier\"`);\n    }\n    let type = null;\n    const argumentIndex = this.argumentNames.indexOf(ast.name);\n    if (argumentIndex === -1) {\n      const declaration = this.getDeclaration(ast);\n      if (declaration) {\n        return declaration.valueType;\n      }\n    } else {\n      const argumentType = this.argumentTypes[argumentIndex];\n      if (argumentType) {\n        type = argumentType;\n      }\n    }\n    if (!type && this.strictTypingChecking) {\n      throw new Error(`Declaration of ${name} not found`);\n    }\n    return type;\n  }\n\n  getLookupType(type) {\n    if (!typeLookupMap.hasOwnProperty(type)) {\n      throw new Error(`unknown typeLookupMap ${ type }`);\n    }\n    return typeLookupMap[type];\n  }\n\n  getConstantType(constantName) {\n    if (this.constantTypes[constantName]) {\n      const type = this.constantTypes[constantName];\n      if (type === 'Float') {\n        return 'Number';\n      } else {\n        return type;\n      }\n    }\n    throw new Error(`Type for constant \"${ constantName }\" not declared`);\n  }\n\n  toString() {\n    if (this._string) return this._string;\n    return this._string = this.astGeneric(this.getJsAST(), []).join('').trim();\n  }\n\n  toJSON() {\n    const settings = {\n      source: this.source,\n      name: this.name,\n      constants: this.constants,\n      constantTypes: this.constantTypes,\n      isRootKernel: this.isRootKernel,\n      isSubKernel: this.isSubKernel,\n      debug: this.debug,\n      output: this.output,\n      loopMaxIterations: this.loopMaxIterations,\n      argumentNames: this.argumentNames,\n      argumentTypes: this.argumentTypes,\n      argumentSizes: this.argumentSizes,\n      returnType: this.returnType,\n      leadingReturnStatement: this.leadingReturnStatement,\n      followingReturnStatement: this.followingReturnStatement,\n    };\n\n    return {\n      ast: this.ast,\n      settings\n    };\n  }\n\n  getType(ast) {\n    if (Array.isArray(ast)) {\n      return this.getType(ast[ast.length - 1]);\n    }\n    switch (ast.type) {\n      case 'BlockStatement':\n        return this.getType(ast.body);\n      case 'ArrayExpression':\n        const childType = this.getType(ast.elements[0]);\n        switch (childType) {\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            return `Matrix(${ast.elements.length})`;\n        }\n        return `Array(${ ast.elements.length })`;\n      case 'Literal':\n        const literalKey = this.astKey(ast);\n        if (this.literalTypes[literalKey]) {\n          return this.literalTypes[literalKey];\n        }\n        if (Number.isInteger(ast.value)) {\n          return 'LiteralInteger';\n        } else if (ast.value === true || ast.value === false) {\n          return 'Boolean';\n        } else {\n          return 'Number';\n        }\n        case 'AssignmentExpression':\n          return this.getType(ast.left);\n        case 'CallExpression':\n          if (this.isAstMathFunction(ast)) {\n            return 'Number';\n          }\n          if (!ast.callee || !ast.callee.name) {\n            if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) {\n              const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name;\n              this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n              return this.lookupReturnType(functionName, ast, this);\n            }\n            if (this.getVariableSignature(ast.callee, true) === 'this.color') {\n              return null;\n            }\n            if (ast.callee.type === 'MemberExpression' && ast.callee.object && ast.callee.property && ast.callee.property.name && ast.arguments) {\n              const functionName = ast.callee.property.name;\n              this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n              return this.lookupReturnType(functionName, ast, this);\n            }\n            throw this.astErrorOutput('Unknown call expression', ast);\n          }\n          if (ast.callee && ast.callee.name) {\n            const functionName = ast.callee.name;\n            this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n            return this.lookupReturnType(functionName, ast, this);\n          }\n          throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n        case 'LogicalExpression':\n          return 'Boolean';\n        case 'BinaryExpression':\n          switch (ast.operator) {\n            case '%':\n            case '/':\n              if (this.fixIntegerDivisionAccuracy) {\n                return 'Number';\n              } else {\n                break;\n              }\n              case '>':\n              case '<':\n                return 'Boolean';\n              case '&':\n              case '|':\n              case '^':\n              case '<<':\n              case '>>':\n              case '>>>':\n                return 'Integer';\n          }\n          const type = this.getType(ast.left);\n          if (this.isState('skip-literal-correction')) return type;\n          if (type === 'LiteralInteger') {\n            const rightType = this.getType(ast.right);\n            if (rightType === 'LiteralInteger') {\n              if (ast.left.value % 1 === 0) {\n                return 'Integer';\n              } else {\n                return 'Float';\n              }\n            }\n            return rightType;\n          }\n          return typeLookupMap[type] || type;\n        case 'UpdateExpression':\n          return this.getType(ast.argument);\n        case 'UnaryExpression':\n          if (ast.operator === '~') {\n            return 'Integer';\n          }\n          return this.getType(ast.argument);\n        case 'VariableDeclaration': {\n          const declarations = ast.declarations;\n          let lastType;\n          for (let i = 0; i < declarations.length; i++) {\n            const declaration = declarations[i];\n            lastType = this.getType(declaration);\n          }\n          if (!lastType) {\n            throw this.astErrorOutput(`Unable to find type for declaration`, ast);\n          }\n          return lastType;\n        }\n        case 'VariableDeclarator':\n          const declaration = this.getDeclaration(ast.id);\n          if (!declaration) {\n            throw this.astErrorOutput(`Unable to find declarator`, ast);\n          }\n\n          if (!declaration.valueType) {\n            throw this.astErrorOutput(`Unable to find declarator valueType`, ast);\n          }\n\n          return declaration.valueType;\n        case 'Identifier':\n          if (ast.name === 'Infinity') {\n            return 'Number';\n          }\n          if (this.isAstVariable(ast)) {\n            const signature = this.getVariableSignature(ast);\n            if (signature === 'value') {\n              return this.getCheckVariableType(ast);\n            }\n          }\n          const origin = this.findIdentifierOrigin(ast);\n          if (origin && origin.init) {\n            return this.getType(origin.init);\n          }\n          return null;\n        case 'ReturnStatement':\n          return this.getType(ast.argument);\n        case 'MemberExpression':\n          if (this.isAstMathFunction(ast)) {\n            switch (ast.property.name) {\n              case 'ceil':\n                return 'Integer';\n              case 'floor':\n                return 'Integer';\n              case 'round':\n                return 'Integer';\n            }\n            return 'Number';\n          }\n          if (this.isAstVariable(ast)) {\n            const variableSignature = this.getVariableSignature(ast);\n            switch (variableSignature) {\n              case 'value[]':\n                return this.getLookupType(this.getCheckVariableType(ast.object));\n              case 'value[][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object));\n              case 'value[][][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object.object));\n              case 'value[][][][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object.object.object));\n              case 'value.thread.value':\n              case 'this.thread.value':\n                return 'Integer';\n              case 'this.output.value':\n                return this.dynamicOutput ? 'Integer' : 'LiteralInteger';\n              case 'this.constants.value':\n                return this.getConstantType(ast.property.name);\n              case 'this.constants.value[]':\n                return this.getLookupType(this.getConstantType(ast.object.property.name));\n              case 'this.constants.value[][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.property.name));\n              case 'this.constants.value[][][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.object.property.name));\n              case 'this.constants.value[][][][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.object.object.property.name));\n              case 'fn()[]':\n              case 'fn()[][]':\n              case 'fn()[][][]':\n                return this.getLookupType(this.getType(ast.object));\n              case 'value.value':\n                if (this.isAstMathVariable(ast)) {\n                  return 'Number';\n                }\n                switch (ast.property.name) {\n                  case 'r':\n                  case 'g':\n                  case 'b':\n                  case 'a':\n                    return this.getLookupType(this.getCheckVariableType(ast.object));\n                }\n                case '[][]':\n                  return 'Number';\n            }\n            throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n          }\n          throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n        case 'ConditionalExpression':\n          return this.getType(ast.consequent);\n        case 'FunctionDeclaration':\n        case 'FunctionExpression':\n          const lastReturn = this.findLastReturn(ast.body);\n          if (lastReturn) {\n            return this.getType(lastReturn);\n          }\n          return null;\n        case 'IfStatement':\n          return this.getType(ast.consequent);\n        case 'SequenceExpression':\n          return this.getType(ast.expressions[ast.expressions.length - 1]);\n        default:\n          throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n    }\n  }\n\n  getCheckVariableType(ast) {\n    const type = this.getVariableType(ast);\n    if (!type) {\n      throw this.astErrorOutput(`${ast.type} is not defined`, ast);\n    }\n    return type;\n  }\n\n  inferArgumentTypesIfNeeded(functionName, args) {\n    for (let i = 0; i < args.length; i++) {\n      if (!this.needsArgumentType(functionName, i)) continue;\n      const type = this.getType(args[i]);\n      if (!type) {\n        throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]);\n      }\n      this.assignArgumentType(functionName, i, type);\n    }\n  }\n\n  isAstMathVariable(ast) {\n    const mathProperties = [\n      'E',\n      'PI',\n      'SQRT2',\n      'SQRT1_2',\n      'LN2',\n      'LN10',\n      'LOG2E',\n      'LOG10E',\n    ];\n    return ast.type === 'MemberExpression' &&\n      ast.object && ast.object.type === 'Identifier' &&\n      ast.object.name === 'Math' &&\n      ast.property &&\n      ast.property.type === 'Identifier' &&\n      mathProperties.indexOf(ast.property.name) > -1;\n  }\n\n  isAstMathFunction(ast) {\n    const mathFunctions = [\n      'abs',\n      'acos',\n      'acosh',\n      'asin',\n      'asinh',\n      'atan',\n      'atan2',\n      'atanh',\n      'cbrt',\n      'ceil',\n      'clz32',\n      'cos',\n      'cosh',\n      'expm1',\n      'exp',\n      'floor',\n      'fround',\n      'imul',\n      'log',\n      'log2',\n      'log10',\n      'log1p',\n      'max',\n      'min',\n      'pow',\n      'random',\n      'round',\n      'sign',\n      'sin',\n      'sinh',\n      'sqrt',\n      'tan',\n      'tanh',\n      'trunc',\n    ];\n    return ast.type === 'CallExpression' &&\n      ast.callee &&\n      ast.callee.type === 'MemberExpression' &&\n      ast.callee.object &&\n      ast.callee.object.type === 'Identifier' &&\n      ast.callee.object.name === 'Math' &&\n      ast.callee.property &&\n      ast.callee.property.type === 'Identifier' &&\n      mathFunctions.indexOf(ast.callee.property.name) > -1;\n  }\n\n  isAstVariable(ast) {\n    return ast.type === 'Identifier' || ast.type === 'MemberExpression';\n  }\n\n  isSafe(ast) {\n    return this.isSafeDependencies(this.getDependencies(ast));\n  }\n\n  isSafeDependencies(dependencies) {\n    return dependencies && dependencies.every ? dependencies.every(dependency => dependency.isSafe) : true;\n  }\n\n  getDependencies(ast, dependencies, isNotSafe) {\n    if (!dependencies) {\n      dependencies = [];\n    }\n    if (!ast) return null;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.getDependencies(ast[i], dependencies, isNotSafe);\n      }\n      return dependencies;\n    }\n    switch (ast.type) {\n      case 'AssignmentExpression':\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'ConditionalExpression':\n        this.getDependencies(ast.test, dependencies, isNotSafe);\n        this.getDependencies(ast.alternate, dependencies, isNotSafe);\n        this.getDependencies(ast.consequent, dependencies, isNotSafe);\n        return dependencies;\n      case 'Literal':\n        dependencies.push({\n          origin: 'literal',\n          value: ast.value,\n          isSafe: isNotSafe === true ? false : ast.value > -Infinity && ast.value < Infinity && !isNaN(ast.value)\n        });\n        break;\n      case 'VariableDeclarator':\n        return this.getDependencies(ast.init, dependencies, isNotSafe);\n      case 'Identifier':\n        const declaration = this.getDeclaration(ast);\n        if (declaration) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'declaration',\n            isSafe: isNotSafe ? false : this.isSafeDependencies(declaration.dependencies),\n          });\n        } else if (this.argumentNames.indexOf(ast.name) > -1) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'argument',\n            isSafe: false,\n          });\n        } else if (this.strictTypingChecking) {\n          throw new Error(`Cannot find identifier origin \"${ast.name}\"`);\n        }\n        break;\n      case 'FunctionDeclaration':\n        return this.getDependencies(ast.body.body[ast.body.body.length - 1], dependencies, isNotSafe);\n      case 'ReturnStatement':\n        return this.getDependencies(ast.argument, dependencies);\n      case 'BinaryExpression':\n      case 'LogicalExpression':\n        isNotSafe = (ast.operator === '/' || ast.operator === '*');\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'UnaryExpression':\n      case 'UpdateExpression':\n        return this.getDependencies(ast.argument, dependencies, isNotSafe);\n      case 'VariableDeclaration':\n        return this.getDependencies(ast.declarations, dependencies, isNotSafe);\n      case 'ArrayExpression':\n        dependencies.push({\n          origin: 'declaration',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'CallExpression':\n        dependencies.push({\n          origin: 'function',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'MemberExpression':\n        const details = this.getMemberExpressionDetails(ast);\n        switch (details.signature) {\n          case 'value[]':\n            this.getDependencies(ast.object, dependencies, isNotSafe);\n            break;\n          case 'value[][]':\n            this.getDependencies(ast.object.object, dependencies, isNotSafe);\n            break;\n          case 'value[][][]':\n            this.getDependencies(ast.object.object.object, dependencies, isNotSafe);\n            break;\n          case 'this.output.value':\n            if (this.dynamicOutput) {\n              dependencies.push({\n                name: details.name,\n                origin: 'output',\n                isSafe: false,\n              });\n            }\n            break;\n        }\n        if (details) {\n          if (details.property) {\n            this.getDependencies(details.property, dependencies, isNotSafe);\n          }\n          if (details.xProperty) {\n            this.getDependencies(details.xProperty, dependencies, isNotSafe);\n          }\n          if (details.yProperty) {\n            this.getDependencies(details.yProperty, dependencies, isNotSafe);\n          }\n          if (details.zProperty) {\n            this.getDependencies(details.zProperty, dependencies, isNotSafe);\n          }\n          return dependencies;\n        }\n        case 'SequenceExpression':\n          return this.getDependencies(ast.expressions, dependencies, isNotSafe);\n        default:\n          throw this.astErrorOutput(`Unhandled type ${ ast.type } in getDependencies`, ast);\n    }\n    return dependencies;\n  }\n\n  getVariableSignature(ast, returnRawValue) {\n    if (!this.isAstVariable(ast)) {\n      throw new Error(`ast of type \"${ ast.type }\" is not a variable signature`);\n    }\n    if (ast.type === 'Identifier') {\n      return 'value';\n    }\n    const signature = [];\n    while (true) {\n      if (!ast) break;\n      if (ast.computed) {\n        signature.push('[]');\n      } else if (ast.type === 'ThisExpression') {\n        signature.unshift('this');\n      } else if (ast.property && ast.property.name) {\n        if (\n          ast.property.name === 'x' ||\n          ast.property.name === 'y' ||\n          ast.property.name === 'z'\n        ) {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        } else if (\n          ast.property.name === 'constants' ||\n          ast.property.name === 'thread' ||\n          ast.property.name === 'output'\n        ) {\n          signature.unshift('.' + ast.property.name);\n        } else {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        }\n      } else if (ast.name) {\n        signature.unshift(returnRawValue ? ast.name : 'value');\n      } else if (ast.callee && ast.callee.name) {\n        signature.unshift(returnRawValue ? ast.callee.name + '()' : 'fn()');\n      } else if (ast.elements) {\n        signature.unshift('[]');\n      } else {\n        signature.unshift('unknown');\n      }\n      ast = ast.object;\n    }\n\n    const signatureString = signature.join('');\n    if (returnRawValue) {\n      return signatureString;\n    }\n\n    const allowedExpressions = [\n      'value',\n      'value[]',\n      'value[][]',\n      'value[][][]',\n      'value[][][][]',\n      'value.value',\n      'value.thread.value',\n      'this.thread.value',\n      'this.output.value',\n      'this.constants.value',\n      'this.constants.value[]',\n      'this.constants.value[][]',\n      'this.constants.value[][][]',\n      'this.constants.value[][][][]',\n      'fn()[]',\n      'fn()[][]',\n      'fn()[][][]',\n      '[][]',\n    ];\n    if (allowedExpressions.indexOf(signatureString) > -1) {\n      return signatureString;\n    }\n    return null;\n  }\n\n  build() {\n    return this.toString().length > 0;\n  }\n\n  astGeneric(ast, retArr) {\n    if (ast === null) {\n      throw this.astErrorOutput('NULL ast', ast);\n    } else {\n      if (Array.isArray(ast)) {\n        for (let i = 0; i < ast.length; i++) {\n          this.astGeneric(ast[i], retArr);\n        }\n        return retArr;\n      }\n\n      switch (ast.type) {\n        case 'FunctionDeclaration':\n          return this.astFunctionDeclaration(ast, retArr);\n        case 'FunctionExpression':\n          return this.astFunctionExpression(ast, retArr);\n        case 'ReturnStatement':\n          return this.astReturnStatement(ast, retArr);\n        case 'Literal':\n          return this.astLiteral(ast, retArr);\n        case 'BinaryExpression':\n          return this.astBinaryExpression(ast, retArr);\n        case 'Identifier':\n          return this.astIdentifierExpression(ast, retArr);\n        case 'AssignmentExpression':\n          return this.astAssignmentExpression(ast, retArr);\n        case 'ExpressionStatement':\n          return this.astExpressionStatement(ast, retArr);\n        case 'EmptyStatement':\n          return this.astEmptyStatement(ast, retArr);\n        case 'BlockStatement':\n          return this.astBlockStatement(ast, retArr);\n        case 'IfStatement':\n          return this.astIfStatement(ast, retArr);\n        case 'SwitchStatement':\n          return this.astSwitchStatement(ast, retArr);\n        case 'BreakStatement':\n          return this.astBreakStatement(ast, retArr);\n        case 'ContinueStatement':\n          return this.astContinueStatement(ast, retArr);\n        case 'ForStatement':\n          return this.astForStatement(ast, retArr);\n        case 'WhileStatement':\n          return this.astWhileStatement(ast, retArr);\n        case 'DoWhileStatement':\n          return this.astDoWhileStatement(ast, retArr);\n        case 'VariableDeclaration':\n          return this.astVariableDeclaration(ast, retArr);\n        case 'VariableDeclarator':\n          return this.astVariableDeclarator(ast, retArr);\n        case 'ThisExpression':\n          return this.astThisExpression(ast, retArr);\n        case 'SequenceExpression':\n          return this.astSequenceExpression(ast, retArr);\n        case 'UnaryExpression':\n          return this.astUnaryExpression(ast, retArr);\n        case 'UpdateExpression':\n          return this.astUpdateExpression(ast, retArr);\n        case 'LogicalExpression':\n          return this.astLogicalExpression(ast, retArr);\n        case 'MemberExpression':\n          return this.astMemberExpression(ast, retArr);\n        case 'CallExpression':\n          return this.astCallExpression(ast, retArr);\n        case 'ArrayExpression':\n          return this.astArrayExpression(ast, retArr);\n        case 'DebuggerStatement':\n          return this.astDebuggerStatement(ast, retArr);\n        case 'ConditionalExpression':\n          return this.astConditionalExpression(ast, retArr);\n      }\n\n      throw this.astErrorOutput('Unknown ast type : ' + ast.type, ast);\n    }\n  }\n  astErrorOutput(error, ast) {\n    if (typeof this.source !== 'string') {\n      return new Error(error);\n    }\n\n    const debugString = utils.getAstString(this.source, ast);\n    const leadingSource = this.source.substr(ast.start);\n    const splitLines = leadingSource.split(/\\n/);\n    const lineBefore = splitLines.length > 0 ? splitLines[splitLines.length - 1] : 0;\n    return new Error(`${error} on line ${ splitLines.length }, position ${ lineBefore.length }:\\n ${ debugString }`);\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    return retArr;\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astFunction(ast, retArr) {\n    throw new Error(`\"astFunction\" not defined on ${ this.constructor.name }`);\n  }\n\n  astFunctionDeclaration(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  astFunctionExpression(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  isChildFunction(ast) {\n    for (let i = 0; i < this.functions.length; i++) {\n      if (this.functions[i] === ast) {\n        return true;\n      }\n    }\n    return false;\n  }\n  astReturnStatement(ast, retArr) {\n    return retArr;\n  }\n  astLiteral(ast, retArr) {\n    this.literalTypes[this.astKey(ast)] = 'Number';\n    return retArr;\n  }\n  astBinaryExpression(ast, retArr) {\n    return retArr;\n  }\n  astIdentifierExpression(ast, retArr) {\n    return retArr;\n  }\n  astAssignmentExpression(ast, retArr) {\n    return retArr;\n  }\n  astExpressionStatement(esNode, retArr) {\n    this.astGeneric(esNode.expression, retArr);\n    retArr.push(';');\n    return retArr;\n  }\n  astEmptyStatement(eNode, retArr) {\n    return retArr;\n  }\n  astBlockStatement(ast, retArr) {\n    return retArr;\n  }\n  astIfStatement(ast, retArr) {\n    return retArr;\n  }\n  astSwitchStatement(ast, retArr) {\n    return retArr;\n  }\n  astBreakStatement(brNode, retArr) {\n    retArr.push('break;');\n    return retArr;\n  }\n  astContinueStatement(crNode, retArr) {\n    retArr.push('continue;\\n');\n    return retArr;\n  }\n  astForStatement(ast, retArr) {\n    return retArr;\n  }\n  astWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  astDoWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  astVariableDeclarator(iVarDecNode, retArr) {\n    this.astGeneric(iVarDecNode.id, retArr);\n    if (iVarDecNode.init !== null) {\n      retArr.push('=');\n      this.astGeneric(iVarDecNode.init, retArr);\n    }\n    return retArr;\n  }\n  astThisExpression(ast, retArr) {\n    return retArr;\n  }\n  astSequenceExpression(sNode, retArr) {\n    const { expressions } = sNode;\n    const sequenceResult = [];\n    for (let i = 0; i < expressions.length; i++) {\n      const expression = expressions[i];\n      const expressionResult = [];\n      this.astGeneric(expression, expressionResult);\n      sequenceResult.push(expressionResult.join(''));\n    }\n    if (sequenceResult.length > 1) {\n      retArr.push('(', sequenceResult.join(','), ')');\n    } else {\n      retArr.push(sequenceResult[0]);\n    }\n    return retArr;\n  }\n  astUnaryExpression(uNode, retArr) {\n    const unaryResult = this.checkAndUpconvertBitwiseUnary(uNode, retArr);\n    if (unaryResult) {\n      return retArr;\n    }\n\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(uNode, retArr) {}\n\n  astUpdateExpression(uNode, retArr) {\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n  astLogicalExpression(logNode, retArr) {\n    retArr.push('(');\n    this.astGeneric(logNode.left, retArr);\n    retArr.push(logNode.operator);\n    this.astGeneric(logNode.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n  astMemberExpression(ast, retArr) {\n    return retArr;\n  }\n  astCallExpression(ast, retArr) {\n    return retArr;\n  }\n  astArrayExpression(ast, retArr) {\n    return retArr;\n  }\n\n  getMemberExpressionDetails(ast) {\n    if (ast.type !== 'MemberExpression') {\n      throw this.astErrorOutput(`Expression ${ ast.type } not a MemberExpression`, ast);\n    }\n    let name = null;\n    let type = null;\n    const variableSignature = this.getVariableSignature(ast);\n    switch (variableSignature) {\n      case 'value':\n        return null;\n      case 'value.thread.value':\n      case 'this.thread.value':\n      case 'this.output.value':\n        return {\n          signature: variableSignature,\n            type: 'Integer',\n            name: ast.property.name\n        };\n      case 'value[]':\n        if (typeof ast.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object),\n            xProperty: ast.property\n        };\n      case 'value[][]':\n        if (typeof ast.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object),\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][]':\n        if (typeof ast.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][][]':\n        if (typeof ast.object.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value.value':\n        if (typeof ast.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        if (this.isAstMathVariable(ast)) {\n          name = ast.property.name;\n          return {\n            name,\n            origin: 'Math',\n            type: 'Number',\n            signature: variableSignature,\n          };\n        }\n        switch (ast.property.name) {\n          case 'r':\n          case 'g':\n          case 'b':\n          case 'a':\n            name = ast.object.name;\n            return {\n              name,\n              property: ast.property.name,\n                origin: 'user',\n                signature: variableSignature,\n                type: 'Number'\n            };\n          default:\n            throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        case 'this.constants.value':\n          if (typeof ast.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n              signature: variableSignature,\n          };\n        case 'this.constants.value[]':\n          if (typeof ast.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n              signature: variableSignature,\n              xProperty: ast.property,\n          };\n        case 'this.constants.value[][]': {\n          if (typeof ast.object.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n            signature: variableSignature,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n          };\n        }\n        case 'this.constants.value[][][]': {\n          if (typeof ast.object.object.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.object.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n            signature: variableSignature,\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n          };\n        }\n        case 'fn()[]':\n        case 'fn()[][]':\n        case '[][]':\n          return {\n            signature: variableSignature,\n              property: ast.property,\n          };\n        default:\n          throw this.astErrorOutput('Unexpected expression', ast);\n    }\n  }\n\n  findIdentifierOrigin(astToFind) {\n    const stack = [this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack[0];\n      if (atNode.type === 'VariableDeclarator' && atNode.id && atNode.id.name && atNode.id.name === astToFind.name) {\n        return atNode;\n      }\n      stack.shift();\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      }\n    }\n    return null;\n  }\n\n  findLastReturn(ast) {\n    const stack = [ast || this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack.pop();\n      if (atNode.type === 'ReturnStatement') {\n        return atNode;\n      }\n      if (atNode.type === 'FunctionDeclaration') {\n        continue;\n      }\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      } else if (atNode.consequent) {\n        stack.push(atNode.consequent);\n      } else if (atNode.cases) {\n        stack.push(atNode.cases);\n      }\n    }\n    return null;\n  }\n\n  getInternalVariableName(name) {\n    if (!this._internalVariableNames.hasOwnProperty(name)) {\n      this._internalVariableNames[name] = 0;\n    }\n    this._internalVariableNames[name]++;\n    if (this._internalVariableNames[name] === 1) {\n      return name;\n    }\n    return name + this._internalVariableNames[name];\n  }\n\n  astKey(ast, separator = ',') {\n    if (!ast.start || !ast.end) throw new Error('AST start and end needed');\n    return `${ast.start}${separator}${ast.end}`;\n  }\n}\n\nconst typeLookupMap = {\n  'Number': 'Number',\n  'Float': 'Float',\n  'Integer': 'Integer',\n  'Array': 'Number',\n  'Array(2)': 'Number',\n  'Array(3)': 'Number',\n  'Array(4)': 'Number',\n  'Matrix(2)': 'Number',\n  'Matrix(3)': 'Number',\n  'Matrix(4)': 'Number',\n  'Array2D': 'Number',\n  'Array3D': 'Number',\n  'Input': 'Number',\n  'HTMLCanvas': 'Array(4)',\n  'OffscreenCanvas': 'Array(4)',\n  'HTMLImage': 'Array(4)',\n  'ImageBitmap': 'Array(4)',\n  'ImageData': 'Array(4)',\n  'HTMLVideo': 'Array(4)',\n  'HTMLImageArray': 'Array(4)',\n  'NumberTexture': 'Number',\n  'MemoryOptimizedNumberTexture': 'Number',\n  'Array1D(2)': 'Array(2)',\n  'Array1D(3)': 'Array(3)',\n  'Array1D(4)': 'Array(4)',\n  'Array2D(2)': 'Array(2)',\n  'Array2D(3)': 'Array(3)',\n  'Array2D(4)': 'Array(4)',\n  'Array3D(2)': 'Array(2)',\n  'Array3D(3)': 'Array(3)',\n  'Array3D(4)': 'Array(4)',\n  'ArrayTexture(1)': 'Number',\n  'ArrayTexture(2)': 'Array(2)',\n  'ArrayTexture(3)': 'Array(3)',\n  'ArrayTexture(4)': 'Array(4)',\n};\n\nmodule.exports = {\n  FunctionNode\n};\n},{\"../utils\":113,\"./function-tracer\":10,\"acorn\":1}],10:[function(require,module,exports){\nconst { utils } = require('../utils');\n\nfunction last(array) {\n  return array.length > 0 ? array[array.length - 1] : null;\n}\n\nconst states = {\n  trackIdentifiers: 'trackIdentifiers',\n  memberExpression: 'memberExpression',\n  inForLoopInit: 'inForLoopInit'\n};\n\nclass FunctionTracer {\n  constructor(ast) {\n    this.runningContexts = [];\n    this.functionContexts = [];\n    this.contexts = [];\n    this.functionCalls = [];\n    this.declarations = [];\n    this.identifiers = [];\n    this.functions = [];\n    this.returnStatements = [];\n    this.trackedIdentifiers = null;\n    this.states = [];\n    this.newFunctionContext();\n    this.scan(ast);\n  }\n\n  isState(state) {\n    return this.states[this.states.length - 1] === state;\n  }\n\n  hasState(state) {\n    return this.states.indexOf(state) > -1;\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.isState(state)) {\n      this.states.pop();\n    } else {\n      throw new Error(`Cannot pop the non-active state \"${state}\"`);\n    }\n  }\n\n  get currentFunctionContext() {\n    return last(this.functionContexts);\n  }\n\n  get currentContext() {\n    return last(this.runningContexts);\n  }\n\n  newFunctionContext() {\n    const newContext = { '@contextType': 'function' };\n    this.contexts.push(newContext);\n    this.functionContexts.push(newContext);\n  }\n\n  newContext(run) {\n    const newContext = Object.assign({ '@contextType': 'const/let' }, this.currentContext);\n    this.contexts.push(newContext);\n    this.runningContexts.push(newContext);\n    run();\n    const { currentFunctionContext } = this;\n    for (const p in currentFunctionContext) {\n      if (!currentFunctionContext.hasOwnProperty(p) || newContext.hasOwnProperty(p)) continue;\n      newContext[p] = currentFunctionContext[p];\n    }\n    this.runningContexts.pop();\n    return newContext;\n  }\n\n  useFunctionContext(run) {\n    const functionContext = last(this.functionContexts);\n    this.runningContexts.push(functionContext);\n    run();\n    this.runningContexts.pop();\n  }\n\n  getIdentifiers(run) {\n    const trackedIdentifiers = this.trackedIdentifiers = [];\n    this.pushState(states.trackIdentifiers);\n    run();\n    this.trackedIdentifiers = null;\n    this.popState(states.trackIdentifiers);\n    return trackedIdentifiers;\n  }\n\n  getDeclaration(name) {\n    const { currentContext, currentFunctionContext, runningContexts } = this;\n    const declaration = currentContext[name] || currentFunctionContext[name] || null;\n\n    if (\n      !declaration &&\n      currentContext === currentFunctionContext &&\n      runningContexts.length > 0\n    ) {\n      const previousRunningContext = runningContexts[runningContexts.length - 2];\n      if (previousRunningContext[name]) {\n        return previousRunningContext[name];\n      }\n    }\n\n    return declaration;\n  }\n\n  scan(ast) {\n    if (!ast) return;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.scan(ast[i]);\n      }\n      return;\n    }\n    switch (ast.type) {\n      case 'Program':\n        this.useFunctionContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'BlockStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'AssignmentExpression':\n      case 'LogicalExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'BinaryExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'UpdateExpression':\n        if (ast.operator === '++') {\n          const declaration = this.getDeclaration(ast.argument.name);\n          if (declaration) {\n            declaration.suggestedType = 'Integer';\n          }\n        }\n        this.scan(ast.argument);\n        break;\n      case 'UnaryExpression':\n        this.scan(ast.argument);\n        break;\n      case 'VariableDeclaration':\n        if (ast.kind === 'var') {\n          this.useFunctionContext(() => {\n            ast.declarations = utils.normalizeDeclarations(ast);\n            this.scan(ast.declarations);\n          });\n        } else {\n          ast.declarations = utils.normalizeDeclarations(ast);\n          this.scan(ast.declarations);\n        }\n        break;\n      case 'VariableDeclarator': {\n        const { currentContext } = this;\n        const inForLoopInit = this.hasState(states.inForLoopInit);\n        const declaration = {\n          ast: ast,\n          context: currentContext,\n          name: ast.id.name,\n          origin: 'declaration',\n          inForLoopInit,\n          inForLoopTest: null,\n          assignable: currentContext === this.currentFunctionContext || (!inForLoopInit && !currentContext.hasOwnProperty(ast.id.name)),\n          suggestedType: null,\n          valueType: null,\n          dependencies: null,\n          isSafe: null,\n        };\n        if (!currentContext[ast.id.name]) {\n          currentContext[ast.id.name] = declaration;\n        }\n        this.declarations.push(declaration);\n        this.scan(ast.id);\n        this.scan(ast.init);\n        break;\n      }\n      case 'FunctionExpression':\n      case 'FunctionDeclaration':\n        if (this.runningContexts.length === 0) {\n          this.scan(ast.body);\n        } else {\n          this.functions.push(ast);\n        }\n        break;\n      case 'IfStatement':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        if (ast.alternate) this.scan(ast.alternate);\n        break;\n      case 'ForStatement': {\n        let testIdentifiers;\n        const context = this.newContext(() => {\n          this.pushState(states.inForLoopInit);\n          this.scan(ast.init);\n          this.popState(states.inForLoopInit);\n\n          testIdentifiers = this.getIdentifiers(() => {\n            this.scan(ast.test);\n          });\n\n          this.scan(ast.update);\n          this.newContext(() => {\n            this.scan(ast.body);\n          });\n        });\n\n        if (testIdentifiers) {\n          for (const p in context) {\n            if (p === '@contextType') continue;\n            if (testIdentifiers.indexOf(p) > -1) {\n              context[p].inForLoopTest = true;\n            }\n          }\n        }\n        break;\n      }\n      case 'DoWhileStatement':\n      case 'WhileStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n          this.scan(ast.test);\n        });\n        break;\n      case 'Identifier': {\n        if (this.isState(states.trackIdentifiers)) {\n          this.trackedIdentifiers.push(ast.name);\n        }\n        this.identifiers.push({\n          context: this.currentContext,\n          declaration: this.getDeclaration(ast.name),\n          ast,\n        });\n        break;\n      }\n      case 'ReturnStatement':\n        this.returnStatements.push(ast);\n        this.scan(ast.argument);\n        break;\n      case 'MemberExpression':\n        this.pushState(states.memberExpression);\n        this.scan(ast.object);\n        this.scan(ast.property);\n        this.popState(states.memberExpression);\n        break;\n      case 'ExpressionStatement':\n        this.scan(ast.expression);\n        break;\n      case 'SequenceExpression':\n        this.scan(ast.expressions);\n        break;\n      case 'CallExpression':\n        this.functionCalls.push({\n          context: this.currentContext,\n          ast,\n        });\n        this.scan(ast.arguments);\n        break;\n      case 'ArrayExpression':\n        this.scan(ast.elements);\n        break;\n      case 'ConditionalExpression':\n        this.scan(ast.test);\n        this.scan(ast.alternate);\n        this.scan(ast.consequent);\n        break;\n      case 'SwitchStatement':\n        this.scan(ast.discriminant);\n        this.scan(ast.cases);\n        break;\n      case 'SwitchCase':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        break;\n\n      case 'ThisExpression':\n      case 'Literal':\n      case 'DebuggerStatement':\n      case 'EmptyStatement':\n      case 'BreakStatement':\n      case 'ContinueStatement':\n        break;\n      default:\n        throw new Error(`unhandled type \"${ast.type}\"`);\n    }\n  }\n}\n\nmodule.exports = {\n  FunctionTracer,\n};\n},{\"../utils\":113}],11:[function(require,module,exports){\nconst { glWiretap } = require('gl-wiretap');\nconst { utils } = require('../../utils');\n\nfunction toStringWithoutUtils(fn) {\n  return fn.toString()\n    .replace('=>', '')\n    .replace(/^function /, '')\n    .replace(/utils[.]/g, '/*utils.*/');\n}\n\nfunction glKernelString(Kernel, args, originKernel, setupContextString, destroyContextString) {\n  if (!originKernel.built) {\n    originKernel.build.apply(originKernel, args);\n  }\n  args = args ? Array.from(args).map(arg => {\n    switch (typeof arg) {\n      case 'boolean':\n        return new Boolean(arg);\n      case 'number':\n        return new Number(arg);\n      default:\n        return arg;\n    }\n  }) : null;\n  const uploadedValues = [];\n  const postResult = [];\n  const context = glWiretap(originKernel.context, {\n    useTrackablePrimitives: true,\n    onReadPixels: (targetName) => {\n      if (kernel.subKernels) {\n        if (!subKernelsResultVariableSetup) {\n          postResult.push(`    const result = { result: ${getRenderString(targetName, kernel)} };`);\n          subKernelsResultVariableSetup = true;\n        } else {\n          const property = kernel.subKernels[subKernelsResultIndex++].property;\n          postResult.push(`    result${isNaN(property) ? '.' + property : `[${property}]`} = ${getRenderString(targetName, kernel)};`);\n        }\n        if (subKernelsResultIndex === kernel.subKernels.length) {\n          postResult.push('    return result;');\n        }\n        return;\n      }\n      if (targetName) {\n        postResult.push(`    return ${getRenderString(targetName, kernel)};`);\n      } else {\n        postResult.push(`    return null;`);\n      }\n    },\n    onUnrecognizedArgumentLookup: (argument) => {\n      const argumentName = findKernelValue(argument, kernel.kernelArguments, [], context, uploadedValues);\n      if (argumentName) {\n        return argumentName;\n      }\n      const constantName = findKernelValue(argument, kernel.kernelConstants, constants ? Object.keys(constants).map(key => constants[key]) : [], context, uploadedValues);\n      if (constantName) {\n        return constantName;\n      }\n      return null;\n    }\n  });\n  let subKernelsResultVariableSetup = false;\n  let subKernelsResultIndex = 0;\n  const {\n    source,\n    canvas,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    kernelArguments,\n    kernelConstants,\n    tactic,\n  } = originKernel;\n  const kernel = new Kernel(source, {\n    canvas,\n    context,\n    checkContext: false,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    tactic,\n  });\n  let result = [];\n  context.setIndent(2);\n  kernel.build.apply(kernel, args);\n  result.push(context.toString());\n  context.reset();\n\n  kernel.kernelArguments.forEach((kernelArgument, i) => {\n    switch (kernelArgument.type) {\n      case 'Integer':\n      case 'Boolean':\n      case 'Number':\n      case 'Float':\n      case 'Array':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'HTMLCanvas':\n      case 'HTMLImage':\n      case 'HTMLVideo':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'HTMLImageArray':\n        for (let imageIndex = 0; imageIndex < args[i].length; imageIndex++) {\n          const arg = args[i];\n          context.insertVariable(`uploadValue_${kernelArgument.name}[${imageIndex}]`, arg[imageIndex]);\n        }\n        break;\n      case 'Input':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'MemoryOptimizedNumberTexture':\n      case 'NumberTexture':\n      case 'Array1D(2)':\n      case 'Array1D(3)':\n      case 'Array1D(4)':\n      case 'Array2D(2)':\n      case 'Array2D(3)':\n      case 'Array2D(4)':\n      case 'Array3D(2)':\n      case 'Array3D(3)':\n      case 'Array3D(4)':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, args[i].texture);\n        break;\n      default:\n        throw new Error(`unhandled kernelArgumentType insertion for glWiretap of type ${kernelArgument.type}`);\n    }\n  });\n  result.push('/** start of injected functions **/');\n  result.push(`function ${toStringWithoutUtils(utils.flattenTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten2dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten3dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten4dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.isArray)}`);\n  if (kernel.renderOutput !== kernel.renderTexture && kernel.formatValues) {\n    result.push(\n      `  const renderOutput = function ${toStringWithoutUtils(kernel.formatValues)};`\n    );\n  }\n  result.push('/** end of injected functions **/');\n  result.push(`  const innerKernel = function (${kernel.kernelArguments.map(kernelArgument => kernelArgument.varName).join(', ')}) {`);\n  context.setIndent(4);\n  kernel.run.apply(kernel, args);\n  if (kernel.renderKernels) {\n    kernel.renderKernels();\n  } else if (kernel.renderOutput) {\n    kernel.renderOutput();\n  }\n  result.push('    /** start setup uploads for kernel values **/');\n  kernel.kernelArguments.forEach(kernelArgument => {\n    result.push('    ' + kernelArgument.getStringValueHandler().split('\\n').join('\\n    '));\n  });\n  result.push('    /** end setup uploads for kernel values **/');\n  result.push(context.toString());\n  if (kernel.renderOutput === kernel.renderTexture) {\n    context.reset();\n    const framebufferName = context.getContextVariableName(kernel.framebuffer);\n    if (kernel.renderKernels) {\n      const results = kernel.renderKernels();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n      result: {\n        texture: ${ textureName },\n        type: '${ results.result.type }',\n        toArray: ${ getToArrayString(results.result, textureName, framebufferName) }\n      },`);\n      const { subKernels, mappedTextures } = kernel;\n      for (let i = 0; i < subKernels.length; i++) {\n        const texture = mappedTextures[i];\n        const subKernel = subKernels[i];\n        const subKernelResult = results[subKernel.property];\n        const subKernelTextureName = context.getContextVariableName(texture.texture);\n        result.push(`\n      ${subKernel.property}: {\n        texture: ${ subKernelTextureName },\n        type: '${ subKernelResult.type }',\n        toArray: ${ getToArrayString(subKernelResult, subKernelTextureName, framebufferName) }\n      },`);\n      }\n      result.push(`    };`);\n    } else {\n      const rendered = kernel.renderOutput();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n        texture: ${ textureName },\n        type: '${ rendered.type }',\n        toArray: ${ getToArrayString(rendered, textureName, framebufferName) }\n      };`);\n    }\n  }\n  result.push(`    ${destroyContextString ? '\\n' + destroyContextString + '    ': ''}`);\n  result.push(postResult.join('\\n'));\n  result.push('  };');\n  if (kernel.graphical) {\n    result.push(getGetPixelsString(kernel));\n    result.push(`  innerKernel.getPixels = getPixels;`);\n  }\n  result.push('  return innerKernel;');\n\n  let constantsUpload = [];\n  kernelConstants.forEach((kernelConstant) => {\n    constantsUpload.push(`${kernelConstant.getStringValueHandler()}`);\n  });\n  return `function kernel(settings) {\n  const { context, constants } = settings;\n  ${constantsUpload.join('')}\n  ${setupContextString ? setupContextString : ''}\n${result.join('\\n')}\n}`;\n}\n\nfunction getRenderString(targetName, kernel) {\n  const readBackValue = kernel.precision === 'single' ? targetName : `new Float32Array(${targetName}.buffer)`;\n  if (kernel.output[2]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]}, ${kernel.output[2]})`;\n  }\n  if (kernel.output[1]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]})`;\n  }\n\n  return `renderOutput(${readBackValue}, ${kernel.output[0]})`;\n}\n\nfunction getGetPixelsString(kernel) {\n  const getPixels = kernel.getPixels.toString();\n  const useFunctionKeyword = !/^function/.test(getPixels);\n  return utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ getPixels }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      }\n      return null;\n    },\n    thisLookup: (property) => {\n      if (property === 'context') {\n        return null;\n      }\n      if (kernel.hasOwnProperty(property)) {\n        return JSON.stringify(kernel[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n}\n\nfunction getToArrayString(kernelResult, textureName, framebufferName) {\n  const toArray = kernelResult.toArray.toString();\n  const useFunctionKeyword = !/^function/.test(toArray);\n  const flattenedFunctions = utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ toArray }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      } else if (object === 'this') {\n        if (name === 'framebuffer') {\n          return '';\n        }\n        return `${useFunctionKeyword ? 'function ' : ''}${kernelResult[name].toString()}`;\n      } else {\n        throw new Error('unhandled fromObject');\n      }\n    },\n    thisLookup: (property, isDeclaration) => {\n      if (property === 'texture') {\n        return textureName;\n      }\n      if (property === 'context') {\n        if (isDeclaration) return null;\n        return 'gl';\n      }\n      if (kernelResult.hasOwnProperty(property)) {\n        return JSON.stringify(kernelResult[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n  return `() => {\n  function framebuffer() { return ${framebufferName}; };\n  ${flattenedFunctions}\n  return toArray();\n  }`;\n}\n\nfunction findKernelValue(argument, kernelValues, values, context, uploadedValues) {\n  if (argument === null) return null;\n  if (kernelValues === null) return null;\n  switch (typeof argument) {\n    case 'boolean':\n    case 'number':\n      return null;\n  }\n  if (\n    typeof HTMLImageElement !== 'undefined' &&\n    argument instanceof HTMLImageElement\n  ) {\n    for (let i = 0; i < kernelValues.length; i++) {\n      const kernelValue = kernelValues[i];\n      if (kernelValue.type !== 'HTMLImageArray' && kernelValue) continue;\n      if (kernelValue.uploadValue !== argument) continue;\n      const variableIndex = values[i].indexOf(argument);\n      if (variableIndex === -1) continue;\n      const variableName = `uploadValue_${kernelValue.name}[${variableIndex}]`;\n      context.insertVariable(variableName, argument);\n      return variableName;\n    }\n  }\n\n  for (let i = 0; i < kernelValues.length; i++) {\n    const kernelValue = kernelValues[i];\n    if (argument !== kernelValue.uploadValue) continue;\n    const variable = `uploadValue_${kernelValue.name}`;\n    context.insertVariable(variable, kernelValue);\n    return variable;\n  }\n  return null;\n}\n\nmodule.exports = {\n  glKernelString\n};\n},{\"../../utils\":113,\"gl-wiretap\":2}],12:[function(require,module,exports){\nconst { Kernel } = require('../kernel');\nconst { utils } = require('../../utils');\nconst { GLTextureArray2Float } = require('./texture/array-2-float');\nconst { GLTextureArray2Float2D } = require('./texture/array-2-float-2d');\nconst { GLTextureArray2Float3D } = require('./texture/array-2-float-3d');\nconst { GLTextureArray3Float } = require('./texture/array-3-float');\nconst { GLTextureArray3Float2D } = require('./texture/array-3-float-2d');\nconst { GLTextureArray3Float3D } = require('./texture/array-3-float-3d');\nconst { GLTextureArray4Float } = require('./texture/array-4-float');\nconst { GLTextureArray4Float2D } = require('./texture/array-4-float-2d');\nconst { GLTextureArray4Float3D } = require('./texture/array-4-float-3d');\nconst { GLTextureFloat } = require('./texture/float');\nconst { GLTextureFloat2D } = require('./texture/float-2d');\nconst { GLTextureFloat3D } = require('./texture/float-3d');\nconst { GLTextureMemoryOptimized } = require('./texture/memory-optimized');\nconst { GLTextureMemoryOptimized2D } = require('./texture/memory-optimized-2d');\nconst { GLTextureMemoryOptimized3D } = require('./texture/memory-optimized-3d');\nconst { GLTextureUnsigned } = require('./texture/unsigned');\nconst { GLTextureUnsigned2D } = require('./texture/unsigned-2d');\nconst { GLTextureUnsigned3D } = require('./texture/unsigned-3d');\nconst { GLTextureGraphical } = require('./texture/graphical');\n\nclass GLKernel extends Kernel {\n  static get mode() {\n    return 'gpu';\n  }\n\n  static getIsFloatRead() {\n    const kernelString = `function kernelFunction() {\n      return 1;\n    }`;\n    const kernel = new this(kernelString, {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [1],\n      precision: 'single',\n      returnType: 'Number',\n      tactic: 'speed',\n    });\n    kernel.build();\n    kernel.run();\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return result[0] === 1;\n  }\n\n  static getIsIntegerDivisionAccurate() {\n    function kernelFunction(v1, v2) {\n      return v1[this.thread.x] / v2[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [2],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [6, 6030401],\n      [3, 3991]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return result[0] === 2 && result[1] === 1511;\n  }\n\n  static getIsSpeedTacticSupported() {\n    function kernelFunction(value) {\n      return value[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [4],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [0, 1, 2, 3]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return Math.round(result[0]) === 0 && Math.round(result[1]) === 1 && Math.round(result[2]) === 2 && Math.round(result[3]) === 3;\n  }\n\n  static get testCanvas() {\n    throw new Error(`\"testCanvas\" not defined on ${ this.name }`);\n  }\n\n  static get testContext() {\n    throw new Error(`\"testContext\" not defined on ${ this.name }`);\n  }\n\n  static getFeatures() {\n    const gl = this.testContext;\n    const isDrawBuffers = this.getIsDrawBuffers();\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      isTextureFloat: this.getIsTextureFloat(),\n      isDrawBuffers,\n      kernelMap: isDrawBuffers,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  static setupFeatureChecks() {\n    throw new Error(`\"setupFeatureChecks\" not defined on ${ this.name }`);\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return kernel.getVariablePrecisionString() + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  setFixIntegerDivisionAccuracy(fix) {\n    this.fixIntegerDivisionAccuracy = fix;\n    return this;\n  }\n\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  setFloatTextures(flag) {\n    utils.warnDeprecated('method', 'setFloatTextures', 'setOptimizeFloatMemory');\n    this.floatTextures = flag;\n    return this;\n  }\n\n  static nativeFunctionArguments(source) {\n    const argumentTypes = [];\n    const argumentNames = [];\n    const states = [];\n    const isStartingVariableName = /^[a-zA-Z_]/;\n    const isVariableChar = /[a-zA-Z_0-9]/;\n    let i = 0;\n    let argumentName = null;\n    let argumentType = null;\n    while (i < source.length) {\n      const char = source[i];\n      const nextChar = source[i + 1];\n      const state = states.length > 0 ? states[states.length - 1] : null;\n\n      if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '*') {\n        states.push('MULTI_LINE_COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'MULTI_LINE_COMMENT' && char === '*' && nextChar === '/') {\n        states.pop();\n        i += 2;\n        continue;\n      }\n\n      else if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '/') {\n        states.push('COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'COMMENT' && char === '\\n') {\n        states.pop();\n        i++;\n        continue;\n      }\n\n      else if (state === null && char === '(') {\n        states.push('FUNCTION_ARGUMENTS');\n        i++;\n        continue;\n      } else if (state === 'FUNCTION_ARGUMENTS') {\n        if (char === ')') {\n          states.pop();\n          break;\n        }\n        if (char === 'f' && nextChar === 'l' && source[i + 2] === 'o' && source[i + 3] === 'a' && source[i + 4] === 't' && source[i + 5] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'float';\n          argumentName = '';\n          i += 6;\n          continue;\n        } else if (char === 'i' && nextChar === 'n' && source[i + 2] === 't' && source[i + 3] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'int';\n          argumentName = '';\n          i += 4;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '2' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec2';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '3' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec3';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '4' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec4';\n          argumentName = '';\n          i += 5;\n          continue;\n        }\n      }\n\n      else if (state === 'DECLARE_VARIABLE') {\n        if (argumentName === '') {\n          if (char === ' ') {\n            i++;\n            continue;\n          }\n          if (!isStartingVariableName.test(char)) {\n            throw new Error('variable name is not expected string');\n          }\n        }\n        argumentName += char;\n        if (!isVariableChar.test(nextChar)) {\n          states.pop();\n          argumentNames.push(argumentName);\n          argumentTypes.push(typeMap[argumentType]);\n        }\n      }\n\n      i++;\n    }\n    if (states.length > 0) {\n      throw new Error('GLSL function was not parsable');\n    }\n    return {\n      argumentNames,\n      argumentTypes,\n    };\n  }\n\n  static nativeFunctionReturnType(source) {\n    return typeMap[source.match(/int|float|vec[2-4]/)[0]];\n  }\n\n  static combineKernels(combinedKernel, lastKernel) {\n    combinedKernel.apply(null, arguments);\n    const {\n      texSize,\n      context,\n      threadDim\n    } = lastKernel.texSize;\n    let result;\n    if (lastKernel.precision === 'single') {\n      const w = texSize[0];\n      const h = Math.ceil(texSize[1] / 4);\n      result = new Float32Array(w * h * 4 * 4);\n      context.readPixels(0, 0, w, h * 4, context.RGBA, context.FLOAT, result);\n    } else {\n      const bytes = new Uint8Array(texSize[0] * texSize[1] * 4);\n      context.readPixels(0, 0, texSize[0], texSize[1], context.RGBA, context.UNSIGNED_BYTE, bytes);\n      result = new Float32Array(bytes.buffer);\n    }\n\n    result = result.subarray(0, threadDim[0] * threadDim[1] * threadDim[2]);\n\n    if (lastKernel.output.length === 1) {\n      return result;\n    } else if (lastKernel.output.length === 2) {\n      return utils.splitArray(result, lastKernel.output[0]);\n    } else if (lastKernel.output.length === 3) {\n      const cube = utils.splitArray(result, lastKernel.output[0] * lastKernel.output[1]);\n      return cube.map(function(x) {\n        return utils.splitArray(x, lastKernel.output[0]);\n      });\n    }\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.transferValues = null;\n    this.formatValues = null;\n    this.TextureConstructor = null;\n    this.renderOutput = null;\n    this.renderRawOutput = null;\n    this.texSize = null;\n    this.translatedSource = null;\n    this.compiledFragmentShader = null;\n    this.compiledVertexShader = null;\n    this.switchingKernels = null;\n    this._textureSwitched = null;\n    this._mappedTextureSwitched = null;\n  }\n\n  checkTextureSize() {\n    const { features } = this.constructor;\n    if (this.texSize[0] > features.maxTextureSize || this.texSize[1] > features.maxTextureSize) {\n      throw new Error(`Texture size [${this.texSize[0]},${this.texSize[1]}] generated by kernel is larger than supported size [${features.maxTextureSize},${features.maxTextureSize}]`);\n    }\n  }\n\n  translateSource() {\n    throw new Error(`\"translateSource\" not defined on ${this.constructor.name}`);\n  }\n\n  pickRenderStrategy(args) {\n    if (this.graphical) {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = (pixels) => pixels;\n      this.TextureConstructor = GLTextureGraphical;\n      return null;\n    }\n    if (this.precision === 'unsigned') {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = this.readPackedPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              return null;\n            }\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              return this.requestFallback(args);\n        }\n      } else {\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToArrays;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            this.renderOutput = this.renderValues;\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              this.formatValues = utils.erect3DPackedFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              this.formatValues = utils.erect2DPackedFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              this.formatValues = utils.erectPackedFloat;\n              return null;\n            }\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              return this.requestFallback(args);\n        }\n      }\n    } else if (this.precision === 'single') {\n      this.renderRawOutput = this.readFloatPixelsToFloat32Array;\n      this.transferValues = this.readFloatPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.optimizeFloatMemory) {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureMemoryOptimized;\n                return null;\n              }\n            } else {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureFloat3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureFloat2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureFloat;\n                return null;\n              }\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              return null;\n            }\n          }\n        }\n      }\n      this.renderOutput = this.renderValues;\n      if (this.subKernels !== null) {\n        this.renderKernels = this.renderKernelsToArrays;\n      }\n      if (this.optimizeFloatMemory) {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized3D;\n              this.formatValues = utils.erectMemoryOptimized3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized2D;\n              this.formatValues = utils.erectMemoryOptimized2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureMemoryOptimized;\n              this.formatValues = utils.erectMemoryOptimizedFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      } else {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureFloat3D;\n              this.formatValues = utils.erect3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureFloat2D;\n              this.formatValues = utils.erect2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureFloat;\n              this.formatValues = utils.erectFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      }\n    } else {\n      throw new Error(`unhandled precision of \"${this.precision}\"`);\n    }\n\n    throw new Error(`unhandled return type \"${this.returnType}\"`);\n  }\n\n  getKernelString() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultTexture() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Integer':\n      case 'Number':\n        return this.getMainResultNumberTexture();\n      case 'Array(2)':\n        return this.getMainResultArray2Texture();\n      case 'Array(3)':\n        return this.getMainResultArray3Texture();\n      case 'Array(4)':\n        return this.getMainResultArray4Texture();\n      default:\n        throw new Error(`unhandled returnType type ${ this.returnType }`);\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultGraphical() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultMemoryOptimizedFloats() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultPackedPixels() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultString() {\n    if (this.graphical) {\n      return this.getMainResultGraphical();\n    } else if (this.precision === 'single') {\n      if (this.optimizeFloatMemory) {\n        return this.getMainResultMemoryOptimizedFloats();\n      }\n      return this.getMainResultTexture();\n    } else {\n      return this.getMainResultPackedPixels();\n    }\n  }\n\n  getMainResultNumberTexture() {\n    return utils.linesToString(this.getMainResultKernelNumberTexture()) +\n      utils.linesToString(this.getMainResultSubKernelNumberTexture());\n  }\n\n  getMainResultArray2Texture() {\n    return utils.linesToString(this.getMainResultKernelArray2Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray2Texture());\n  }\n\n  getMainResultArray3Texture() {\n    return utils.linesToString(this.getMainResultKernelArray3Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray3Texture());\n  }\n\n  getMainResultArray4Texture() {\n    return utils.linesToString(this.getMainResultKernelArray4Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray4Texture());\n  }\n\n  getFloatTacticDeclaration() {\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    return `precision ${variablePrecision} float;\\n`;\n  }\n\n  getIntTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic, true)} int;\\n`;\n  }\n\n  getSampler2DTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2D;\\n`;\n  }\n\n  getSampler2DArrayTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2DArray;\\n`;\n  }\n\n  renderTexture() {\n    return this.immutable ? this.texture.clone() : this.texture;\n  }\n  readPackedPixelsToUint8Array() {\n    if (this.precision !== 'unsigned') throw new Error('Requires this.precision to be \"unsigned\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const result = new Uint8Array(texSize[0] * texSize[1] * 4);\n    gl.readPixels(0, 0, texSize[0], texSize[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n\n  readPackedPixelsToFloat32Array() {\n    return new Float32Array(this.readPackedPixelsToUint8Array().buffer);\n  }\n\n  readFloatPixelsToFloat32Array() {\n    if (this.precision !== 'single') throw new Error('Requires this.precision to be \"single\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const w = texSize[0];\n    const h = texSize[1];\n    const result = new Float32Array(w * h * 4);\n    gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n\n  getPixels(flip) {\n    const {\n      context: gl,\n      output\n    } = this;\n    const [width, height] = output;\n    const pixels = new Uint8Array(width * height * 4);\n    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n    return new Uint8ClampedArray((flip ? pixels : utils.flipPixels(pixels, width, height)).buffer);\n  }\n\n  renderKernelsToArrays() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    for (let i = 0; i < this.subKernels.length; i++) {\n      result[this.subKernels[i].property] = this.mappedTextures[i].toArray();\n    }\n    return result;\n  }\n\n  renderKernelsToTextures() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    if (this.immutable) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i].clone();\n      }\n    } else {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i];\n      }\n    }\n    return result;\n  }\n\n  resetSwitchingKernels() {\n    const existingValue = this.switchingKernels;\n    this.switchingKernels = null;\n    return existingValue;\n  }\n\n  setOutput(output) {\n    const newOutput = this.toKernelOutput(output);\n    if (this.program) {\n      if (!this.dynamicOutput) {\n        throw new Error('Resizing a kernel with dynamicOutput: false is not possible');\n      }\n      const newThreadDim = [newOutput[0], newOutput[1] || 1, newOutput[2] || 1];\n      const newTexSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, newThreadDim);\n      const oldTexSize = this.texSize;\n      if (oldTexSize) {\n        const oldPrecision = this.getVariablePrecisionString(oldTexSize, this.tactic);\n        const newPrecision = this.getVariablePrecisionString(newTexSize, this.tactic);\n        if (oldPrecision !== newPrecision) {\n          if (this.debug) {\n            console.warn('Precision requirement changed, asking GPU instance to recompile');\n          }\n          this.switchKernels({\n            type: 'outputPrecisionMismatch',\n            precision: newPrecision,\n            needed: output\n          });\n          return;\n        }\n      }\n      this.output = newOutput;\n      this.threadDim = newThreadDim;\n      this.texSize = newTexSize;\n      const { context: gl } = this;\n      gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n      this.updateMaxTexSize();\n      this.framebuffer.width = this.texSize[0];\n      this.framebuffer.height = this.texSize[1];\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      this.canvas.width = this.maxTexSize[0];\n      this.canvas.height = this.maxTexSize[1];\n      if (this.texture) {\n        this.texture.delete();\n      }\n      this.texture = null;\n      this._setupOutputTexture();\n      if (this.mappedTextures && this.mappedTextures.length > 0) {\n        for (let i = 0; i < this.mappedTextures.length; i++) {\n          this.mappedTextures[i].delete();\n        }\n        this.mappedTextures = null;\n        this._setupSubOutputTextures();\n      }\n    } else {\n      this.output = newOutput;\n    }\n    return this;\n  }\n  renderValues() {\n    return this.formatValues(\n      this.transferValues(),\n      this.output[0],\n      this.output[1],\n      this.output[2]\n    );\n  }\n  switchKernels(reason) {\n    if (this.switchingKernels) {\n      this.switchingKernels.push(reason);\n    } else {\n      this.switchingKernels = [reason];\n    }\n  }\n  getVariablePrecisionString(textureSize = this.texSize, tactic = this.tactic, isInt = false) {\n    if (!tactic) {\n      if (!this.constructor.features.isSpeedTacticSupported) return 'highp';\n      const low = this.constructor.features[isInt ? 'lowIntPrecision' : 'lowFloatPrecision'];\n      const medium = this.constructor.features[isInt ? 'mediumIntPrecision' : 'mediumFloatPrecision'];\n      const high = this.constructor.features[isInt ? 'highIntPrecision' : 'highFloatPrecision'];\n      const requiredSize = Math.log2(textureSize[0] * textureSize[1]);\n      if (requiredSize <= low.rangeMax) {\n        return 'lowp';\n      } else if (requiredSize <= medium.rangeMax) {\n        return 'mediump';\n      } else if (requiredSize <= high.rangeMax) {\n        return 'highp';\n      } else {\n        throw new Error(`The required size exceeds that of the ability of your system`);\n      }\n    }\n    switch (tactic) {\n      case 'speed':\n        return 'lowp';\n      case 'balanced':\n        return 'mediump';\n      case 'precision':\n        return 'highp';\n      default:\n        throw new Error(`Unknown tactic \"${tactic}\" use \"speed\", \"balanced\", \"precision\", or empty for auto`);\n    }\n  }\n\n  updateTextureArgumentRefs(kernelValue, arg) {\n    if (!this.immutable) return;\n    if (this.texture.texture === arg.texture) {\n      const { prevArg } = kernelValue;\n      if (prevArg) {\n        if (prevArg.texture._refs === 1) {\n          this.texture.delete();\n          this.texture = prevArg.clone();\n          this._textureSwitched = true;\n        }\n        prevArg.delete();\n      }\n      kernelValue.prevArg = arg.clone();\n    } else if (this.mappedTextures && this.mappedTextures.length > 0) {\n      const { mappedTextures } = this;\n      for (let i = 0; i < mappedTextures.length; i++) {\n        const mappedTexture = mappedTextures[i];\n        if (mappedTexture.texture === arg.texture) {\n          const { prevArg } = kernelValue;\n          if (prevArg) {\n            if (prevArg.texture._refs === 1) {\n              mappedTexture.delete();\n              mappedTextures[i] = prevArg.clone();\n              this._mappedTextureSwitched[i] = true;\n            }\n            prevArg.delete();\n          }\n          kernelValue.prevArg = arg.clone();\n          return;\n        }\n      }\n    }\n  }\n\n  onActivate(previousKernel) {\n    this._textureSwitched = true;\n    this.texture = previousKernel.texture;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        this._mappedTextureSwitched[i] = true;\n      }\n      this.mappedTextures = previousKernel.mappedTextures;\n    }\n  }\n\n  initCanvas() {}\n}\n\nconst typeMap = {\n  int: 'Integer',\n  float: 'Number',\n  vec2: 'Array(2)',\n  vec3: 'Array(3)',\n  vec4: 'Array(4)',\n};\n\nmodule.exports = {\n  GLKernel\n};\n},{\"../../utils\":113,\"../kernel\":35,\"./texture/array-2-float\":15,\"./texture/array-2-float-2d\":13,\"./texture/array-2-float-3d\":14,\"./texture/array-3-float\":18,\"./texture/array-3-float-2d\":16,\"./texture/array-3-float-3d\":17,\"./texture/array-4-float\":21,\"./texture/array-4-float-2d\":19,\"./texture/array-4-float-3d\":20,\"./texture/float\":24,\"./texture/float-2d\":22,\"./texture/float-3d\":23,\"./texture/graphical\":25,\"./texture/memory-optimized\":29,\"./texture/memory-optimized-2d\":27,\"./texture/memory-optimized-3d\":28,\"./texture/unsigned\":32,\"./texture/unsigned-2d\":30,\"./texture/unsigned-3d\":31}],13:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect2DArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float2D\n};\n},{\"../../../utils\":113,\"./float\":24}],14:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect3DArray2(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float3D\n};\n},{\"../../../utils\":113,\"./float\":24}],15:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erectArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float\n};\n},{\"../../../utils\":113,\"./float\":24}],16:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect2DArray3(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float2D\n};\n},{\"../../../utils\":113,\"./float\":24}],17:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect3DArray3(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float3D\n};\n},{\"../../../utils\":113,\"./float\":24}],18:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erectArray3(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float\n};\n},{\"../../../utils\":113,\"./float\":24}],19:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect2DArray4(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float2D\n};\n},{\"../../../utils\":113,\"./float\":24}],20:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect3DArray4(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float3D\n};\n},{\"../../../utils\":113,\"./float\":24}],21:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erectArray4(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float\n};\n},{\"../../../utils\":113,\"./float\":24}],22:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat2D\n};\n},{\"../../../utils\":113,\"./float\":24}],23:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat3D\n};\n},{\"../../../utils\":113,\"./float\":24}],24:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureFloat extends GLTexture {\n  get textureType() {\n    return this.context.FLOAT;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  renderRawOutput() {\n    const gl = this.context;\n    const size = this.size;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Float32Array(size[0] * size[1] * 4);\n    gl.readPixels(0, 0, size[0], size[1], gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return this.renderRawOutput();\n  }\n  toArray() {\n    return utils.erectFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat\n};\n},{\"../../../utils\":113,\"./index\":26}],25:[function(require,module,exports){\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureGraphical extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return this.renderValues();\n  }\n}\n\nmodule.exports = {\n  GLTextureGraphical\n};\n},{\"./unsigned\":32}],26:[function(require,module,exports){\nconst { Texture } = require('../../../texture');\n\nclass GLTexture extends Texture {\n  get textureType() {\n    throw new Error(`\"textureType\" not implemented on ${ this.name }`);\n  }\n\n  clone() {\n    return new this.constructor(this);\n  }\n\n  beforeMutate() {\n    if (this.texture._refs > 1) {\n      this.newTexture();\n      return true;\n    }\n    return false;\n  }\n\n  cloneTexture() {\n    this.texture._refs--;\n    const { context: gl, size, texture, kernel } = this;\n    if (kernel.debug) {\n      console.warn('cloning internal texture');\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, size[0], size[1]);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  newTexture() {\n    this.texture._refs--;\n    const gl = this.context;\n    const size = this.size;\n    const kernel = this.kernel;\n    if (kernel.debug) {\n      console.warn('new internal texture');\n    }\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  clear() {\n    if (this.texture._refs) {\n      this.texture._refs--;\n      const gl = this.context;\n      const target = this.texture = gl.createTexture();\n      selectTexture(gl, target);\n      const size = this.size;\n      target._refs = 1;\n      gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    }\n    const { context: gl, texture } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    gl.clearColor(0, 0, 0, 0);\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n  }\n\n  delete() {\n    if (this._deleted) return;\n    this._deleted = true;\n    if (this.texture._refs) {\n      this.texture._refs--;\n      if (this.texture._refs) return;\n    }\n    this.context.deleteTexture(this.texture);\n  }\n\n  framebuffer() {\n    if (!this._framebuffer) {\n      this._framebuffer = this.kernel.getRawValueFramebuffer(this.size[0], this.size[1]);\n    }\n    return this._framebuffer;\n  }\n}\n\nfunction selectTexture(gl, texture) {\n  gl.activeTexture(gl.TEXTURE15);\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n}\n\nmodule.exports = { GLTexture };\n},{\"../../../texture\":112}],27:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized2D\n};\n},{\"../../../utils\":113,\"./float\":24}],28:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized3D\n};\n},{\"../../../utils\":113,\"./float\":24}],29:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimizedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized\n};\n},{\"../../../utils\":113,\"./float\":24}],30:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned2D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect2DPackedFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned2D\n};\n},{\"../../../utils\":113,\"./unsigned\":32}],31:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned3D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect3DPackedFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned3D\n};\n},{\"../../../utils\":113,\"./unsigned\":32}],32:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureUnsigned extends GLTexture {\n  get textureType() {\n    return this.context.UNSIGNED_BYTE;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  renderRawOutput() {\n    const { context: gl } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Uint8Array(this.size[0] * this.size[1] * 4);\n    gl.readPixels(0, 0, this.size[0], this.size[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return new Float32Array(this.renderRawOutput().buffer);\n  }\n  toArray() {\n    return utils.erectPackedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned\n};\n},{\"../../../utils\":113,\"./index\":26}],33:[function(require,module,exports){\nconst getContext = require('gl');\nconst { WebGLKernel } = require('../web-gl/kernel');\nconst { glKernelString } = require('../gl/kernel-string');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nclass HeadlessGLKernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) return isSupported;\n    this.setupFeatureChecks();\n    isSupported = testContext !== null;\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    testCanvas = null;\n    testExtensions = null;\n    if (typeof getContext !== 'function') return;\n    try { \n      testContext = getContext(2, 2, {\n        preserveDrawingBuffer: true\n      });\n      if (!testContext || !testContext.getExtension) return;\n      testExtensions = {\n        STACKGL_resize_drawingbuffer: testContext.getExtension('STACKGL_resize_drawingbuffer'),\n        STACKGL_destroy_context: testContext.getExtension('STACKGL_destroy_context'),\n        OES_texture_float: testContext.getExtension('OES_texture_float'),\n        OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n        OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n        WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n        WEBGL_color_buffer_float: testContext.getExtension('WEBGL_color_buffer_float'),\n      };\n      features = this.getFeatures();\n    } catch (e) {\n      console.warn(e);\n    }\n  }\n\n  static isContextMatch(context) {\n    try {\n      return context.getParameter(context.RENDERER) === 'ANGLE';\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  initCanvas() {\n    return {};\n  }\n\n  initContext() {\n    return getContext(2, 2, {\n      preserveDrawingBuffer: true\n    });\n  }\n\n  initExtensions() {\n    this.extensions = {\n      STACKGL_resize_drawingbuffer: this.context.getExtension('STACKGL_resize_drawingbuffer'),\n      STACKGL_destroy_context: this.context.getExtension('STACKGL_destroy_context'),\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n    };\n  }\n\n  build() {\n    super.build.apply(this, arguments);\n    if (!this.fallbackRequested) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n  }\n\n  destroyExtensions() {\n    this.extensions.STACKGL_resize_drawingbuffer = null;\n    this.extensions.STACKGL_destroy_context = null;\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('STACKGL_destroy_context');\n    if (extension && extension.destroy) {\n      extension.destroy();\n    }\n  }\n\n  toString() {\n    const setupContextString = `const gl = context || require('gl')(1, 1);\\n`;\n    const destroyContextString = `    if (!context) { gl.getExtension('STACKGL_destroy_context').destroy(); }\\n`;\n    return glKernelString(this.constructor, arguments, this, setupContextString, destroyContextString);\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    if (this.graphical && this.extensions.STACKGL_resize_drawingbuffer) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n    return this;\n  }\n}\n\nmodule.exports = {\n  HeadlessGLKernel\n};\n},{\"../gl/kernel-string\":11,\"../web-gl/kernel\":69,\"gl\":1}],34:[function(require,module,exports){\nclass KernelValue {\n  constructor(value, settings) {\n    const {\n      name,\n      kernel,\n      context,\n      checkContext,\n      onRequestContextHandle,\n      onUpdateValueMismatch,\n      origin,\n      strictIntegers,\n      type,\n      tactic,\n    } = settings;\n    if (!name) {\n      throw new Error('name not set');\n    }\n    if (!type) {\n      throw new Error('type not set');\n    }\n    if (!origin) {\n      throw new Error('origin not set');\n    }\n    if (origin !== 'user' && origin !== 'constants') {\n      throw new Error(`origin must be \"user\" or \"constants\" value is \"${ origin }\"`);\n    }\n    if (!onRequestContextHandle) {\n      throw new Error('onRequestContextHandle is not set');\n    }\n    this.name = name;\n    this.origin = origin;\n    this.tactic = tactic;\n    this.varName = origin === 'constants' ? `constants.${name}` : name;\n    this.kernel = kernel;\n    this.strictIntegers = strictIntegers;\n    this.type = value.type || type;\n    this.size = value.size || null;\n    this.index = null;\n    this.context = context;\n    this.checkContext = checkContext !== null && checkContext !== undefined ? checkContext : true;\n    this.contextHandle = null;\n    this.onRequestContextHandle = onRequestContextHandle;\n    this.onUpdateValueMismatch = onUpdateValueMismatch;\n    this.forceUploadEachRun = null;\n  }\n\n  get id() {\n    return `${this.origin}_${name}`;\n  }\n\n  getSource() {\n    throw new Error(`\"getSource\" not defined on ${ this.constructor.name }`);\n  }\n\n  updateValue(value) {\n    throw new Error(`\"updateValue\" not defined on ${ this.constructor.name }`);\n  }\n}\n\nmodule.exports = {\n  KernelValue\n};\n},{}],35:[function(require,module,exports){\nconst { utils } = require('../utils');\nconst { Input } = require('../input');\n\nclass Kernel {\n  static get isSupported() {\n    throw new Error(`\"isSupported\" not implemented on ${ this.name }`);\n  }\n\n  static isContextMatch(context) {\n    throw new Error(`\"isContextMatch\" not implemented on ${ this.name }`);\n  }\n\n  static getFeatures() {\n    throw new Error(`\"getFeatures\" not implemented on ${ this.name }`);\n  }\n\n  static destroyContext(context) {\n    throw new Error(`\"destroyContext\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionArguments() {\n    throw new Error(`\"nativeFunctionArguments\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`\"nativeFunctionReturnType\" called on ${ this.name }`);\n  }\n\n  static combineKernels() {\n    throw new Error(`\"combineKernels\" called on ${ this.name }`);\n  }\n\n  constructor(source, settings) {\n    if (typeof source !== 'object') {\n      if (typeof source !== 'string') {\n        throw new Error('source not a string');\n      }\n      if (!utils.isFunctionString(source)) {\n        throw new Error('source not a function string');\n      }\n    }\n    this.useLegacyEncoder = false;\n    this.fallbackRequested = false;\n    this.onRequestFallback = null;\n\n    this.argumentNames = typeof source === 'string' ? utils.getArgumentNamesFromString(source) : null;\n    this.argumentTypes = null;\n    this.argumentSizes = null;\n    this.argumentBitRatios = null;\n    this.kernelArguments = null;\n    this.kernelConstants = null;\n    this.forceUploadKernelConstants = null;\n\n\n    this.source = source;\n\n    this.output = null;\n\n    this.debug = false;\n\n    this.graphical = false;\n\n    this.loopMaxIterations = 0;\n\n    this.constants = null;\n\n    this.constantTypes = null;\n\n    this.constantBitRatios = null;\n\n    this.dynamicArguments = false;\n\n    this.dynamicOutput = false;\n\n    this.canvas = null;\n\n    this.context = null;\n\n    this.checkContext = null;\n\n    this.gpu = null;\n\n    this.functions = null;\n\n    this.nativeFunctions = null;\n\n    this.injectedNative = null;\n\n    this.subKernels = null;\n\n    this.validate = true;\n\n    this.immutable = false;\n\n    this.pipeline = false;\n\n    this.precision = null;\n\n    this.tactic = null;\n\n    this.plugins = null;\n\n    this.returnType = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.optimizeFloatMemory = null;\n    this.strictIntegers = false;\n    this.fixIntegerDivisionAccuracy = null;\n    this.built = false;\n    this.signature = null;\n  }\n\n  mergeSettings(settings) {\n    for (let p in settings) {\n      if (!settings.hasOwnProperty(p) || !this.hasOwnProperty(p)) continue;\n      switch (p) {\n        case 'output':\n          if (!Array.isArray(settings.output)) {\n            this.setOutput(settings.output); \n            continue;\n          }\n          break;\n        case 'functions':\n          this.functions = [];\n          for (let i = 0; i < settings.functions.length; i++) {\n            this.addFunction(settings.functions[i]);\n          }\n          continue;\n        case 'graphical':\n          if (settings[p] && !settings.hasOwnProperty('precision')) {\n            this.precision = 'unsigned';\n          }\n          this[p] = settings[p];\n          continue;\n        case 'nativeFunctions':\n          if (!settings.nativeFunctions) continue;\n          this.nativeFunctions = [];\n          for (let i = 0; i < settings.nativeFunctions.length; i++) {\n            const s = settings.nativeFunctions[i];\n            const { name, source } = s;\n            this.addNativeFunction(name, source, s);\n          }\n          continue;\n      }\n      this[p] = settings[p];\n    }\n\n    if (!this.canvas) this.canvas = this.initCanvas();\n    if (!this.context) this.context = this.initContext();\n    if (!this.plugins) this.plugins = this.initPlugins(settings);\n  }\n  build() {\n    throw new Error(`\"build\" not defined on ${ this.constructor.name }`);\n  }\n\n  run() {\n    throw new Error(`\"run\" not defined on ${ this.constructor.name }`)\n  }\n\n  initCanvas() {\n    throw new Error(`\"initCanvas\" not defined on ${ this.constructor.name }`);\n  }\n\n  initContext() {\n    throw new Error(`\"initContext\" not defined on ${ this.constructor.name }`);\n  }\n\n  initPlugins(settings) {\n    throw new Error(`\"initPlugins\" not defined on ${ this.constructor.name }`);\n  }\n\n  addFunction(source, settings = {}) {\n    if (source.name && source.source && source.argumentTypes && 'returnType' in source) {\n      this.functions.push(source);\n    } else if ('settings' in source && 'source' in source) {\n      this.functions.push(this.functionToIGPUFunction(source.source, source.settings));\n    } else if (typeof source === 'string' || typeof source === 'function') {\n      this.functions.push(this.functionToIGPUFunction(source, settings));\n    } else {\n      throw new Error(`function not properly defined`);\n    }\n    return this;\n  }\n\n  addNativeFunction(name, source, settings = {}) {\n    const { argumentTypes, argumentNames } = settings.argumentTypes ?\n      splitArgumentTypes(settings.argumentTypes) :\n      this.constructor.nativeFunctionArguments(source) || {};\n    this.nativeFunctions.push({\n      name,\n      source,\n      settings,\n      argumentTypes,\n      argumentNames,\n      returnType: settings.returnType || this.constructor.nativeFunctionReturnType(source)\n    });\n    return this;\n  }\n\n  setupArguments(args) {\n    this.kernelArguments = [];\n    if (!this.argumentTypes) {\n      if (!this.argumentTypes) {\n        this.argumentTypes = [];\n        for (let i = 0; i < args.length; i++) {\n          const argType = utils.getVariableType(args[i], this.strictIntegers);\n          const type = argType === 'Integer' ? 'Number' : argType;\n          this.argumentTypes.push(type);\n          this.kernelArguments.push({\n            type\n          });\n        }\n      }\n    } else {\n      for (let i = 0; i < this.argumentTypes.length; i++) {\n        this.kernelArguments.push({\n          type: this.argumentTypes[i]\n        });\n      }\n    }\n\n    this.argumentSizes = new Array(args.length);\n    this.argumentBitRatios = new Int32Array(args.length);\n\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      this.argumentSizes[i] = arg.constructor === Input ? arg.size : null;\n      this.argumentBitRatios[i] = this.getBitRatio(arg);\n    }\n\n    if (this.argumentNames.length !== args.length) {\n      throw new Error(`arguments are miss-aligned`);\n    }\n  }\n\n  setupConstants() {\n    this.kernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    if (this.constants) {\n      for (let name in this.constants) {\n        if (needsConstantTypes) {\n          const type = utils.getVariableType(this.constants[name], this.strictIntegers);\n          this.constantTypes[name] = type;\n          this.kernelConstants.push({\n            name,\n            type\n          });\n        } else {\n          this.kernelConstants.push({\n            name,\n            type: this.constantTypes[name]\n          });\n        }\n        this.constantBitRatios[name] = this.getBitRatio(this.constants[name]);\n      }\n    }\n  }\n\n  setOptimizeFloatMemory(flag) {\n    this.optimizeFloatMemory = flag;\n    return this;\n  }\n\n  toKernelOutput(output) {\n    if (output.hasOwnProperty('x')) {\n      if (output.hasOwnProperty('y')) {\n        if (output.hasOwnProperty('z')) {\n          return [output.x, output.y, output.z];\n        } else {\n          return [output.x, output.y];\n        }\n      } else {\n        return [output.x];\n      }\n    } else {\n      return output;\n    }\n  }\n\n  setOutput(output) {\n    this.output = this.toKernelOutput(output);\n    return this;\n  }\n\n  setDebug(flag) {\n    this.debug = flag;\n    return this;\n  }\n\n  setGraphical(flag) {\n    this.graphical = flag;\n    this.precision = 'unsigned';\n    return this;\n  }\n\n  setLoopMaxIterations(max) {\n    this.loopMaxIterations = max;\n    return this;\n  }\n\n  setConstants(constants) {\n    this.constants = constants;\n    return this;\n  }\n\n  setConstantTypes(constantTypes) {\n    this.constantTypes = constantTypes;\n    return this;\n  }\n\n  setFunctions(functions) {\n    for (let i = 0; i < functions.length; i++) {\n      this.addFunction(functions[i]);\n    }\n    return this;\n  }\n\n  setNativeFunctions(nativeFunctions) {\n    for (let i = 0; i < nativeFunctions.length; i++) {\n      const settings = nativeFunctions[i];\n      const { name, source } = settings;\n      this.addNativeFunction(name, source, settings);\n    }\n    return this;\n  }\n\n  setInjectedNative(injectedNative) {\n    this.injectedNative = injectedNative;\n    return this;\n  }\n\n  setPipeline(flag) {\n    this.pipeline = flag;\n    return this;\n  }\n\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  setDimensions(flag) {\n    utils.warnDeprecated('method', 'setDimensions', 'setOutput');\n    this.output = flag;\n    return this;\n  }\n\n  setOutputToTexture(flag) {\n    utils.warnDeprecated('method', 'setOutputToTexture', 'setPipeline');\n    this.pipeline = flag;\n    return this;\n  }\n\n  setImmutable(flag) {\n    this.immutable = flag;\n    return this;\n  }\n\n  setCanvas(canvas) {\n    this.canvas = canvas;\n    return this;\n  }\n\n  setStrictIntegers(flag) {\n    this.strictIntegers = flag;\n    return this;\n  }\n\n  setDynamicOutput(flag) {\n    this.dynamicOutput = flag;\n    return this;\n  }\n\n  setHardcodeConstants(flag) {\n    utils.warnDeprecated('method', 'setHardcodeConstants');\n    this.setDynamicOutput(flag);\n    this.setDynamicArguments(flag);\n    return this;\n  }\n\n  setDynamicArguments(flag) {\n    this.dynamicArguments = flag;\n    return this;\n  }\n\n  setUseLegacyEncoder(flag) {\n    this.useLegacyEncoder = flag;\n    return this;\n  }\n\n  setWarnVarUsage(flag) {\n    utils.warnDeprecated('method', 'setWarnVarUsage');\n    return this;\n  }\n\n  getCanvas() {\n    utils.warnDeprecated('method', 'getCanvas');\n    return this.canvas;\n  }\n\n  getWebGl() {\n    utils.warnDeprecated('method', 'getWebGl');\n    return this.context;\n  }\n\n  setContext(context) {\n    this.context = context;\n    return this;\n  }\n\n  setArgumentTypes(argumentTypes) {\n    if (Array.isArray(argumentTypes)) {\n      this.argumentTypes = argumentTypes;\n    } else {\n      this.argumentTypes = [];\n      for (const p in argumentTypes) {\n        if (!argumentTypes.hasOwnProperty(p)) continue;\n        const argumentIndex = this.argumentNames.indexOf(p);\n        if (argumentIndex === -1) throw new Error(`unable to find argument ${ p }`);\n        this.argumentTypes[argumentIndex] = argumentTypes[p];\n      }\n    }\n    return this;\n  }\n\n  setTactic(tactic) {\n    this.tactic = tactic;\n    return this;\n  }\n\n  requestFallback(args) {\n    if (!this.onRequestFallback) {\n      throw new Error(`\"onRequestFallback\" not defined on ${ this.constructor.name }`);\n    }\n    this.fallbackRequested = true;\n    return this.onRequestFallback(args);\n  }\n\n  validateSettings() {\n    throw new Error(`\"validateSettings\" not defined on ${ this.constructor.name }`);\n  }\n\n  addSubKernel(subKernel) {\n    if (this.subKernels === null) {\n      this.subKernels = [];\n    }\n    if (!subKernel.source) throw new Error('subKernel missing \"source\" property');\n    if (!subKernel.property && isNaN(subKernel.property)) throw new Error('subKernel missing \"property\" property');\n    if (!subKernel.name) throw new Error('subKernel missing \"name\" property');\n    this.subKernels.push(subKernel);\n    return this;\n  }\n\n  destroy(removeCanvasReferences) {\n    throw new Error(`\"destroy\" called on ${ this.constructor.name }`);\n  }\n\n  getBitRatio(value) {\n    if (this.precision === 'single') {\n      return 4;\n    } else if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  getPixels(flip) {\n    throw new Error(`\"getPixels\" called on ${ this.constructor.name }`);\n  }\n\n  checkOutput() {\n    if (!this.output || !utils.isArray(this.output)) throw new Error('kernel.output not an array');\n    if (this.output.length < 1) throw new Error('kernel.output is empty, needs at least 1 value');\n    for (let i = 0; i < this.output.length; i++) {\n      if (isNaN(this.output[i]) || this.output[i] < 1) {\n        throw new Error(`${ this.constructor.name }.output[${ i }] incorrectly defined as \\`${ this.output[i] }\\`, needs to be numeric, and greater than 0`);\n      }\n    }\n  }\n\n  prependString(value) {\n    throw new Error(`\"prependString\" called on ${ this.constructor.name }`);\n  }\n\n  hasPrependString(value) {\n    throw new Error(`\"hasPrependString\" called on ${ this.constructor.name }`);\n  }\n\n  toJSON() {\n    return {\n      settings: {\n        output: this.output,\n        pipeline: this.pipeline,\n        argumentNames: this.argumentNames,\n        argumentsTypes: this.argumentTypes,\n        constants: this.constants,\n        pluginNames: this.plugins ? this.plugins.map(plugin => plugin.name) : null,\n        returnType: this.returnType,\n      }\n    };\n  }\n\n  buildSignature(args) {\n    const Constructor = this.constructor;\n    this.signature = Constructor.getSignature(this, Constructor.getArgumentTypes(this, args));\n  }\n\n  static getArgumentTypes(kernel, args) {\n    const argumentTypes = new Array(args.length);\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      const type = kernel.argumentTypes[i];\n      if (arg.type) {\n        argumentTypes[i] = arg.type;\n      } else {\n        switch (type) {\n          case 'Number':\n          case 'Integer':\n          case 'Float':\n          case 'ArrayTexture(1)':\n            argumentTypes[i] = utils.getVariableType(arg);\n            break;\n          default:\n            argumentTypes[i] = type;\n        }\n      }\n    }\n    return argumentTypes;\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    throw new Error(`\"getSignature\" not implemented on ${ this.name }`);\n  }\n\n  functionToIGPUFunction(source, settings = {}) {\n    if (typeof source !== 'string' && typeof source !== 'function') throw new Error('source not a string or function');\n    const sourceString = typeof source === 'string' ? source : source.toString();\n    let argumentTypes = [];\n\n    if (Array.isArray(settings.argumentTypes)) {\n      argumentTypes = settings.argumentTypes;\n    } else if (typeof settings.argumentTypes === 'object') {\n      argumentTypes = utils.getArgumentNamesFromString(sourceString)\n        .map(name => settings.argumentTypes[name]) || [];\n    } else {\n      argumentTypes = settings.argumentTypes || [];\n    }\n\n    return {\n      name: utils.getFunctionNameFromString(sourceString) || null,\n      source: sourceString,\n      argumentTypes,\n      returnType: settings.returnType || null,\n    };\n  }\n\n  onActivate(previousKernel) {}\n}\n\nfunction splitArgumentTypes(argumentTypesObject) {\n  const argumentNames = Object.keys(argumentTypesObject);\n  const argumentTypes = [];\n  for (let i = 0; i < argumentNames.length; i++) {\n    const argumentName = argumentNames[i];\n    argumentTypes.push(argumentTypesObject[argumentName]);\n  }\n  return { argumentTypes, argumentNames };\n}\n\nmodule.exports = {\n  Kernel\n};\n},{\"../input\":109,\"../utils\":113}],36:[function(require,module,exports){\nconst fragmentShader = `__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nvarying vec2 vTexCoord;\n\nfloat acosh(float x) {\n  return log(x + sqrt(x * x - 1.0));\n}\n\nfloat sinh(float x) {\n  return (pow(${Math.E}, x) - pow(${Math.E}, -x)) / 2.0;\n}\n\nfloat asinh(float x) {\n  return log(x + sqrt(x * x + 1.0));\n}\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat atanh(float x) {\n  x = (x + 1.0) / (x - 1.0);\n  if (x < 0.0) {\n    return 0.5 * log(-x);\n  }\n  return 0.5 * log(x);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat cosh(float x) {\n  return (pow(${Math.E}, x) + pow(${Math.E}, -x)) / 2.0; \n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat tanh(float x) {\n  float e = exp(2.0 * x);\n  return (e - 1.0) / (e + 1.0);\n}\n\nfloat trunc(float x) {\n  if (x >= 0.0) {\n    return floor(x); \n  } else {\n    return ceil(x);\n  }\n}\n\nvec4 _round(vec4 x) {\n  return floor(x + 0.5);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x / y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(_round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(_round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  if (channel == 0) return texel.r * 255.0 + texel.g * 65280.0;\n  if (channel == 1) return texel.b * 255.0 + texel.a * 65280.0;\n  return 0.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  if (channel == 0) return texel.r * 255.0;\n  if (channel == 1) return texel.g * 255.0;\n  if (channel == 2) return texel.b * 255.0;\n  if (channel == 3) return texel.a * 255.0;\n  return 0.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return texel.r;\n  if (channel == 1) return texel.g;\n  if (channel == 2) return texel.b;\n  if (channel == 3) return texel.a;\n  return 0.0;\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture2D(tex, st / vec2(texSize));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture2D(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n  \n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture2D(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nvoid color(sampler2D image) {\n  actualColor = texture2D(image, vTexCoord);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};\n},{}],37:[function(require,module,exports){\nconst { utils } = require('../../utils');\nconst { FunctionNode } = require('../function-node');\n\nclass WebGLFunctionNode extends FunctionNode {\n  constructor(source, settings) {\n    super(source, settings);\n    if (settings && settings.hasOwnProperty('fixIntegerDivisionAccuracy')) {\n      this.fixIntegerDivisionAccuracy = settings.fixIntegerDivisionAccuracy;\n    }\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    const consequentType = this.getType(ast.consequent);\n    const alternateType = this.getType(ast.alternate);\n    if (consequentType === null && alternateType === null) {\n      retArr.push('if (');\n      this.astGeneric(ast.test, retArr);\n      retArr.push(') {');\n      this.astGeneric(ast.consequent, retArr);\n      retArr.push(';');\n      retArr.push('} else {');\n      this.astGeneric(ast.alternate, retArr);\n      retArr.push(';');\n      retArr.push('}');\n      return retArr;\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astFunction(ast, retArr) {\n    if (this.isRootKernel) {\n      retArr.push('void');\n    } else {\n      if (!this.returnType) {\n        const lastReturn = this.findLastReturn();\n        if (lastReturn) {\n          this.returnType = this.getType(ast.body);\n          if (this.returnType === 'LiteralInteger') {\n            this.returnType = 'Number';\n          }\n        }\n      }\n\n      const { returnType } = this;\n      if (!returnType) {\n        retArr.push('void');\n      } else {\n        const type = typeMap[returnType];\n        if (!type) {\n          throw new Error(`unknown type ${returnType}`);\n        }\n        retArr.push(type);\n      }\n    }\n    retArr.push(' ');\n    retArr.push(this.name);\n    retArr.push('(');\n\n    if (!this.isRootKernel) {\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        let argumentType = this.argumentTypes[this.argumentNames.indexOf(argumentName)];\n        if (!argumentType) {\n          throw this.astErrorOutput(`Unknown argument ${argumentName} type`, ast);\n        }\n        if (argumentType === 'LiteralInteger') {\n          this.argumentTypes[i] = argumentType = 'Number';\n        }\n        const type = typeMap[argumentType];\n        if (!type) {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        const name = utils.sanitizeName(argumentName);\n        if (type === 'sampler2D' || type === 'sampler2DArray') {\n          retArr.push(`${type} user_${name},ivec2 user_${name}Size,ivec3 user_${name}Dim`);\n        } else {\n          retArr.push(`${type} user_${name}`);\n        }\n      }\n    }\n\n    retArr.push(') {\\n');\n\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    retArr.push('}\\n');\n    return retArr;\n  }\n\n  astReturnStatement(ast, retArr) {\n    if (!ast.argument) throw this.astErrorOutput('Unexpected return statement', ast);\n    this.pushState('skip-literal-correction');\n    const type = this.getType(ast.argument);\n    this.popState('skip-literal-correction');\n\n    const result = [];\n\n    if (!this.returnType) {\n      if (type === 'LiteralInteger' || type === 'Integer') {\n        this.returnType = 'Number';\n      } else {\n        this.returnType = type;\n      }\n    }\n\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Float':\n        switch (type) {\n          case 'Integer':\n            result.push('float(');\n            this.astGeneric(ast.argument, result);\n            result.push(')');\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToFloat(ast.argument, result);\n\n            if (this.getType(ast) === 'Integer') {\n              result.unshift('float(');\n              result.push(')');\n            }\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Integer':\n        switch (type) {\n          case 'Float':\n          case 'Number':\n            this.castValueToInteger(ast.argument, result);\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToInteger(ast.argument, result);\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Array(4)':\n      case 'Array(3)':\n      case 'Array(2)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'Input':\n        this.astGeneric(ast.argument, result);\n        break;\n      default:\n        throw this.astErrorOutput(`unhandled return type ${this.returnType}`, ast);\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(`kernelResult = ${ result.join('') };`);\n      retArr.push('return;');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = ${ result.join('') };`);\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push(`return ${ result.join('') };`);\n    }\n    return retArr;\n  }\n\n  astLiteral(ast, retArr) {\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    const key = this.astKey(ast);\n    if (Number.isInteger(ast.value)) {\n      if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n        this.literalTypes[key] = 'Integer';\n        retArr.push(`${ast.value}`);\n      } else if (this.isState('casting-to-float') || this.isState('building-float')) {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      } else {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      }\n    } else if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n      this.literalTypes[key] = 'Integer';\n      retArr.push(Math.round(ast.value));\n    } else {\n      this.literalTypes[key] = 'Number';\n      retArr.push(`${ast.value}`);\n    }\n    return retArr;\n  }\n\n  astBinaryExpression(ast, retArr) {\n    if (this.checkAndUpconvertOperator(ast, retArr)) {\n      return retArr;\n    }\n\n    if (this.fixIntegerDivisionAccuracy && ast.operator === '/') {\n      retArr.push('divWithIntCheck(');\n      this.pushState('building-float');\n      switch (this.getType(ast.left)) {\n        case 'Integer':\n          this.castValueToFloat(ast.left, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.left, retArr);\n          break;\n        default:\n          this.astGeneric(ast.left, retArr);\n      }\n      retArr.push(', ');\n      switch (this.getType(ast.right)) {\n        case 'Integer':\n          this.castValueToFloat(ast.right, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.right, retArr);\n          break;\n        default:\n          this.astGeneric(ast.right, retArr);\n      }\n      this.popState('building-float');\n      retArr.push(')');\n      return retArr;\n    }\n\n    retArr.push('(');\n    const leftType = this.getType(ast.left) || 'Number';\n    const rightType = this.getType(ast.right) || 'Number';\n    if (!leftType || !rightType) {\n      throw this.astErrorOutput(`Unhandled binary expression`, ast);\n    }\n    const key = leftType + ' & ' + rightType;\n    switch (key) {\n      case 'Integer & Integer':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n      case 'Number & Float':\n      case 'Float & Number':\n      case 'Float & Float':\n      case 'Number & Number':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & LiteralInteger':\n        if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n          this.pushState('building-integer');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.astGeneric(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.castLiteralToFloat(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castLiteralToFloat(ast.right, retArr);\n          this.popState('building-float');\n        }\n        break;\n\n      case 'Integer & Float':\n      case 'Integer & Number':\n        if (ast.operator === '>' || ast.operator === '<' && ast.right.type === 'Literal') {\n          if (!Number.isInteger(ast.right.value)) {\n            this.pushState('building-float');\n            this.castValueToFloat(ast.left, retArr);\n            retArr.push(operatorMap[ast.operator] || ast.operator);\n            this.astGeneric(ast.right, retArr);\n            this.popState('building-float');\n            break;\n          }\n        }\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.pushState('casting-to-integer');\n        if (ast.right.type === 'Literal') {\n          const literalResult = [];\n          this.astGeneric(ast.right, literalResult);\n          const literalType = this.getType(ast.right);\n          if (literalType === 'Integer') {\n            retArr.push(literalResult.join(''));\n          } else {\n            throw this.astErrorOutput(`Unhandled binary expression with literal`, ast);\n          }\n        } else {\n          retArr.push('int(');\n          this.astGeneric(ast.right, retArr);\n          retArr.push(')');\n        }\n        this.popState('casting-to-integer');\n        this.popState('building-integer');\n        break;\n      case 'Integer & LiteralInteger':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToInteger(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Number & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'Float & LiteralInteger':\n      case 'Number & LiteralInteger':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & Float':\n      case 'LiteralInteger & Number':\n        if (this.isState('casting-to-integer')) {\n          this.pushState('building-integer');\n          this.castLiteralToInteger(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castValueToInteger(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.pushState('casting-to-float');\n          this.astGeneric(ast.right, retArr);\n          this.popState('casting-to-float');\n          this.popState('building-float');\n        }\n        break;\n      case 'LiteralInteger & Integer':\n        this.pushState('building-integer');\n        this.castLiteralToInteger(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Boolean & Boolean':\n        this.pushState('building-boolean');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-boolean');\n        break;\n\n      case 'Float & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n\n      default:\n        throw this.astErrorOutput(`Unhandled binary expression between ${key}`, ast);\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  checkAndUpconvertOperator(ast, retArr) {\n    const bitwiseResult = this.checkAndUpconvertBitwiseOperators(ast, retArr);\n    if (bitwiseResult) {\n      return bitwiseResult;\n    }\n    const upconvertableOperators = {\n      '%': this.fixIntegerDivisionAccuracy ? 'integerCorrectionModulo' : 'modulo',\n      '**': 'pow',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.left)) {\n      case 'Integer':\n        this.castValueToFloat(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    switch (this.getType(ast.right)) {\n      case 'Integer':\n        this.castValueToFloat(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseOperators(ast, retArr) {\n    const upconvertableOperators = {\n      '&': 'bitwiseAnd',\n      '|': 'bitwiseOr',\n      '^': 'bitwiseXOR',\n      '<<': 'bitwiseZeroFillLeftShift',\n      '>>': 'bitwiseSignedRightShift',\n      '>>>': 'bitwiseZeroFillRightShift',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    const leftType = this.getType(ast.left);\n    switch (leftType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    const rightType = this.getType(ast.right);\n    switch (rightType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(ast, retArr) {\n    const upconvertableOperators = {\n      '~': 'bitwiseNot',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.argument)) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.argument, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.argument, retArr);\n        break;\n      default:\n        this.astGeneric(ast.argument, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  castLiteralToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  castLiteralToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  castValueToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    retArr.push('int(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  castValueToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    retArr.push('float(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput('IdentifierExpression - not an Identifier', idtNode);\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      retArr.push('3.402823466e+38');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      const { declarations } = forNode.init;\n      if (declarations.length > 1) {\n        isSafe = false;\n      }\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < declarations.length; i++) {\n        if (declarations[i].init && declarations[i].init.type !== 'Literal') {\n          isSafe = false;\n        }\n      }\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      const initString = initArr.join('');\n      const initNeedsSemiColon = initString[initString.length - 1] !== ';';\n      retArr.push(`for (${initString}${initNeedsSemiColon ? ';' : ''}${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), '\\n');\n      }\n      retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', whileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    retArr.push('if (!');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') break;\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', doWhileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') break;\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n\n  astAssignmentExpression(assNode, retArr) {\n    if (assNode.operator === '%=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('mod(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else if (assNode.operator === '**=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('pow(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else {\n      const leftType = this.getType(assNode.left);\n      const rightType = this.getType(assNode.right);\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(assNode.operator);\n      if (leftType !== 'Integer' && rightType === 'Integer') {\n        retArr.push('float(');\n        this.astGeneric(assNode.right, retArr);\n        retArr.push(')');\n      } else {\n        this.astGeneric(assNode.right, retArr);\n      }\n      return retArr;\n    }\n  }\n\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); \n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astVariableDeclaration(varDecNode, retArr) {\n    const declarations = varDecNode.declarations;\n    if (!declarations || !declarations[0] || !declarations[0].init) {\n      throw this.astErrorOutput('Unexpected expression', varDecNode);\n    }\n    const result = [];\n    let lastType = null;\n    const declarationSets = [];\n    let declarationSet = [];\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const init = declaration.init;\n      const info = this.getDeclaration(declaration.id);\n      const actualType = this.getType(declaration.init);\n      let type = actualType;\n      if (type === 'LiteralInteger') {\n        if (info.suggestedType === 'Integer') {\n          type = 'Integer';\n        } else {\n          type = 'Number';\n        }\n      }\n      const markupType = typeMap[type];\n      if (!markupType) {\n        throw this.astErrorOutput(`Markup type ${ type } not handled`, varDecNode);\n      }\n      const declarationResult = [];\n      if (actualType === 'Integer' && type === 'Integer') {\n        info.valueType = 'Number';\n        if (i === 0 || lastType === null) {\n          declarationResult.push('float ');\n        } else if (type !== lastType) {\n          throw new Error('Unhandled declaration');\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        declarationResult.push('float(');\n        this.astGeneric(init, declarationResult);\n        declarationResult.push(')');\n      } else {\n        info.valueType = type;\n        if (i === 0 || lastType === null) {\n          declarationResult.push(`${markupType} `);\n        } else if (type !== lastType) {\n          declarationSets.push(declarationSet.join(','));\n          declarationSet = [];\n          declarationResult.push(`${markupType} `);\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        if (actualType === 'Number' && type === 'Integer') {\n          if (init.left && init.left.type === 'Literal') {\n            this.astGeneric(init, declarationResult);\n          } else {\n            declarationResult.push('int(');\n            this.astGeneric(init, declarationResult);\n            declarationResult.push(')');\n          }\n        } else if (actualType === 'LiteralInteger' && type === 'Integer') {\n          this.castLiteralToInteger(init, declarationResult);\n        } else {\n          this.astGeneric(init, declarationResult);\n        }\n      }\n      declarationSet.push(declarationResult.join(''));\n    }\n\n    if (declarationSet.length > 0) {\n      declarationSets.push(declarationSet.join(','));\n    }\n\n    result.push(declarationSets.join(';'));\n\n    retArr.push(result.join(''));\n    retArr.push(';');\n    return retArr;\n  }\n\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n  }\n\n  astSwitchStatement(ast, retArr) {\n    if (ast.type !== 'SwitchStatement') {\n      throw this.astErrorOutput('Invalid switch statement', ast);\n    }\n    const { discriminant, cases } = ast;\n    const type = this.getType(discriminant);\n    const varName = `switchDiscriminant${this.astKey(ast, '_')}`;\n    switch (type) {\n      case 'Float':\n      case 'Number':\n        retArr.push(`float ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n      case 'Integer':\n        retArr.push(`int ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n    }\n    if (cases.length === 1 && !cases[0].test) {\n      this.astGeneric(cases[0].consequent, retArr);\n      return retArr;\n    }\n\n    let fallingThrough = false;\n    let defaultResult = [];\n    let movingDefaultToEnd = false;\n    let pastFirstIf = false;\n    for (let i = 0; i < cases.length; i++) {\n      if (!cases[i].test) {\n        if (cases.length > i + 1) {\n          movingDefaultToEnd = true;\n          this.astGeneric(cases[i].consequent, defaultResult);\n          continue;\n        } else {\n          retArr.push(' else {\\n');\n        }\n      } else {\n        if (i === 0 || !pastFirstIf) {\n          pastFirstIf = true;\n          retArr.push(`if (${varName} == `);\n        } else {\n          if (fallingThrough) {\n            retArr.push(`${varName} == `);\n            fallingThrough = false;\n          } else {\n            retArr.push(` else if (${varName} == `);\n          }\n        }\n        if (type === 'Integer') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'Number':\n            case 'Float':\n              this.castValueToInteger(cases[i].test, retArr);\n              break;\n            case 'LiteralInteger':\n              this.castLiteralToInteger(cases[i].test, retArr);\n              break;\n          }\n        } else if (type === 'Float') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'LiteralInteger':\n              this.castLiteralToFloat(cases[i].test, retArr);\n              break;\n            case 'Integer':\n              this.castValueToFloat(cases[i].test, retArr);\n              break;\n          }\n        } else {\n          throw new Error('unhanlded');\n        }\n        if (!cases[i].consequent || cases[i].consequent.length === 0) {\n          fallingThrough = true;\n          retArr.push(' || ');\n          continue;\n        }\n        retArr.push(`) {\\n`);\n      }\n      this.astGeneric(cases[i].consequent, retArr);\n      retArr.push('\\n}');\n    }\n    if (movingDefaultToEnd) {\n      retArr.push(' else {');\n      retArr.push(defaultResult.join(''));\n      retArr.push('}');\n    }\n    return retArr;\n  }\n\n  astThisExpression(tNode, retArr) {\n    retArr.push('this');\n    return retArr;\n  }\n\n  astMemberExpression(mNode, retArr) {\n    const {\n      property,\n      name,\n      signature,\n      origin,\n      type,\n      xProperty,\n      yProperty,\n      zProperty\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'value.thread.value':\n      case 'this.thread.value':\n        if (name !== 'x' && name !== 'y' && name !== 'z') {\n          throw this.astErrorOutput('Unexpected expression, expected `this.thread.x`, `this.thread.y`, or `this.thread.z`', mNode);\n        }\n        retArr.push(`threadId.${name}`);\n        return retArr;\n      case 'this.output.value':\n        if (this.dynamicOutput) {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.x)');\n              } else {\n                retArr.push('uOutputDim.x');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.y)');\n              } else {\n                retArr.push('uOutputDim.y');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.z)');\n              } else {\n                retArr.push('uOutputDim.z');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        } else {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[0]);\n              } else {\n                retArr.push(this.output[0], '.0');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[1]);\n              } else {\n                retArr.push(this.output[1], '.0');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[2]);\n              } else {\n                retArr.push(this.output[2], '.0');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value[][][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        const cleanName = utils.sanitizeName(name);\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ cleanName }.r`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ cleanName }.g`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ cleanName }.b`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ cleanName }.a`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n        if (typeof xProperty === 'undefined') {\n          switch (type) {\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              retArr.push(`constants_${ utils.sanitizeName(name) }`);\n              return retArr;\n          }\n        }\n        case 'this.constants.value[]':\n        case 'this.constants.value[][]':\n        case 'this.constants.value[][][]':\n        case 'this.constants.value[][][][]':\n          break;\n        case 'fn()[]':\n          this.astCallExpression(mNode.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(property));\n          retArr.push(']');\n          return retArr;\n        case 'fn()[][]':\n          this.astCallExpression(mNode.object.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(mNode.object.property));\n          retArr.push(']');\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(mNode.property));\n          retArr.push(']');\n          return retArr;\n        case '[][]':\n          this.astArrayExpression(mNode.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(property));\n          retArr.push(']');\n          return retArr;\n        default:\n          throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (mNode.computed === false) {\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${utils.sanitizeName(name)}`);\n          return retArr;\n      }\n    }\n\n    const markupName = `${origin}_${utils.sanitizeName(name)}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(xProperty));\n        retArr.push(']');\n        break;\n      case 'HTMLImageArray':\n        retArr.push(`getImage3D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(1)':\n        retArr.push(`getFloatFromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(2)':\n      case 'Array2D(2)':\n      case 'Array3D(2)':\n        retArr.push(`getMemoryOptimizedVec2(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(2)':\n        retArr.push(`getVec2FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(3)':\n      case 'Array2D(3)':\n      case 'Array3D(3)':\n        retArr.push(`getMemoryOptimizedVec3(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(3)':\n        retArr.push(`getVec3FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(4)':\n      case 'Array2D(4)':\n      case 'Array3D(4)':\n        retArr.push(`getMemoryOptimizedVec4(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(4)':\n      case 'HTMLCanvas':\n      case 'OffscreenCanvas':\n      case 'HTMLImage':\n      case 'ImageBitmap':\n      case 'ImageData':\n      case 'HTMLVideo':\n        retArr.push(`getVec4FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'NumberTexture':\n      case 'Array':\n      case 'Array2D':\n      case 'Array3D':\n      case 'Array4D':\n      case 'Input':\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        if (this.precision === 'single') {\n          retArr.push(`getMemoryOptimized32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        } else {\n          const bitRatio = (origin === 'user' ?\n            this.lookupFunctionArgumentBitRatio(this.name, name) :\n            this.constantBitRatios[name]\n          );\n          switch (bitRatio) {\n            case 1:\n              retArr.push(`get8(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 2:\n              retArr.push(`get16(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 4:\n            case 0:\n              retArr.push(`get32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            default:\n              throw new Error(`unhandled bit ratio of ${bitRatio}`);\n          }\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        }\n        break;\n      case 'MemoryOptimizedNumberTexture':\n        retArr.push(`getMemoryOptimized32(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`${markupName}[${this.memberExpressionPropertyMarkup(yProperty)}]`);\n        if (yProperty) {\n          retArr.push(`[${this.memberExpressionPropertyMarkup(xProperty)}]`);\n        }\n        break;\n      default:\n        throw new Error(`unhandled member expression \"${ type }\"`);\n    }\n    return retArr;\n  }\n\n  astCallExpression(ast, retArr) {\n    if (!ast.callee) {\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n\n    let functionName = null;\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    if (isMathFunction || (ast.callee.object && ast.callee.object.type === 'ThisExpression')) {\n      functionName = ast.callee.property.name;\n    }\n    else if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[0].type === 'Literal' && !isNaN(ast.callee.expressions[0].raw)) {\n      functionName = ast.callee.expressions[1].property.name;\n    } else {\n      functionName = ast.callee.name;\n    }\n\n    if (!functionName) {\n      throw this.astErrorOutput(`Unhandled function, couldn't find name`, ast);\n    }\n\n    switch (functionName) {\n      case 'pow':\n        functionName = '_pow';\n        break;\n      case 'round':\n        functionName = '_round';\n        break;\n    }\n\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    if (functionName === 'random' && this.plugins && this.plugins.length > 0) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.functionMatch === 'Math.random()' && plugin.functionReplace) {\n          retArr.push(plugin.functionReplace);\n          return retArr;\n        }\n      }\n    }\n\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    retArr.push(functionName);\n\n    retArr.push('(');\n\n    if (isMathFunction) {\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        const argumentType = this.getType(argument);\n        if (i > 0) {\n          retArr.push(', ');\n        }\n\n        switch (argumentType) {\n          case 'Integer':\n            this.castValueToFloat(argument, retArr);\n            break;\n          default:\n            this.astGeneric(argument, retArr);\n            break;\n        }\n      }\n    } else {\n      const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        let targetType = targetTypes[i];\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        const argumentType = this.getType(argument);\n        if (!targetType) {\n          this.triggerImplyArgumentType(functionName, i, argumentType, this);\n          targetType = argumentType;\n        }\n        switch (argumentType) {\n          case 'Boolean':\n            this.astGeneric(argument, retArr);\n            continue;\n          case 'Number':\n          case 'Float':\n            if (targetType === 'Integer') {\n              retArr.push('int(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.astGeneric(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Integer':\n            if (targetType === 'Number' || targetType === 'Float') {\n              retArr.push('float(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Integer') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'LiteralInteger':\n            if (targetType === 'Integer') {\n              this.castLiteralToInteger(argument, retArr);\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            if (targetType === argumentType) {\n              if (argument.type === 'Identifier') {\n                retArr.push(`user_${utils.sanitizeName(argument.name)}`);\n              } else if (argument.type === 'ArrayExpression' || argument.type === 'MemberExpression' || argument.type === 'CallExpression') {\n                this.astGeneric(argument, retArr);\n              } else {\n                throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              }\n              continue;\n            }\n            break;\n          case 'HTMLCanvas':\n          case 'OffscreenCanvas':\n          case 'HTMLImage':\n          case 'ImageBitmap':\n          case 'ImageData':\n          case 'HTMLImageArray':\n          case 'HTMLVideo':\n          case 'ArrayTexture(1)':\n          case 'ArrayTexture(2)':\n          case 'ArrayTexture(3)':\n          case 'ArrayTexture(4)':\n          case 'Array':\n          case 'Input':\n            if (targetType === argumentType) {\n              if (argument.type !== 'Identifier') throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              this.triggerImplyArgumentBitRatio(this.name, argument.name, functionName, i);\n              const name = utils.sanitizeName(argument.name);\n              retArr.push(`user_${name},user_${name}Size,user_${name}Dim`);\n              continue;\n            }\n            break;\n        }\n        throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named \"${ argument.name }\"`, ast);\n      }\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n\n    const arrLen = arrNode.elements.length;\n\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`mat${arrLen}(`);\n        break;\n      default:\n        retArr.push(`vec${arrLen}(`);\n    }\n    for (let i = 0; i < arrLen; ++i) {\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      const subNode = arrNode.elements[i];\n      this.astGeneric(subNode, retArr)\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  memberExpressionXYZ(x, y, z, retArr) {\n    if (z) {\n      retArr.push(this.memberExpressionPropertyMarkup(z), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    if (y) {\n      retArr.push(this.memberExpressionPropertyMarkup(y), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    retArr.push(this.memberExpressionPropertyMarkup(x));\n    return retArr;\n  }\n\n  memberExpressionPropertyMarkup(property) {\n    if (!property) {\n      throw new Error('Property not set');\n    }\n    const type = this.getType(property);\n    const result = [];\n    switch (type) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(property, result);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(property, result);\n        break;\n      default:\n        this.astGeneric(property, result);\n    }\n    return result.join('');\n  }\n}\n\nconst typeMap = {\n  'Array': 'sampler2D',\n  'Array(2)': 'vec2',\n  'Array(3)': 'vec3',\n  'Array(4)': 'vec4',\n  'Matrix(2)': 'mat2',\n  'Matrix(3)': 'mat3',\n  'Matrix(4)': 'mat4',\n  'Array2D': 'sampler2D',\n  'Array3D': 'sampler2D',\n  'Boolean': 'bool',\n  'Float': 'float',\n  'Input': 'sampler2D',\n  'Integer': 'int',\n  'Number': 'float',\n  'LiteralInteger': 'float',\n  'NumberTexture': 'sampler2D',\n  'MemoryOptimizedNumberTexture': 'sampler2D',\n  'ArrayTexture(1)': 'sampler2D',\n  'ArrayTexture(2)': 'sampler2D',\n  'ArrayTexture(3)': 'sampler2D',\n  'ArrayTexture(4)': 'sampler2D',\n  'HTMLVideo': 'sampler2D',\n  'HTMLCanvas': 'sampler2D',\n  'OffscreenCanvas': 'sampler2D',\n  'HTMLImage': 'sampler2D',\n  'ImageBitmap': 'sampler2D',\n  'ImageData': 'sampler2D',\n  'HTMLImageArray': 'sampler2DArray',\n};\n\nconst operatorMap = {\n  '===': '==',\n  '!==': '!='\n};\n\nmodule.exports = {\n  WebGLFunctionNode\n};\n},{\"../../utils\":113,\"../function-node\":9}],38:[function(require,module,exports){\nconst { WebGLKernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGLKernelValueFloat } = require('./kernel-value/float');\nconst { WebGLKernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGLKernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGLKernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGLKernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGLKernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGLKernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGLKernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGLKernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGLKernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGLKernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGLKernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGLKernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGLKernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGLKernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGLKernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGLKernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGLKernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGLKernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGLKernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGLKernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGLKernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGLKernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueUnsignedInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueDynamicSingleArray3DI,\n      'Input': WebGLKernelValueDynamicSingleInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueSingleArray3DI,\n      'Input': WebGLKernelValueSingleInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  lookupKernelValueType,\n  kernelValueMaps,\n};\n},{\"./kernel-value/array2\":40,\"./kernel-value/array3\":41,\"./kernel-value/array4\":42,\"./kernel-value/boolean\":43,\"./kernel-value/dynamic-html-image\":44,\"./kernel-value/dynamic-html-video\":45,\"./kernel-value/dynamic-memory-optimized-number-texture\":46,\"./kernel-value/dynamic-number-texture\":47,\"./kernel-value/dynamic-single-array\":48,\"./kernel-value/dynamic-single-array1d-i\":49,\"./kernel-value/dynamic-single-array2d-i\":50,\"./kernel-value/dynamic-single-array3d-i\":51,\"./kernel-value/dynamic-single-input\":52,\"./kernel-value/dynamic-unsigned-array\":53,\"./kernel-value/dynamic-unsigned-input\":54,\"./kernel-value/float\":55,\"./kernel-value/html-image\":56,\"./kernel-value/html-video\":57,\"./kernel-value/integer\":59,\"./kernel-value/memory-optimized-number-texture\":60,\"./kernel-value/number-texture\":61,\"./kernel-value/single-array\":62,\"./kernel-value/single-array1d-i\":63,\"./kernel-value/single-array2d-i\":64,\"./kernel-value/single-array3d-i\":65,\"./kernel-value/single-input\":66,\"./kernel-value/unsigned-array\":67,\"./kernel-value/unsigned-input\":68}],39:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\nconst { Input } = require('../../../input');\n\nclass WebGLKernelArray extends WebGLKernelValue {\n  checkSize(width, height) {\n    if (!this.kernel.validate) return;\n    const { maxTextureSize } = this.kernel.constructor.features;\n    if (width > maxTextureSize || height > maxTextureSize) {\n      if (width > height) {\n        throw new Error(`Argument texture width of ${width} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else if (width < height) {\n        throw new Error(`Argument texture height of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else {\n        throw new Error(`Argument texture height and width of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      }\n    }\n  }\n\n  setup() {\n    this.requestTexture();\n    this.setupTexture();\n    this.defineTexture();\n  }\n\n  requestTexture() {\n    this.texture = this.onRequestTexture();\n  }\n\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n  }\n\n  setupTexture() {\n    this.contextHandle = this.onRequestContextHandle();\n    this.index = this.onRequestIndex();\n    this.dimensionsId = this.id + 'Dim';\n    this.sizeId = this.id + 'Size';\n  }\n\n  getBitRatio(value) {\n    if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  destroy() {\n    if (this.prevArg) {\n      this.prevArg.delete();\n    }\n    this.context.deleteTexture(this.texture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelArray\n};\n},{\"../../../input\":109,\"./index\":58}],40:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray2 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec2 ${this.id} = vec2(${value[0]},${value[1]});\\n`;\n    }\n    return `uniform vec2 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform2fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray2\n};\n},{\"./index\":58}],41:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray3 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec3 ${this.id} = vec3(${value[0]},${value[1]},${value[2]});\\n`;\n    }\n    return `uniform vec3 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform3fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray3\n};\n},{\"./index\":58}],42:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray4 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec4 ${this.id} = vec4(${value[0]},${value[1]},${value[2]},${value[3]});\\n`;\n    }\n    return `uniform vec4 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform4fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray4\n};\n},{\"./index\":58}],43:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueBoolean extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const bool ${this.id} = ${value};\\n`;\n    }\n    return `uniform bool ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueBoolean\n};\n},{\"../../../utils\":113,\"./index\":58}],44:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueDynamicHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLImage\n};\n},{\"../../../utils\":113,\"./html-image\":56}],45:[function(require,module,exports){\nconst { WebGLKernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGLKernelValueDynamicHTMLVideo extends WebGLKernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLVideo\n};\n},{\"./dynamic-html-image\":44}],46:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    this.dimensions = inputTexture.dimensions;\n    this.checkSize(inputTexture.size[0], inputTexture.size[1]);\n    this.textureSize = inputTexture.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(inputTexture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":113,\"./memory-optimized-number-texture\":60}],47:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('./number-texture');\n\nclass WebGLKernelValueDynamicNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = value.dimensions;\n    this.checkSize(value.size[0], value.size[1]);\n    this.textureSize = value.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicNumberTexture\n};\n},{\"../../../utils\":113,\"./number-texture\":61}],48:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('./single-array');\n\nclass WebGLKernelValueDynamicSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray\n};\n},{\"../../../utils\":113,\"./single-array\":62}],49:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('./single-array1d-i');\n\nclass WebGLKernelValueDynamicSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray1DI\n};\n},{\"../../../utils\":113,\"./single-array1d-i\":63}],50:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('./single-array2d-i');\n\nclass WebGLKernelValueDynamicSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray2DI\n};\n},{\"../../../utils\":113,\"./single-array2d-i\":64}],51:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('./single-array3d-i');\n\nclass WebGLKernelValueDynamicSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray3DI\n};\n},{\"../../../utils\":113,\"./single-array3d-i\":65}],52:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('./single-input');\n\nclass WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleInput\n};\n},{\"../../../utils\":113,\"./single-input\":66}],53:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('./unsigned-array');\n\nclass WebGLKernelValueDynamicUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedArray\n};\n},{\"../../../utils\":113,\"./unsigned-array\":67}],54:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('./unsigned-input');\n\nclass WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value.value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedInput\n};\n},{\"../../../utils\":113,\"./unsigned-input\":68}],55:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueFloat extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      if (Number.isInteger(value)) {\n        return `const float ${this.id} = ${value}.0;\\n`;\n      }\n      return `const float ${this.id} = ${value};\\n`;\n    }\n    return `uniform float ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1f(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueFloat\n};\n},{\"../../../utils\":113,\"./index\":58}],56:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueHTMLImage extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.uploadValue = value;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputImage) {\n    if (inputImage.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputImage.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue = inputImage);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueHTMLImage\n};\n},{\"../../../utils\":113,\"./array\":39}],57:[function(require,module,exports){\nconst { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueHTMLVideo extends WebGLKernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueHTMLVideo\n};\n},{\"./html-image\":56}],58:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { KernelValue } = require('../../kernel-value');\n\nclass WebGLKernelValue extends KernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.dimensionsId = null;\n    this.sizeId = null;\n    this.initialValueConstructor = value.constructor;\n    this.onRequestTexture = settings.onRequestTexture;\n    this.onRequestIndex = settings.onRequestIndex;\n    this.uploadValue = null;\n    this.textureSize = null;\n    this.bitRatio = null;\n    this.prevArg = null;\n  }\n\n  get id() {\n    return `${this.origin}_${utils.sanitizeName(this.name)}`;\n  }\n\n  setup() {}\n\n  getTransferArrayType(value) {\n    if (Array.isArray(value[0])) {\n      return this.getTransferArrayType(value[0]);\n    }\n    switch (value.constructor) {\n      case Array:\n      case Int32Array:\n      case Int16Array:\n      case Int8Array:\n        return Float32Array;\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Uint16Array:\n      case Uint32Array:\n      case Float32Array:\n      case Float64Array:\n        return value.constructor;\n    }\n    console.warn('Unfamiliar constructor type.  Will go ahead and use, but likley this may result in a transfer of zeros');\n    return value.constructor;\n  }\n\n  getStringValueHandler() {\n    throw new Error(`\"getStringValueHandler\" not implemented on ${this.constructor.name}`);\n  }\n\n  getVariablePrecisionString() {\n    return this.kernel.getVariablePrecisionString(this.textureSize || undefined, this.tactic || undefined);\n  }\n\n  destroy() {}\n}\n\nmodule.exports = {\n  WebGLKernelValue\n};\n},{\"../../../utils\":113,\"../../kernel-value\":34}],59:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueInteger extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueInteger\n};\n},{\"../../../utils\":113,\"./index\":58}],60:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nconst sameError = `Source and destination textures are the same.  Use immutable = true and manually cleanup kernel output texture memory with texture.delete()`;\n\nclass WebGLKernelValueMemoryOptimizedNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    this.dimensions = value.dimensions;\n    this.textureSize = value.size;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueMemoryOptimizedNumberTexture,\n  sameError\n};\n},{\"../../../utils\":113,\"./array\":39}],61:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\nconst { sameError } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    const { size: textureSize, dimensions } = value;\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = dimensions;\n    this.textureSize = textureSize;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueNumberTexture\n};\n},{\"../../../utils\":113,\"./array\":39,\"./memory-optimized-number-texture\":60}],62:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray\n};\n},{\"../../../utils\":113,\"./array\":39}],63:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray1DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], 1, 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten2dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray1DI\n};\n},{\"../../../utils\":113,\"./array\":39}],64:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray2DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten3dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray2DI\n};\n},{\"../../../utils\":113,\"./array\":39}],65:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray3DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], valueDimensions[3]]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten4dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray3DI\n};\n},{\"../../../utils\":113,\"./array\":39}],66:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}.value, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(input.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleInput\n};\n},{\"../../../utils\":113,\"./array\":39}],67:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedArray\n};\n},{\"../../../utils\":113,\"./array\":39}],68:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    const [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value.value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}.value, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedInput\n};\n},{\"../../../utils\":113,\"./array\":39}],69:[function(require,module,exports){\nconst { GLKernel } = require('../gl/kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { WebGLFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst mrud = require('../../plugins/math-random-uniformly-distributed');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { glKernelString } = require('../gl/kernel-string');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nconst plugins = [mrud];\nconst canvases = [];\nconst maxTexSizes = {};\n\n\nclass WebGLKernel extends GLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      OES_texture_float: testContext.getExtension('OES_texture_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    if (typeof WebGLRenderingContext !== 'undefined') {\n      return context instanceof WebGLRenderingContext;\n    }\n    return false;\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.program = null;\n    this.pipeline = settings.pipeline;\n    this.endianness = utils.systemEndianness();\n    this.extensions = {};\n    this.argumentTextureCount = 0;\n    this.constantTextureCount = 0;\n    this.fragShader = null;\n    this.vertShader = null;\n    this.drawBuffersMap = null;\n\n    this.maxTexSize = null;\n    this.onRequestSwitchKernel = null;\n\n    this.texture = null;\n    this.mappedTextures = null;\n    this.mergeSettings(source.settings || settings);\n\n    this.threadDim = null;\n    this.framebuffer = null;\n    this.buffer = null;\n\n    this.textureCache = [];\n    this.programUniformLocationCache = {};\n    this.uniform1fCache = {};\n    this.uniform1iCache = {};\n    this.uniform2fCache = {};\n    this.uniform2fvCache = {};\n    this.uniform2ivCache = {};\n    this.uniform3fvCache = {};\n    this.uniform3ivCache = {};\n    this.uniform4fvCache = {};\n    this.uniform4ivCache = {};\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      const canvas = document.createElement('canvas');\n      canvas.width = 2;\n      canvas.height = 2;\n      return canvas;\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl', settings) || this.canvas.getContext('experimental-webgl', settings);\n  }\n\n  initPlugins(settings) {\n    const pluginsToUse = [];\n    const { source } = this;\n    if (typeof source === 'string') {\n      for (let i = 0; i < plugins.length; i++) {\n        const plugin = plugins[i];\n        if (source.match(plugin.functionMatch)) {\n          pluginsToUse.push(plugin);\n        }\n      }\n    } else if (typeof source === 'object') {\n      if (settings.pluginNames) { \n        for (let i = 0; i < plugins.length; i++) {\n          const plugin = plugins[i];\n          const usePlugin = settings.pluginNames.some(pluginName => pluginName === plugin.name);\n          if (usePlugin) {\n            pluginsToUse.push(plugin);\n          }\n        }\n      }\n    }\n    return pluginsToUse;\n  }\n\n  initExtensions() {\n    this.extensions = {\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n      WEBGL_color_buffer_float: this.context.getExtension('WEBGL_color_buffer_float'),\n    };\n  }\n\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n\n    if (this.optimizeFloatMemory === true && !features.isTextureFloat) {\n      throw new Error('Float textures are not supported');\n    } else if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Single precision not supported');\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.subKernels && this.subKernels.length > 0 && !this.extensions.WEBGL_draw_buffers) {\n      throw new Error('could not instantiate draw buffers extension');\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'precision') {\n        this.precision = 'unsigned';\n        console.warn('Cannot use graphical mode and single precision at the same time');\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  updateMaxTexSize() {\n    const { texSize, canvas } = this;\n    if (this.maxTexSize === null) {\n      let canvasIndex = canvases.indexOf(canvas);\n      if (canvasIndex === -1) {\n        canvasIndex = canvases.length;\n        canvases.push(canvas);\n        maxTexSizes[canvasIndex] = [texSize[0], texSize[1]];\n      }\n      this.maxTexSize = maxTexSizes[canvasIndex];\n    }\n    if (this.maxTexSize[0] < texSize[0]) {\n      this.maxTexSize[0] = texSize[0];\n    }\n    if (this.maxTexSize[1] < texSize[1]) {\n      this.maxTexSize[1] = texSize[1];\n    }\n  }\n\n  setupArguments(args) {\n    this.kernelArguments = [];\n    this.argumentTextureCount = 0;\n    const needsArgumentTypes = this.argumentTypes === null;\n    if (needsArgumentTypes) {\n      this.argumentTypes = [];\n    }\n    this.argumentSizes = [];\n    this.argumentBitRatios = [];\n\n    if (args.length < this.argumentNames.length) {\n      throw new Error('not enough arguments for kernel');\n    } else if (args.length > this.argumentNames.length) {\n      throw new Error('too many arguments for kernel');\n    }\n\n    const { context: gl } = this;\n    let textureIndexes = 0;\n\n    const onRequestTexture = () => {\n      return this.createTexture();\n    };\n    const onRequestIndex = () => {\n      return this.constantTextureCount + textureIndexes++;\n    };\n    const onUpdateValueMismatch = (constructor) => {\n      this.switchKernels({\n        type: 'argumentMismatch',\n        needed: constructor\n      });\n    };\n    const onRequestContextHandle = () => {\n      return gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount++;\n    };\n\n    for (let index = 0; index < args.length; index++) {\n      const value = args[index];\n      const name = this.argumentNames[index];\n      let type;\n      if (needsArgumentTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.argumentTypes.push(type);\n      } else {\n        type = this.argumentTypes[index];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, this.dynamicArguments ? 'dynamic' : 'static', this.precision, args[index]);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelArgument = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'user',\n        context: gl,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture,\n        onRequestIndex,\n        onUpdateValueMismatch,\n        onRequestContextHandle,\n      });\n      this.kernelArguments.push(kernelArgument);\n      kernelArgument.setup();\n      this.argumentSizes.push(kernelArgument.textureSize);\n      this.argumentBitRatios[index] = kernelArgument.bitRatio;\n    }\n  }\n\n  createTexture() {\n    const texture = this.context.createTexture();\n    this.textureCache.push(texture);\n    return texture;\n  }\n\n  setupConstants(args) {\n    const { context: gl } = this;\n    this.kernelConstants = [];\n    this.forceUploadKernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    let textureIndexes = 0;\n    for (const name in this.constants) {\n      const value = this.constants[name];\n      let type;\n      if (needsConstantTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.constantTypes[name] = type;\n      } else {\n        type = this.constantTypes[name];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, 'static', this.precision, value);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelValue = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'constants',\n        context: this.context,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture: () => {\n          return this.createTexture();\n        },\n        onRequestIndex: () => {\n          return textureIndexes++;\n        },\n        onRequestContextHandle: () => {\n          return gl.TEXTURE0 + this.constantTextureCount++;\n        }\n      });\n      this.constantBitRatios[name] = kernelValue.bitRatio;\n      this.kernelConstants.push(kernelValue);\n      kernelValue.setup();\n      if (kernelValue.forceUploadEachRun) {\n        this.forceUploadKernelConstants.push(kernelValue);\n      }\n    }\n  }\n\n  build() {\n    if (this.built) return;\n    this.initExtensions();\n    this.validateSettings(arguments);\n    this.setupConstants(arguments);\n    if (this.fallbackRequested) return;\n    this.setupArguments(arguments);\n    if (this.fallbackRequested) return;\n    this.updateMaxTexSize();\n    this.translateSource();\n    const failureResult = this.pickRenderStrategy(arguments);\n    if (failureResult) {\n      return failureResult;\n    }\n    const { texSize, context: gl, canvas } = this;\n    gl.enable(gl.SCISSOR_TEST);\n    if (this.pipeline && this.precision === 'single') {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    } else {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    }\n    const threadDim = this.threadDim = Array.from(this.output);\n    while (threadDim.length < 3) {\n      threadDim.push(1);\n    }\n\n    const compiledVertexShader = this.getVertexShader(arguments);\n    const vertShader = gl.createShader(gl.VERTEX_SHADER);\n    gl.shaderSource(vertShader, compiledVertexShader);\n    gl.compileShader(vertShader);\n    this.vertShader = vertShader;\n\n    const compiledFragmentShader = this.getFragmentShader(arguments);\n    const fragShader = gl.createShader(gl.FRAGMENT_SHADER);\n    gl.shaderSource(fragShader, compiledFragmentShader);\n    gl.compileShader(fragShader);\n    this.fragShader = fragShader;\n\n    if (this.debug) {\n      console.log('GLSL Shader Output:');\n      console.log(compiledFragmentShader);\n    }\n\n    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader));\n    }\n    if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader));\n    }\n\n    const program = this.program = gl.createProgram();\n    gl.attachShader(program, vertShader);\n    gl.attachShader(program, fragShader);\n    gl.linkProgram(program);\n    this.framebuffer = gl.createFramebuffer();\n    this.framebuffer.width = texSize[0];\n    this.framebuffer.height = texSize[1];\n    this.rawValueFramebuffers = {};\n\n    const vertices = new Float32Array([-1, -1,\n      1, -1, -1, 1,\n      1, 1\n    ]);\n    const texCoords = new Float32Array([\n      0, 0,\n      1, 0,\n      0, 1,\n      1, 1\n    ]);\n\n    const texCoordOffset = vertices.byteLength;\n\n    let buffer = this.buffer;\n    if (!buffer) {\n      buffer = this.buffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n      gl.bufferData(gl.ARRAY_BUFFER, vertices.byteLength + texCoords.byteLength, gl.STATIC_DRAW);\n    } else {\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n    }\n\n    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);\n    gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);\n\n    const aPosLoc = gl.getAttribLocation(this.program, 'aPos');\n    gl.enableVertexAttribArray(aPosLoc);\n    gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0);\n    const aTexCoordLoc = gl.getAttribLocation(this.program, 'aTexCoord');\n    gl.enableVertexAttribArray(aTexCoordLoc);\n    gl.vertexAttribPointer(aTexCoordLoc, 2, gl.FLOAT, false, 0, texCoordOffset);\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\n    let i = 0;\n    gl.useProgram(this.program);\n    for (let p in this.constants) {\n      this.kernelConstants[i++].updateValue(this.constants[p]);\n    }\n\n    this._setupOutputTexture();\n    if (\n      this.subKernels !== null &&\n      this.subKernels.length > 0\n    ) {\n      this._mappedTextureSwitched = {};\n      this._setupSubOutputTextures();\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGLFunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  setupReturnTypes(functionBuilder) {\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n\n    if (this.subKernels && this.subKernels.length > 0) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const subKernel = this.subKernels[i];\n        if (!subKernel.returnType) {\n          subKernel.returnType = functionBuilder.getSubKernelResultType(i);\n        }\n      }\n    }\n  }\n\n  run() {\n    const { kernelArguments, texSize, forceUploadKernelConstants, context: gl } = this;\n\n    gl.useProgram(this.program);\n    gl.scissor(0, 0, texSize[0], texSize[1]);\n    if (this.dynamicOutput) {\n      this.setUniform3iv('uOutputDim', new Int32Array(this.threadDim));\n      this.setUniform2iv('uTexSize', texSize);\n    }\n\n    this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);\n\n    for (let i = 0; i < forceUploadKernelConstants.length; i++) {\n      const constant = forceUploadKernelConstants[i];\n      constant.updateValue(this.constants[constant.name]);\n      if (this.switchingKernels) return;\n    }\n    for (let i = 0; i < kernelArguments.length; i++) {\n      kernelArguments[i].updateValue(arguments[i]);\n      if (this.switchingKernels) return;\n    }\n\n    if (this.plugins) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.onBeforeRun) {\n          plugin.onBeforeRun(this);\n        }\n      }\n    }\n\n    if (this.graphical) {\n      if (this.pipeline) {\n        gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n        gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n        if (this.immutable) {\n          this._replaceOutputTexture();\n        }\n        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n        return this.immutable ? this.texture.clone() : this.texture;\n      }\n      gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n      gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n      return;\n    }\n\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    if (this.immutable) {\n      this._replaceOutputTexture();\n    }\n\n    if (this.subKernels !== null) {\n      if (this.immutable) {\n        this._replaceSubOutputTextures();\n      }\n      this.drawBuffers();\n    }\n\n    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n  }\n\n  drawBuffers() {\n    this.extensions.WEBGL_draw_buffers.drawBuffersWEBGL(this.drawBuffersMap);\n  }\n\n  getInternalFormat() {\n    return this.context.RGBA;\n  }\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n\n  _replaceOutputTexture() {\n    if (this.texture.beforeMutate() || this._textureSwitched) {\n      const gl = this.context;\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      this._textureSwitched = false;\n    }\n  }\n\n  _setupOutputTexture() {\n    const gl = this.context;\n    const texSize = this.texSize;\n    if (this.texture) {\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    const texture = this.createTexture();\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  _replaceSubOutputTextures() {\n    const gl = this.context;\n    for (let i = 0; i < this.mappedTextures.length; i++) {\n      const mappedTexture = this.mappedTextures[i];\n      if (mappedTexture.beforeMutate() || this._mappedTextureSwitched[i]) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, mappedTexture.texture, 0);\n        this._mappedTextureSwitched[i] = false;\n      }\n    }\n  }\n\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      if (this.precision === 'single') {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  setUniform1f(name, value) {\n    if (this.uniform1fCache.hasOwnProperty(name)) {\n      const cache = this.uniform1fCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1fCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1f(loc, value);\n  }\n\n  setUniform1i(name, value) {\n    if (this.uniform1iCache.hasOwnProperty(name)) {\n      const cache = this.uniform1iCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1iCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1i(loc, value);\n  }\n\n  setUniform2f(name, value1, value2) {\n    if (this.uniform2fCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fCache[name];\n      if (\n        value1 === cache[0] &&\n        value2 === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fCache[name] = [value1, value2];\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2f(loc, value1, value2);\n  }\n\n  setUniform2fv(name, value) {\n    if (this.uniform2fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2fv(loc, value);\n  }\n\n  setUniform2iv(name, value) {\n    if (this.uniform2ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform2ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2iv(loc, value);\n  }\n\n  setUniform3fv(name, value) {\n    if (this.uniform3fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform3fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3fv(loc, value);\n  }\n\n  setUniform3iv(name, value) {\n    if (this.uniform3ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform3ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3iv(loc, value);\n  }\n\n  setUniform4fv(name, value) {\n    if (this.uniform4fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform4fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4fv(loc, value);\n  }\n\n  setUniform4iv(name, value) {\n    if (this.uniform4ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform4ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4iv(loc, value);\n  }\n\n  getUniformLocation(name) {\n    if (this.programUniformLocationCache.hasOwnProperty(name)) {\n      return this.programUniformLocationCache[name];\n    }\n    return this.programUniformLocationCache[name] = this.context.getUniformLocation(this.program, name);\n  }\n\n  _getFragShaderArtifactMap(args) {\n    return {\n      HEADER: this._getHeaderString(),\n      LOOP_MAX: this._getLoopMaxString(),\n      PLUGINS: this._getPluginsString(),\n      CONSTANTS: this._getConstantsString(),\n      DECODE32_ENDIANNESS: this._getDecode32EndiannessString(),\n      ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(),\n      DIVIDE_WITH_INTEGER_CHECK: this._getDivideWithIntegerCheckString(),\n      INJECTED_NATIVE: this._getInjectedNative(),\n      MAIN_CONSTANTS: this._getMainConstantsString(),\n      MAIN_ARGUMENTS: this._getMainArgumentsString(args),\n      KERNEL: this.getKernelString(),\n      MAIN_RESULT: this.getMainResultString(),\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  _getVertShaderArtifactMap(args) {\n    return {\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  _getHeaderString() {\n    return (\n      this.subKernels !== null ?\n      '#extension GL_EXT_draw_buffers : require\\n' :\n      ''\n    );\n  }\n\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${parseInt(this.loopMaxIterations)};\\n` :\n      ' 1000;\\n'\n    );\n  }\n\n  _getPluginsString() {\n    if (!this.plugins) return '\\n';\n    return this.plugins.map(plugin => plugin.source && this.source.match(plugin.functionMatch) ? plugin.source : '').join('\\n');\n  }\n\n  _getConstantsString() {\n    const result = [];\n    const { threadDim, texSize } = this;\n    if (this.dynamicOutput) {\n      result.push(\n        'uniform ivec3 uOutputDim',\n        'uniform ivec2 uTexSize'\n      );\n    } else {\n      result.push(\n        `ivec3 uOutputDim = ivec3(${threadDim[0]}, ${threadDim[1]}, ${threadDim[2]})`,\n        `ivec2 uTexSize = ivec2(${texSize[0]}, ${texSize[1]})`\n      );\n    }\n    return utils.linesToString(result);\n  }\n\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    if (subKernels === null || subKernels.length < 1) {\n      return 'varying vec2 vTexCoord;\\n';\n    } else {\n      return 'out vec2 vTexCoord;\\n';\n    }\n  }\n\n  _getDecode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  _getEncode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  _getDivideWithIntegerCheckString() {\n    return this.fixIntegerDivisionAccuracy ?\n      `float divWithIntCheck(float x, float y) {\n  if (floor(x) == x && floor(y) == y && integerMod(x, y) == 0.0) {\n    return float(int(x) / int(y));\n  }\n  return x / y;\n}\n\nfloat integerCorrectionModulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -(number - (divisor * floor(divWithIntCheck(number, divisor))));\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return number - (divisor * floor(divWithIntCheck(number, divisor)));\n}` :\n      '';\n  }\n\n  _getMainArgumentsString(args) {\n    const results = [];\n    const { argumentNames } = this;\n    for (let i = 0; i < argumentNames.length; i++) {\n      results.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return results.join('');\n  }\n\n  _getInjectedNative() {\n    return this.injectedNative || '';\n  }\n\n  _getMainConstantsString() {\n    const result = [];\n    const { constants } = this;\n    if (constants) {\n      let i = 0;\n      for (const name in constants) {\n        if (!this.constants.hasOwnProperty(name)) continue;\n        result.push(this.kernelConstants[i++].getSource(this.constants[name]));\n      }\n    }\n    return result.join('');\n  }\n\n  getRawValueFramebuffer(width, height) {\n    if (!this.rawValueFramebuffers[width]) {\n      this.rawValueFramebuffers[width] = {};\n    }\n    if (!this.rawValueFramebuffers[width][height]) {\n      const framebuffer = this.context.createFramebuffer();\n      framebuffer.width = width;\n      framebuffer.height = height;\n      this.rawValueFramebuffers[width][height] = framebuffer;\n    }\n    return this.rawValueFramebuffers[width][height];\n  }\n\n  getKernelResultDeclaration() {\n    switch (this.returnType) {\n      case 'Array(2)':\n        return 'vec2 kernelResult';\n      case 'Array(3)':\n        return 'vec3 kernelResult';\n      case 'Array(4)':\n        return 'vec4 kernelResult';\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Number':\n      case 'Integer':\n        return 'float kernelResult';\n      default:\n        if (this.graphical) {\n          return 'float kernelResult';\n        } else {\n          throw new Error(`unrecognized output type \"${ this.returnType }\"`);\n        }\n    }\n  }\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const { subKernels } = this;\n    if (subKernels !== null) {\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n      }\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragColor = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultMemoryOptimizedFloats() {\n    const result = [\n      '  index *= 4',\n    ];\n\n    switch (this.returnType) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        const channels = ['r', 'g', 'b', 'a'];\n        for (let i = 0; i < channels.length; i++) {\n          const channel = channels[i];\n          this.getMainResultKernelMemoryOptimizedFloats(result, channel);\n          this.getMainResultSubKernelMemoryOptimizedFloats(result, channel);\n          if (i + 1 < channels.length) {\n            result.push('  index += 1');\n          }\n        }\n        break;\n      default:\n        throw new Error(`optimized output only usable with Numbers, ${this.returnType} specified`);\n    }\n\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0].${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = float(subKernelResult_${this.subKernels[i].name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = subKernelResult_${this.subKernels[i].name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n      '  gl_FragData[0][2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n        `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    switch (this.returnType) {\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          const subKernel = this.subKernels[i];\n          if (subKernel.returnType === 'Integer') {\n            result.push(\n              `  gl_FragData[${i + 1}] = float(subKernelResult_${this.subKernels[i].name})`\n            );\n          } else {\n            result.push(\n              `  gl_FragData[${i + 1}] = subKernelResult_${this.subKernels[i].name}`\n            );\n          }\n        }\n        break;\n      case 'Array(2)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n          );\n        }\n        break;\n      case 'Array(3)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n          );\n        }\n        break;\n      case 'Array(4)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`,\n            `  gl_FragData[${i + 1}][3] = subKernelResult_${this.subKernels[i].name}[3]`\n          );\n        }\n        break;\n    }\n\n    return result;\n  }\n\n  replaceArtifacts(src, map) {\n    return src.replace(/[ ]*__([A-Z]+[0-9]*([_]?[A-Z]*[0-9]?)*)__;\\n/g, (match, artifact) => {\n      if (map.hasOwnProperty(artifact)) {\n        return map[artifact];\n      }\n      throw `unhandled artifact ${artifact}`;\n    });\n  }\n\n  getFragmentShader(args) {\n    if (this.compiledFragmentShader !== null) {\n      return this.compiledFragmentShader;\n    }\n    return this.compiledFragmentShader = this.replaceArtifacts(this.constructor.fragmentShader, this._getFragShaderArtifactMap(args));\n  }\n\n  getVertexShader(args) {\n    if (this.compiledVertexShader !== null) {\n      return this.compiledVertexShader;\n    }\n    return this.compiledVertexShader = this.replaceArtifacts(this.constructor.vertexShader, this._getVertShaderArtifactMap(args));\n  }\n\n  toString() {\n    const setupContextString = utils.linesToString([\n      `const gl = context`,\n    ]);\n    return glKernelString(this.constructor, arguments, this, setupContextString);\n  }\n\n  destroy(removeCanvasReferences) {\n    if (!this.context) return;\n    if (this.buffer) {\n      this.context.deleteBuffer(this.buffer);\n    }\n    if (this.framebuffer) {\n      this.context.deleteFramebuffer(this.framebuffer);\n    }\n    for (const width in this.rawValueFramebuffers) {\n      for (const height in this.rawValueFramebuffers[width]) {\n        this.context.deleteFramebuffer(this.rawValueFramebuffers[width][height]);\n        delete this.rawValueFramebuffers[width][height];\n      }\n      delete this.rawValueFramebuffers[width];\n    }\n    if (this.vertShader) {\n      this.context.deleteShader(this.vertShader);\n    }\n    if (this.fragShader) {\n      this.context.deleteShader(this.fragShader);\n    }\n    if (this.program) {\n      this.context.deleteProgram(this.program);\n    }\n    if (this.texture) {\n      this.texture.delete();\n      const textureCacheIndex = this.textureCache.indexOf(this.texture.texture);\n      if (textureCacheIndex > -1) {\n        this.textureCache.splice(textureCacheIndex, 1);\n      }\n      this.texture = null;\n    }\n    if (this.mappedTextures && this.mappedTextures.length) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        const mappedTexture = this.mappedTextures[i];\n        mappedTexture.delete();\n        const textureCacheIndex = this.textureCache.indexOf(mappedTexture.texture);\n        if (textureCacheIndex > -1) {\n          this.textureCache.splice(textureCacheIndex, 1);\n        }\n      }\n      this.mappedTextures = null;\n    }\n    if (this.kernelArguments) {\n      for (let i = 0; i < this.kernelArguments.length; i++) {\n        this.kernelArguments[i].destroy();\n      }\n    }\n    if (this.kernelConstants) {\n      for (let i = 0; i < this.kernelConstants.length; i++) {\n        this.kernelConstants[i].destroy();\n      }\n    }\n    while (this.textureCache.length > 0) {\n      const texture = this.textureCache.pop();\n      this.context.deleteTexture(texture);\n    }\n    if (removeCanvasReferences) {\n      const idx = canvases.indexOf(this.canvas);\n      if (idx >= 0) {\n        canvases[idx] = null;\n        maxTexSizes[idx] = null;\n      }\n    }\n    this.destroyExtensions();\n    delete this.context;\n    delete this.canvas;\n    if (!this.gpu) return;\n    const i = this.gpu.kernels.indexOf(this);\n    if (i === -1) return;\n    this.gpu.kernels.splice(i, 1);\n  }\n\n  destroyExtensions() {\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('WEBGL_lose_context');\n    if (extension) {\n      extension.loseContext();\n    }\n  }\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGLFunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGLKernel\n};\n},{\"../../plugins/math-random-uniformly-distributed\":111,\"../../utils\":113,\"../function-builder\":8,\"../gl/kernel\":12,\"../gl/kernel-string\":11,\"./fragment-shader\":36,\"./function-node\":37,\"./kernel-value-maps\":38,\"./vertex-shader\":70}],70:[function(require,module,exports){\nconst vertexShader = `__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nattribute vec2 aPos;\nattribute vec2 aTexCoord;\n\nvarying vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};\n},{}],71:[function(require,module,exports){\nconst fragmentShader = `#version 300 es\n__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n__SAMPLER_2D_ARRAY_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nin vec2 vTexCoord;\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x/y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  return texel[channel*2] * 255.0 + texel[channel*2 + 1] * 65280.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  return texel[channel] * 255.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  index = index / 4;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return texel[channel];\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, st / vec2(texSize));\n}\n\nvec4 getImage3D(sampler2DArray tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, vec3(st / vec2(texSize), z));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n\n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};\n},{}],72:[function(require,module,exports){\nconst { utils } = require('../../utils');\nconst { WebGLFunctionNode } = require('../web-gl/function-node');\n\nclass WebGL2FunctionNode extends WebGLFunctionNode {\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      retArr.push('intBitsToFloat(2139095039)');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  WebGL2FunctionNode\n};\n},{\"../../utils\":113,\"../web-gl/function-node\":37}],73:[function(require,module,exports){\nconst { WebGL2KernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGL2KernelValueFloat } = require('./kernel-value/float');\nconst { WebGL2KernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGL2KernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGL2KernelValueHTMLImageArray } = require('./kernel-value/html-image-array');\nconst { WebGL2KernelValueDynamicHTMLImageArray } = require('./kernel-value/dynamic-html-image-array');\n\nconst { WebGL2KernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGL2KernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGL2KernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGL2KernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGL2KernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGL2KernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGL2KernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGL2KernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGL2KernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGL2KernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGL2KernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGL2KernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGL2KernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGL2KernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGL2KernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGL2KernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGL2KernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGL2KernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGL2KernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGL2KernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGL2KernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGL2KernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGL2KernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueUnsignedInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Input': WebGL2KernelValueDynamicSingleInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueSingleArray3DI,\n      'Input': WebGL2KernelValueSingleInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  kernelValueMaps,\n  lookupKernelValueType\n};\n},{\"./kernel-value/array2\":74,\"./kernel-value/array3\":75,\"./kernel-value/array4\":76,\"./kernel-value/boolean\":77,\"./kernel-value/dynamic-html-image\":79,\"./kernel-value/dynamic-html-image-array\":78,\"./kernel-value/dynamic-html-video\":80,\"./kernel-value/dynamic-memory-optimized-number-texture\":81,\"./kernel-value/dynamic-number-texture\":82,\"./kernel-value/dynamic-single-array\":83,\"./kernel-value/dynamic-single-array1d-i\":84,\"./kernel-value/dynamic-single-array2d-i\":85,\"./kernel-value/dynamic-single-array3d-i\":86,\"./kernel-value/dynamic-single-input\":87,\"./kernel-value/dynamic-unsigned-array\":88,\"./kernel-value/dynamic-unsigned-input\":89,\"./kernel-value/float\":90,\"./kernel-value/html-image\":92,\"./kernel-value/html-image-array\":91,\"./kernel-value/html-video\":93,\"./kernel-value/integer\":94,\"./kernel-value/memory-optimized-number-texture\":95,\"./kernel-value/number-texture\":96,\"./kernel-value/single-array\":97,\"./kernel-value/single-array1d-i\":98,\"./kernel-value/single-array2d-i\":99,\"./kernel-value/single-array3d-i\":100,\"./kernel-value/single-input\":101,\"./kernel-value/unsigned-array\":102,\"./kernel-value/unsigned-input\":103}],74:[function(require,module,exports){\nconst { WebGLKernelValueArray2 } = require('../../web-gl/kernel-value/array2');\n\nclass WebGL2KernelValueArray2 extends WebGLKernelValueArray2 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray2\n};\n},{\"../../web-gl/kernel-value/array2\":40}],75:[function(require,module,exports){\nconst { WebGLKernelValueArray3 } = require('../../web-gl/kernel-value/array3');\n\nclass WebGL2KernelValueArray3 extends WebGLKernelValueArray3 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray3\n};\n},{\"../../web-gl/kernel-value/array3\":41}],76:[function(require,module,exports){\nconst { WebGLKernelValueArray4 } = require('../../web-gl/kernel-value/array4');\n\nclass WebGL2KernelValueArray4 extends WebGLKernelValueArray4 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray4\n};\n},{\"../../web-gl/kernel-value/array4\":42}],77:[function(require,module,exports){\nconst { WebGLKernelValueBoolean } = require('../../web-gl/kernel-value/boolean');\n\nclass WebGL2KernelValueBoolean extends WebGLKernelValueBoolean {}\n\nmodule.exports = {\n  WebGL2KernelValueBoolean\n};\n},{\"../../web-gl/kernel-value/boolean\":43}],78:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImageArray } = require('./html-image-array');\n\nclass WebGL2KernelValueDynamicHTMLImageArray extends WebGL2KernelValueHTMLImageArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { width, height } = images[0];\n    this.checkSize(width, height);\n    this.dimensions = [width, height, images.length];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(images);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImageArray\n};\n},{\"../../../utils\":113,\"./html-image-array\":91}],79:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicHTMLImage } = require('../../web-gl/kernel-value/dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLImage extends WebGLKernelValueDynamicHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImage\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/dynamic-html-image\":44}],80:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLVideo extends WebGL2KernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLVideo\n};\n},{\"../../../utils\":113,\"./dynamic-html-image\":79}],81:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/dynamic-memory-optimized-number-texture');\n\nclass WebGL2KernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueDynamicMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/dynamic-memory-optimized-number-texture\":46}],82:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicNumberTexture } = require('../../web-gl/kernel-value/dynamic-number-texture');\n\nclass WebGL2KernelValueDynamicNumberTexture extends WebGLKernelValueDynamicNumberTexture {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicNumberTexture\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/dynamic-number-texture\":47}],83:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray } = require('../../web-gl2/kernel-value/single-array');\n\nclass WebGL2KernelValueDynamicSingleArray extends WebGL2KernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray\n};\n},{\"../../../utils\":113,\"../../web-gl2/kernel-value/single-array\":97}],84:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray1DI } = require('../../web-gl2/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueDynamicSingleArray1DI extends WebGL2KernelValueSingleArray1DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray1DI\n};\n},{\"../../../utils\":113,\"../../web-gl2/kernel-value/single-array1d-i\":98}],85:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray2DI } = require('../../web-gl2/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueDynamicSingleArray2DI extends WebGL2KernelValueSingleArray2DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray2DI\n};\n},{\"../../../utils\":113,\"../../web-gl2/kernel-value/single-array2d-i\":99}],86:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray3DI } = require('../../web-gl2/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueDynamicSingleArray3DI extends WebGL2KernelValueSingleArray3DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray3DI\n};\n},{\"../../../utils\":113,\"../../web-gl2/kernel-value/single-array3d-i\":100}],87:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleInput } = require('../../web-gl2/kernel-value/single-input');\n\nclass WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleInput\n};\n},{\"../../../utils\":113,\"../../web-gl2/kernel-value/single-input\":101}],88:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('../../web-gl/kernel-value/dynamic-unsigned-array');\n\nclass WebGL2KernelValueDynamicUnsignedArray extends WebGLKernelValueDynamicUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedArray\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/dynamic-unsigned-array\":53}],89:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('../../web-gl/kernel-value/dynamic-unsigned-input');\n\nclass WebGL2KernelValueDynamicUnsignedInput extends WebGLKernelValueDynamicUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedInput\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/dynamic-unsigned-input\":54}],90:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueFloat } = require('../../web-gl/kernel-value/float');\n\nclass WebGL2KernelValueFloat extends WebGLKernelValueFloat {}\n\nmodule.exports = {\n  WebGL2KernelValueFloat\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/float\":55}],91:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('../../web-gl/kernel-value/array');\n\nclass WebGL2KernelValueHTMLImageArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.checkSize(value[0].width, value[0].height);\n    this.dimensions = [value[0].width, value[0].height, value.length];\n    this.textureSize = [value[0].width, value[0].height];\n  }\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    gl.texImage3D(\n      gl.TEXTURE_2D_ARRAY,\n      0,\n      gl.RGBA,\n      images[0].width,\n      images[0].height,\n      images.length,\n      0,\n      gl.RGBA,\n      gl.UNSIGNED_BYTE,\n      null\n    );\n    for (let i = 0; i < images.length; i++) {\n      const xOffset = 0;\n      const yOffset = 0;\n      const imageDepth = 1;\n      gl.texSubImage3D(\n        gl.TEXTURE_2D_ARRAY,\n        0,\n        xOffset,\n        yOffset,\n        i,\n        images[i].width,\n        images[i].height,\n        imageDepth,\n        gl.RGBA,\n        gl.UNSIGNED_BYTE,\n        this.uploadValue = images[i]\n      );\n    }\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImageArray\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/array\":39}],92:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('../../web-gl/kernel-value/html-image');\n\nclass WebGL2KernelValueHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImage\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/html-image\":56}],93:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImage } = require('./html-image');\n\nclass WebGL2KernelValueHTMLVideo extends WebGL2KernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLVideo\n};\n},{\"../../../utils\":113,\"./html-image\":92}],94:[function(require,module,exports){\nconst { WebGLKernelValueInteger } = require('../../web-gl/kernel-value/integer');\n\nclass WebGL2KernelValueInteger extends WebGLKernelValueInteger {\n  getSource(value) {\n    const variablePrecision = this.getVariablePrecisionString();\n    if (this.origin === 'constants') {\n      return `const ${ variablePrecision } int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform ${ variablePrecision } int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueInteger\n};\n},{\"../../web-gl/kernel-value/integer\":59}],95:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/memory-optimized-number-texture');\n\nclass WebGL2KernelValueMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/memory-optimized-number-texture\":60}],96:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('../../web-gl/kernel-value/number-texture');\n\nclass WebGL2KernelValueNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueNumberTexture\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/number-texture\":61}],97:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('../../web-gl/kernel-value/single-array');\n\nclass WebGL2KernelValueSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/single-array\":62}],98:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('../../web-gl/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray1DI\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/single-array1d-i\":63}],99:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('../../web-gl/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray2DI\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/single-array2d-i\":64}],100:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('../../web-gl/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray3DI\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/single-array3d-i\":65}],101:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('../../web-gl/kernel-value/single-input');\n\nclass WebGL2KernelValueSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleInput\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/single-input\":66}],102:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('../../web-gl/kernel-value/unsigned-array');\n\nclass WebGL2KernelValueUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedArray\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/unsigned-array\":67}],103:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('../../web-gl/kernel-value/unsigned-input');\n\nclass WebGL2KernelValueUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedInput\n};\n},{\"../../../utils\":113,\"../../web-gl/kernel-value/unsigned-input\":68}],104:[function(require,module,exports){\nconst { WebGLKernel } = require('../web-gl/kernel');\nconst { WebGL2FunctionNode } = require('./function-node');\nconst { FunctionBuilder } = require('../function-builder');\nconst { utils } = require('../../utils');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\n\nlet features = null;\n\nclass WebGL2Kernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl2');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      EXT_color_buffer_float: testContext.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    if (typeof WebGL2RenderingContext !== 'undefined') {\n      return context instanceof WebGL2RenderingContext;\n    }\n    return false;\n  }\n\n  static getFeatures() {\n    const gl = this.testContext;\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      kernelMap: true,\n      isTextureFloat: true,\n      isDrawBuffers: true,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  static getIsTextureFloat() {\n    return true;\n  }\n\n  static getChannelCount() {\n    return testContext.getParameter(testContext.MAX_DRAW_BUFFERS);\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl2', settings);\n  }\n\n  initExtensions() {\n    this.extensions = {\n      EXT_color_buffer_float: this.context.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n    };\n  }\n\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n    if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Float texture outputs are not supported');\n    } else if (!this.graphical && this.precision === null) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'single') {\n        console.warn('Cannot use graphical mode and single precision at the same time');\n        this.precision = 'unsigned';\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGL2FunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  drawBuffers() {\n    this.context.drawBuffers(this.drawBuffersMap);\n  }\n\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.R32F:\n        return gl.RED;\n      case gl.RG32F:\n        return gl.RG;\n      case gl.RGBA32F:\n        return gl.RGBA;\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n  getInternalFormat() {\n    const { context: gl } = this;\n\n    if (this.precision === 'single') {\n      if (this.pipeline) {\n        switch (this.returnType) {\n          case 'Number':\n          case 'Float':\n          case 'Integer':\n            if (this.optimizeFloatMemory) {\n              return gl.RGBA32F;\n            } else {\n              return gl.R32F;\n            }\n            case 'Array(2)':\n              return gl.RG32F;\n            case 'Array(3)': \n            case 'Array(4)':\n              return gl.RGBA32F;\n            default:\n              throw new Error('Unhandled return type');\n        }\n      }\n      return gl.RGBA32F;\n    }\n    return gl.RGBA;\n  }\n\n  _setupOutputTexture() {\n    const gl = this.context;\n    if (this.texture) {\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    const texture = gl.createTexture();\n    const texSize = this.texSize;\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      const format = this.getInternalFormat();\n      if (this.precision === 'single') {\n        gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  _getHeaderString() {\n    return '';\n  }\n\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    if (subKernels === null || subKernels.length < 1) {\n      return `in ${ variablePrecision } vec2 vTexCoord;\\n`;\n    } else {\n      return `out ${ variablePrecision } vec2 vTexCoord;\\n`;\n    }\n  }\n\n  _getMainArgumentsString(args) {\n    const result = [];\n    const argumentNames = this.argumentNames;\n    for (let i = 0; i < argumentNames.length; i++) {\n      result.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return result.join('');\n  }\n\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const subKernels = this.subKernels;\n    if (subKernels !== null) {\n      result.push(\n        'layout(location = 0) out vec4 data0'\n      );\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n      }\n    } else {\n      result.push(\n        'out vec4 data0'\n      );\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0 = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0.${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}.${channel} = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}.${channel} = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}[0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}[0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n      '  data0[2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`,\n        `  data${i + 1}[2] = subKernelResult_${subKernel.name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  data${i + 1} = subKernelResult_${this.subKernels[i].name}`\n      );\n    }\n    return result;\n  }\n\n  destroyExtensions() {\n    this.extensions.EXT_color_buffer_float = null;\n    this.extensions.OES_texture_float_linear = null;\n  }\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGL2FunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGL2Kernel\n};\n},{\"../../utils\":113,\"../function-builder\":8,\"../web-gl/kernel\":69,\"./fragment-shader\":71,\"./function-node\":72,\"./kernel-value-maps\":73,\"./vertex-shader\":105}],105:[function(require,module,exports){\nconst vertexShader = `#version 300 es\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nin vec2 aPos;\nin vec2 aTexCoord;\n\nout vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};\n},{}],106:[function(require,module,exports){\nconst lib = require('./index');\nconst GPU = lib.GPU;\nfor (const p in lib) {\n  if (!lib.hasOwnProperty(p)) continue;\n  if (p === 'GPU') continue; \n  GPU[p] = lib[p];\n}\n\nif (typeof window !== 'undefined') {\n  bindTo(window);\n}\nif (typeof self !== 'undefined') {\n  bindTo(self);\n}\n\nfunction bindTo(target) {\n  if (target.GPU) return;\n  Object.defineProperty(target, 'GPU', {\n    get() {\n      return GPU;\n    }\n  });\n}\n\nmodule.exports = lib;\n},{\"./index\":108}],107:[function(require,module,exports){\nconst { gpuMock } = require('gpu-mock.js');\nconst { utils } = require('./utils');\nconst { Kernel } = require('./backend/kernel');\nconst { CPUKernel } = require('./backend/cpu/kernel');\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelRunShortcut } = require('./kernel-run-shortcut');\n\n\nconst kernelOrder = [HeadlessGLKernel, WebGL2Kernel, WebGLKernel];\n\nconst kernelTypes = ['gpu', 'cpu'];\n\nconst internalKernels = {\n  'headlessgl': HeadlessGLKernel,\n  'webgl2': WebGL2Kernel,\n  'webgl': WebGLKernel,\n};\n\nlet validate = true;\n\nclass GPU {\n  static disableValidation() {\n    validate = false;\n  }\n\n  static enableValidation() {\n    validate = true;\n  }\n\n  static get isGPUSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported);\n  }\n\n  static get isKernelMapSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.kernelMap);\n  }\n\n  static get isOffscreenCanvasSupported() {\n    return (typeof Worker !== 'undefined' && typeof OffscreenCanvas !== 'undefined') || typeof importScripts !== 'undefined';\n  }\n\n  static get isWebGLSupported() {\n    return WebGLKernel.isSupported;\n  }\n\n  static get isWebGL2Supported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  static get isHeadlessGLSupported() {\n    return HeadlessGLKernel.isSupported;\n  }\n\n  static get isCanvasSupported() {\n    return typeof HTMLCanvasElement !== 'undefined';\n  }\n\n  static get isGPUHTMLImageArraySupported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  static get isSinglePrecisionSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.isFloatRead && Kernel.features.isTextureFloat);\n  }\n\n  constructor(settings) {\n    settings = settings || {};\n    this.canvas = settings.canvas || null;\n    this.context = settings.context || null;\n    this.mode = settings.mode;\n    this.Kernel = null;\n    this.kernels = [];\n    this.functions = [];\n    this.nativeFunctions = [];\n    this.injectedNative = null;\n    if (this.mode === 'dev') return;\n    this.chooseKernel();\n    if (settings.functions) {\n      for (let i = 0; i < settings.functions.length; i++) {\n        this.addFunction(settings.functions[i]);\n      }\n    }\n\n    if (settings.nativeFunctions) {\n      for (const p in settings.nativeFunctions) {\n        if (!settings.nativeFunctions.hasOwnProperty(p)) continue;\n        const s = settings.nativeFunctions[p];\n        const { name, source } = s;\n        this.addNativeFunction(name, source, s);\n      }\n    }\n  }\n\n  chooseKernel() {\n    if (this.Kernel) return;\n\n    let Kernel = null;\n\n    if (this.context) {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        const ExternalKernel = kernelOrder[i];\n        if (ExternalKernel.isContextMatch(this.context)) {\n          if (!ExternalKernel.isSupported) {\n            throw new Error(`Kernel type ${ExternalKernel.name} not supported`);\n          }\n          Kernel = ExternalKernel;\n          break;\n        }\n      }\n      if (Kernel === null) {\n        throw new Error('unknown Context');\n      }\n    } else if (this.mode) {\n      if (this.mode in internalKernels) {\n        if (!validate || internalKernels[this.mode].isSupported) {\n          Kernel = internalKernels[this.mode];\n        }\n      } else if (this.mode === 'gpu') {\n        for (let i = 0; i < kernelOrder.length; i++) {\n          if (kernelOrder[i].isSupported) {\n            Kernel = kernelOrder[i];\n            break;\n          }\n        }\n      } else if (this.mode === 'cpu') {\n        Kernel = CPUKernel;\n      }\n      if (!Kernel) {\n        throw new Error(`A requested mode of \"${this.mode}\" and is not supported`);\n      }\n    } else {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        if (kernelOrder[i].isSupported) {\n          Kernel = kernelOrder[i];\n          break;\n        }\n      }\n      if (!Kernel) {\n        Kernel = CPUKernel;\n      }\n    }\n\n    if (!this.mode) {\n      this.mode = Kernel.mode;\n    }\n    this.Kernel = Kernel;\n  }\n\n  createKernel(source, settings) {\n    if (typeof source === 'undefined') {\n      throw new Error('Missing source parameter');\n    }\n    if (typeof source !== 'object' && !utils.isFunction(source) && typeof source !== 'string') {\n      throw new Error('source parameter not a function');\n    }\n\n    const kernels = this.kernels;\n    if (this.mode === 'dev') {\n      const devKernel = gpuMock(source, upgradeDeprecatedCreateKernelSettings(settings));\n      kernels.push(devKernel);\n      return devKernel;\n    }\n\n    source = typeof source === 'function' ? source.toString() : source;\n    const switchableKernels = {};\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings) || {};\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    function onRequestFallback(args) {\n      console.warn('Falling back to CPU');\n      const fallbackKernel = new CPUKernel(source, {\n        argumentTypes: kernelRun.argumentTypes,\n        constantTypes: kernelRun.constantTypes,\n        graphical: kernelRun.graphical,\n        loopMaxIterations: kernelRun.loopMaxIterations,\n        constants: kernelRun.constants,\n        dynamicOutput: kernelRun.dynamicOutput,\n        dynamicArgument: kernelRun.dynamicArguments,\n        output: kernelRun.output,\n        precision: kernelRun.precision,\n        pipeline: kernelRun.pipeline,\n        immutable: kernelRun.immutable,\n        optimizeFloatMemory: kernelRun.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: kernelRun.fixIntegerDivisionAccuracy,\n        functions: kernelRun.functions,\n        nativeFunctions: kernelRun.nativeFunctions,\n        injectedNative: kernelRun.injectedNative,\n        subKernels: kernelRun.subKernels,\n        strictIntegers: kernelRun.strictIntegers,\n        debug: kernelRun.debug,\n      });\n      fallbackKernel.build.apply(fallbackKernel, args);\n      const result = fallbackKernel.run.apply(fallbackKernel, args);\n      kernelRun.replaceKernel(fallbackKernel);\n      return result;\n    }\n\n    function onRequestSwitchKernel(reasons, args, _kernel) {\n      if (_kernel.debug) {\n        console.warn('Switching kernels');\n      }\n      let newOutput = null;\n      if (_kernel.signature && !switchableKernels[_kernel.signature]) {\n        switchableKernels[_kernel.signature] = _kernel;\n      }\n      if (_kernel.dynamicOutput) {\n        for (let i = reasons.length - 1; i >= 0; i--) {\n          const reason = reasons[i];\n          if (reason.type === 'outputPrecisionMismatch') {\n            newOutput = reason.needed;\n          }\n        }\n      }\n\n      const Constructor = _kernel.constructor;\n      const argumentTypes = Constructor.getArgumentTypes(_kernel, args);\n      const signature = Constructor.getSignature(_kernel, argumentTypes);\n      const existingKernel = switchableKernels[signature];\n      if (existingKernel) {\n        existingKernel.onActivate(_kernel);\n        return existingKernel;\n      }\n\n      const newKernel = switchableKernels[signature] = new Constructor(source, {\n        argumentTypes,\n        constantTypes: _kernel.constantTypes,\n        graphical: _kernel.graphical,\n        loopMaxIterations: _kernel.loopMaxIterations,\n        constants: _kernel.constants,\n        dynamicOutput: _kernel.dynamicOutput,\n        dynamicArgument: _kernel.dynamicArguments,\n        context: _kernel.context,\n        canvas: _kernel.canvas,\n        output: newOutput || _kernel.output,\n        precision: _kernel.precision,\n        pipeline: _kernel.pipeline,\n        immutable: _kernel.immutable,\n        optimizeFloatMemory: _kernel.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: _kernel.fixIntegerDivisionAccuracy,\n        functions: _kernel.functions,\n        nativeFunctions: _kernel.nativeFunctions,\n        injectedNative: _kernel.injectedNative,\n        subKernels: _kernel.subKernels,\n        strictIntegers: _kernel.strictIntegers,\n        debug: _kernel.debug,\n        gpu: _kernel.gpu,\n        validate,\n        returnType: _kernel.returnType,\n        tactic: _kernel.tactic,\n        onRequestFallback,\n        onRequestSwitchKernel,\n        texture: _kernel.texture,\n        mappedTextures: _kernel.mappedTextures,\n        drawBuffersMap: _kernel.drawBuffersMap,\n      });\n      newKernel.build.apply(newKernel, args);\n      kernelRun.replaceKernel(newKernel);\n      kernels.push(newKernel);\n      return newKernel;\n    }\n    const mergedSettings = Object.assign({\n      context: this.context,\n      canvas: this.canvas,\n      functions: this.functions,\n      nativeFunctions: this.nativeFunctions,\n      injectedNative: this.injectedNative,\n      gpu: this,\n      validate,\n      onRequestFallback,\n      onRequestSwitchKernel\n    }, settingsCopy);\n\n    const kernel = new this.Kernel(source, mergedSettings);\n    const kernelRun = kernelRunShortcut(kernel);\n\n    if (!this.canvas) {\n      this.canvas = kernel.canvas;\n    }\n\n    if (!this.context) {\n      this.context = kernel.context;\n    }\n\n    kernels.push(kernel);\n\n    return kernelRun;\n  }\n\n  createKernelMap() {\n    let fn;\n    let settings;\n    const argument2Type = typeof arguments[arguments.length - 2];\n    if (argument2Type === 'function' || argument2Type === 'string') {\n      fn = arguments[arguments.length - 2];\n      settings = arguments[arguments.length - 1];\n    } else {\n      fn = arguments[arguments.length - 1];\n    }\n\n    if (this.mode !== 'dev') {\n      if (!this.Kernel.isSupported || !this.Kernel.features.kernelMap) {\n        if (this.mode && kernelTypes.indexOf(this.mode) < 0) {\n          throw new Error(`kernelMap not supported on ${this.Kernel.name}`);\n        }\n      }\n    }\n\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings);\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    if (Array.isArray(arguments[0])) {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let i = 0; i < functions.length; i++) {\n        const source = functions[i].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name,\n          source,\n          property: i,\n        });\n      }\n    } else {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let p in functions) {\n        if (!functions.hasOwnProperty(p)) continue;\n        const source = functions[p].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name: name || p,\n          source,\n          property: p,\n        });\n      }\n    }\n    return this.createKernel(fn, settingsCopy);\n  }\n\n  combineKernels() {\n    const firstKernel = arguments[0];\n    const combinedKernel = arguments[arguments.length - 1];\n    if (firstKernel.kernel.constructor.mode === 'cpu') return combinedKernel;\n    const canvas = arguments[0].canvas;\n    const context = arguments[0].context;\n    const max = arguments.length - 1;\n    for (let i = 0; i < max; i++) {\n      arguments[i]\n        .setCanvas(canvas)\n        .setContext(context)\n        .setPipeline(true);\n    }\n\n    return function() {\n      const texture = combinedKernel.apply(this, arguments);\n      if (texture.toArray) {\n        return texture.toArray();\n      }\n      return texture;\n    };\n  }\n\n  setFunctions(functions) {\n    this.functions = functions;\n    return this;\n  }\n\n  setNativeFunctions(nativeFunctions) {\n    this.nativeFunctions = nativeFunctions;\n    return this;\n  }\n\n  addFunction(source, settings) {\n    this.functions.push({ source, settings });\n    return this;\n  }\n\n  addNativeFunction(name, source, settings) {\n    if (this.kernels.length > 0) {\n      throw new Error('Cannot call \"addNativeFunction\" after \"createKernels\" has been called.');\n    }\n    this.nativeFunctions.push(Object.assign({ name, source }, settings));\n    return this;\n  }\n\n  injectNative(source) {\n    this.injectedNative = source;\n    return this;\n  }\n\n  destroy() {\n    return new Promise((resolve, reject) => {\n      if (!this.kernels) {\n        resolve();\n      }\n      setTimeout(() => {\n        try {\n          for (let i = 0; i < this.kernels.length; i++) {\n            this.kernels[i].destroy(true); \n          }\n          let firstKernel = this.kernels[0];\n          if (firstKernel) {\n            if (firstKernel.kernel) {\n              firstKernel = firstKernel.kernel;\n            }\n            if (firstKernel.constructor.destroyContext) {\n              firstKernel.constructor.destroyContext(this.context);\n            }\n          }\n        } catch (e) {\n          reject(e);\n        }\n        resolve();\n      }, 0);\n    });\n  }\n}\n\n\nfunction upgradeDeprecatedCreateKernelSettings(settings) {\n  if (!settings) {\n    return {};\n  }\n  const upgradedSettings = Object.assign({}, settings);\n\n  if (settings.hasOwnProperty('floatOutput')) {\n    utils.warnDeprecated('setting', 'floatOutput', 'precision');\n    upgradedSettings.precision = settings.floatOutput ? 'single' : 'unsigned';\n  }\n  if (settings.hasOwnProperty('outputToTexture')) {\n    utils.warnDeprecated('setting', 'outputToTexture', 'pipeline');\n    upgradedSettings.pipeline = Boolean(settings.outputToTexture);\n  }\n  if (settings.hasOwnProperty('outputImmutable')) {\n    utils.warnDeprecated('setting', 'outputImmutable', 'immutable');\n    upgradedSettings.immutable = Boolean(settings.outputImmutable);\n  }\n  if (settings.hasOwnProperty('floatTextures')) {\n    utils.warnDeprecated('setting', 'floatTextures', 'optimizeFloatMemory');\n    upgradedSettings.optimizeFloatMemory = Boolean(settings.floatTextures);\n  }\n  return upgradedSettings;\n}\n\nmodule.exports = {\n  GPU,\n  kernelOrder,\n  kernelTypes\n};\n},{\"./backend/cpu/kernel\":7,\"./backend/headless-gl/kernel\":33,\"./backend/kernel\":35,\"./backend/web-gl/kernel\":69,\"./backend/web-gl2/kernel\":104,\"./kernel-run-shortcut\":110,\"./utils\":113,\"gpu-mock.js\":3}],108:[function(require,module,exports){\nconst { GPU } = require('./gpu');\nconst { alias } = require('./alias');\nconst { utils } = require('./utils');\nconst { Input, input } = require('./input');\nconst { Texture } = require('./texture');\nconst { FunctionBuilder } = require('./backend/function-builder');\nconst { FunctionNode } = require('./backend/function-node');\nconst { CPUFunctionNode } = require('./backend/cpu/function-node');\nconst { CPUKernel } = require('./backend/cpu/kernel');\n\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\n\nconst { WebGLFunctionNode } = require('./backend/web-gl/function-node');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelValueMaps: webGLKernelValueMaps } = require('./backend/web-gl/kernel-value-maps');\n\nconst { WebGL2FunctionNode } = require('./backend/web-gl2/function-node');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { kernelValueMaps: webGL2KernelValueMaps } = require('./backend/web-gl2/kernel-value-maps');\n\nconst { GLKernel } = require('./backend/gl/kernel');\n\nconst { Kernel } = require('./backend/kernel');\n\nconst { FunctionTracer } = require('./backend/function-tracer');\n\nconst mathRandom = require('./plugins/math-random-uniformly-distributed');\n\nmodule.exports = {\n  alias,\n  CPUFunctionNode,\n  CPUKernel,\n  GPU,\n  FunctionBuilder,\n  FunctionNode,\n  HeadlessGLKernel,\n  Input,\n  input,\n  Texture,\n  utils,\n\n  WebGL2FunctionNode,\n  WebGL2Kernel,\n  webGL2KernelValueMaps,\n\n  WebGLFunctionNode,\n  WebGLKernel,\n  webGLKernelValueMaps,\n\n  GLKernel,\n  Kernel,\n  FunctionTracer,\n\n  plugins: {\n    mathRandom\n  }\n};\n},{\"./alias\":4,\"./backend/cpu/function-node\":5,\"./backend/cpu/kernel\":7,\"./backend/function-builder\":8,\"./backend/function-node\":9,\"./backend/function-tracer\":10,\"./backend/gl/kernel\":12,\"./backend/headless-gl/kernel\":33,\"./backend/kernel\":35,\"./backend/web-gl/function-node\":37,\"./backend/web-gl/kernel\":69,\"./backend/web-gl/kernel-value-maps\":38,\"./backend/web-gl2/function-node\":72,\"./backend/web-gl2/kernel\":104,\"./backend/web-gl2/kernel-value-maps\":73,\"./gpu\":107,\"./input\":109,\"./plugins/math-random-uniformly-distributed\":111,\"./texture\":112,\"./utils\":113}],109:[function(require,module,exports){\nclass Input {\n  constructor(value, size) {\n    this.value = value;\n    if (Array.isArray(size)) {\n      this.size = size;\n    } else {\n      this.size = new Int32Array(3);\n      if (size.z) {\n        this.size = new Int32Array([size.x, size.y, size.z]);\n      } else if (size.y) {\n        this.size = new Int32Array([size.x, size.y]);\n      } else {\n        this.size = new Int32Array([size.x]);\n      }\n    }\n\n    const [w, h, d] = this.size;\n    if (d) {\n      if (this.value.length !== (w * h * d)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`);\n      }\n    } else if (h) {\n      if (this.value.length !== (w * h)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`);\n      }\n    } else {\n      if (this.value.length !== w) {\n        throw new Error(`Input size ${this.value.length} does not match ${w}`);\n      }\n    }\n\n  }\n\n  toArray() {\n    const { utils } = require('./utils');\n    const [w, h, d] = this.size;\n    if (d) {\n      return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d);\n    } else if (h) {\n      return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h);\n    } else {\n      return this.value;\n    }\n  }\n}\n\nfunction input(value, size) {\n  return new Input(value, size);\n}\n\nmodule.exports = {\n  Input,\n  input\n};\n},{\"./utils\":113}],110:[function(require,module,exports){\nconst { utils } = require('./utils');\n\nfunction kernelRunShortcut(kernel) {\n  let run = function() {\n    kernel.build.apply(kernel, arguments);\n    run = function() {\n      let result = kernel.run.apply(kernel, arguments);\n      if (kernel.switchingKernels) {\n        const reasons = kernel.resetSwitchingKernels();\n        const newKernel = kernel.onRequestSwitchKernel(reasons, arguments, kernel);\n        shortcut.kernel = kernel = newKernel;\n        result = newKernel.run.apply(newKernel, arguments);\n      }\n      if (kernel.renderKernels) {\n        return kernel.renderKernels();\n      } else if (kernel.renderOutput) {\n        return kernel.renderOutput();\n      } else {\n        return result;\n      }\n    };\n    return run.apply(kernel, arguments);\n  };\n  const shortcut = function() {\n    return run.apply(kernel, arguments);\n  };\n  shortcut.exec = function() {\n    return new Promise((accept, reject) => {\n      try {\n        accept(run.apply(this, arguments));\n      } catch (e) {\n        reject(e);\n      }\n    });\n  };\n  shortcut.replaceKernel = function(replacementKernel) {\n    kernel = replacementKernel;\n    bindKernelToShortcut(kernel, shortcut);\n  };\n\n  bindKernelToShortcut(kernel, shortcut);\n  return shortcut;\n}\n\nfunction bindKernelToShortcut(kernel, shortcut) {\n  if (shortcut.kernel) {\n    shortcut.kernel = kernel;\n    return;\n  }\n  const properties = utils.allPropertiesOf(kernel);\n  for (let i = 0; i < properties.length; i++) {\n    const property = properties[i];\n    if (property[0] === '_' && property[1] === '_') continue;\n    if (typeof kernel[property] === 'function') {\n      if (property.substring(0, 3) === 'add' || property.substring(0, 3) === 'set') {\n        shortcut[property] = function() {\n          shortcut.kernel[property].apply(shortcut.kernel, arguments);\n          return shortcut;\n        };\n      } else {\n        shortcut[property] = function() {\n          return shortcut.kernel[property].apply(shortcut.kernel, arguments);\n        };\n      }\n    } else {\n      shortcut.__defineGetter__(property, () => shortcut.kernel[property]);\n      shortcut.__defineSetter__(property, (value) => {\n        shortcut.kernel[property] = value;\n      });\n    }\n  }\n  shortcut.kernel = kernel;\n}\nmodule.exports = {\n  kernelRunShortcut\n};\n},{\"./utils\":113}],111:[function(require,module,exports){\nconst source = `// https://www.shadertoy.com/view/4t2SDh\n//note: uniformly distributed, normalized rand, [0,1]\nhighp float randomSeedShift = 1.0;\nhighp float slide = 1.0;\nuniform highp float randomSeed1;\nuniform highp float randomSeed2;\n\nhighp float nrand(highp vec2 n) {\n  highp float result = fract(sin(dot((n.xy + 1.0) * vec2(randomSeed1 * slide, randomSeed2 * randomSeedShift), vec2(12.9898, 78.233))) * 43758.5453);\n  randomSeedShift = result;\n  if (randomSeedShift > 0.5) {\n    slide += 0.00009; \n  } else {\n    slide += 0.0009;\n  }\n  return result;\n}`;\n\nconst name = 'math-random-uniformly-distributed';\n\nconst functionMatch = `Math.random()`;\n\nconst functionReplace = `nrand(vTexCoord)`;\n\nconst functionReturnType = 'Number';\nconst onBeforeRun = (kernel) => {\n  kernel.setUniform1f('randomSeed1', Math.random());\n  kernel.setUniform1f('randomSeed2', Math.random());\n};\n\nconst plugin = {\n  name,\n  onBeforeRun,\n  functionMatch,\n  functionReplace,\n  functionReturnType,\n  source\n};\n\nmodule.exports = plugin;\n},{}],112:[function(require,module,exports){\nclass Texture {\n  constructor(settings) {\n    const {\n      texture,\n      size,\n      dimensions,\n      output,\n      context,\n      type = 'NumberTexture',\n      kernel,\n      internalFormat,\n      textureFormat\n    } = settings;\n    if (!output) throw new Error('settings property \"output\" required.');\n    if (!context) throw new Error('settings property \"context\" required.');\n    if (!texture) throw new Error('settings property \"texture\" required.');\n    if (!kernel) throw new Error('settings property \"kernel\" required.');\n    this.texture = texture;\n    if (texture._refs) {\n      texture._refs++;\n    } else {\n      texture._refs = 1;\n    }\n    this.size = size;\n    this.dimensions = dimensions;\n    this.output = output;\n    this.context = context;\n    this.kernel = kernel;\n    this.type = type;\n    this._deleted = false;\n    this.internalFormat = internalFormat;\n    this.textureFormat = textureFormat;\n  }\n\n  toArray() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  clone() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  delete() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  clear() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n}\n\nmodule.exports = {\n  Texture\n};\n},{}],113:[function(require,module,exports){\nconst acorn = require('acorn');\nconst { Input } = require('./input');\nconst { Texture } = require('./texture');\n\nconst FUNCTION_NAME = /function ([^(]*)/;\nconst STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/mg;\nconst ARGUMENT_NAMES = /([^\\s,]+)/g;\n\nconst utils = {\n  systemEndianness() {\n    return _systemEndianness;\n  },\n  getSystemEndianness() {\n    const b = new ArrayBuffer(4);\n    const a = new Uint32Array(b);\n    const c = new Uint8Array(b);\n    a[0] = 0xdeadbeef;\n    if (c[0] === 0xef) return 'LE';\n    if (c[0] === 0xde) return 'BE';\n    throw new Error('unknown endianness');\n  },\n\n  isFunction(funcObj) {\n    return typeof(funcObj) === 'function';\n  },\n\n  isFunctionString(fn) {\n    if (typeof fn === 'string') {\n      return (fn\n        .slice(0, 'function'.length)\n        .toLowerCase() === 'function');\n    }\n    return false;\n  },\n\n  getFunctionNameFromString(funcStr) {\n    const result = FUNCTION_NAME.exec(funcStr);\n    if (!result || result.length === 0) return null;\n    return result[1].trim();\n  },\n\n  getFunctionBodyFromString(funcStr) {\n    return funcStr.substring(funcStr.indexOf('{') + 1, funcStr.lastIndexOf('}'));\n  },\n\n  getArgumentNamesFromString(fn) {\n    const fnStr = fn.replace(STRIP_COMMENTS, '');\n    let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);\n    if (result === null) {\n      result = [];\n    }\n    return result;\n  },\n\n  clone(obj) {\n    if (obj === null || typeof obj !== 'object' || obj.hasOwnProperty('isActiveClone')) return obj;\n\n    const temp = obj.constructor(); \n\n    for (let key in obj) {\n      if (Object.prototype.hasOwnProperty.call(obj, key)) {\n        obj.isActiveClone = null;\n        temp[key] = utils.clone(obj[key]);\n        delete obj.isActiveClone;\n      }\n    }\n\n    return temp;\n  },\n\n  isArray(array) {\n    return !isNaN(array.length);\n  },\n\n  getVariableType(value, strictIntegers) {\n    if (utils.isArray(value)) {\n      if (value.length > 0 && value[0].nodeName === 'IMG') {\n        return 'HTMLImageArray';\n      }\n      return 'Array';\n    }\n\n    switch (value.constructor) {\n      case Boolean:\n        return 'Boolean';\n      case Number:\n        if (strictIntegers && Number.isInteger(value)) {\n          return 'Integer';\n        }\n        return 'Float';\n      case Texture:\n        return value.type;\n      case Input:\n        return 'Input';\n    }\n    if ('nodeName' in value) {\n      switch (value.nodeName) {\n        case 'IMG':\n          return 'HTMLImage';\n        case 'CANVAS':\n          return 'HTMLImage';\n        case 'VIDEO':\n          return 'HTMLVideo';\n      }\n    } else if (value.hasOwnProperty('type')) {\n      return value.type;\n    } else if (typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas) {\n      return 'OffscreenCanvas';\n    } else if (typeof ImageBitmap !== 'undefined' && value instanceof ImageBitmap) {\n      return 'ImageBitmap';\n    } else if (typeof ImageData !== 'undefined' && value instanceof ImageData) {\n      return 'ImageData';\n    }\n    return 'Unknown';\n  },\n\n  getKernelTextureSize(settings, dimensions) {\n    let [w, h, d] = dimensions;\n    let texelCount = (w || 1) * (h || 1) * (d || 1);\n\n    if (settings.optimizeFloatMemory && settings.precision === 'single') {\n      w = texelCount = Math.ceil(texelCount / 4);\n    }\n    if (h > 1 && w * h === texelCount) {\n      return new Int32Array([w, h]);\n    }\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  closestSquareDimensions(length) {\n    const sqrt = Math.sqrt(length);\n    let high = Math.ceil(sqrt);\n    let low = Math.floor(sqrt);\n    while (high * low < length) {\n      high--;\n      low = Math.ceil(length / high);\n    }\n    return new Int32Array([low, Math.ceil(length / low)]);\n  },\n\n  getMemoryOptimizedFloatTextureSize(dimensions, bitRatio) {\n    const totalArea = utils.roundTo((dimensions[0] || 1) * (dimensions[1] || 1) * (dimensions[2] || 1) * (dimensions[3] || 1), 4);\n    const texelCount = totalArea / bitRatio;\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  getMemoryOptimizedPackedTextureSize(dimensions, bitRatio) {\n    const [w, h, d] = dimensions;\n    const totalArea = utils.roundTo((w || 1) * (h || 1) * (d || 1), 4);\n    const texelCount = totalArea / (4 / bitRatio);\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  roundTo(n, d) {\n    return Math.floor((n + d - 1) / d) * d;\n  },\n  getDimensions(x, pad) {\n    let ret;\n    if (utils.isArray(x)) {\n      const dim = [];\n      let temp = x;\n      while (utils.isArray(temp)) {\n        dim.push(temp.length);\n        temp = temp[0];\n      }\n      ret = dim.reverse();\n    } else if (x instanceof Texture) {\n      ret = x.output;\n    } else if (x instanceof Input) {\n      ret = x.size;\n    } else {\n      throw new Error(`Unknown dimensions of ${x}`);\n    }\n\n    if (pad) {\n      ret = Array.from(ret);\n      while (ret.length < 3) {\n        ret.push(1);\n      }\n    }\n\n    return new Int32Array(ret);\n  },\n\n  flatten2dArrayTo(array, target) {\n    let offset = 0;\n    for (let y = 0; y < array.length; y++) {\n      target.set(array[y], offset);\n      offset += array[y].length;\n    }\n  },\n\n  flatten3dArrayTo(array, target) {\n    let offset = 0;\n    for (let z = 0; z < array.length; z++) {\n      for (let y = 0; y < array[z].length; y++) {\n        target.set(array[z][y], offset);\n        offset += array[z][y].length;\n      }\n    }\n  },\n\n  flatten4dArrayTo(array, target) {\n    let offset = 0;\n    for (let l = 0; l < array.length; l++) {\n      for (let z = 0; z < array[l].length; z++) {\n        for (let y = 0; y < array[l][z].length; y++) {\n          target.set(array[l][z][y], offset);\n          offset += array[l][z][y].length;\n        }\n      }\n    }\n  },\n\n  flattenTo(array, target) {\n    if (utils.isArray(array[0])) {\n      if (utils.isArray(array[0][0])) {\n        if (utils.isArray(array[0][0][0])) {\n          utils.flatten4dArrayTo(array, target);\n        } else {\n          utils.flatten3dArrayTo(array, target);\n        }\n      } else {\n        utils.flatten2dArrayTo(array, target);\n      }\n    } else {\n      target.set(array);\n    }\n  },\n\n  splitArray(array, part) {\n    const result = [];\n    for (let i = 0; i < array.length; i += part) {\n      result.push(new array.constructor(array.buffer, i * 4 + array.byteOffset, part));\n    }\n    return result;\n  },\n\n  getAstString(source, ast) {\n    const lines = Array.isArray(source) ? source : source.split(/\\r?\\n/g);\n    const start = ast.loc.start;\n    const end = ast.loc.end;\n    const result = [];\n    if (start.line === end.line) {\n      result.push(lines[start.line - 1].substring(start.column, end.column));\n    } else {\n      result.push(lines[start.line - 1].slice(start.column));\n      for (let i = start.line; i < end.line; i++) {\n        result.push(lines[i]);\n      }\n      result.push(lines[end.line - 1].slice(0, end.column));\n    }\n    return result.join('\\n');\n  },\n\n  allPropertiesOf(obj) {\n    const props = [];\n\n    do {\n      props.push.apply(props, Object.getOwnPropertyNames(obj));\n    } while (obj = Object.getPrototypeOf(obj));\n\n    return props;\n  },\n\n  linesToString(lines) {\n    if (lines.length > 0) {\n      return lines.join(';\\n') + ';\\n';\n    } else {\n      return '\\n';\n    }\n  },\n  warnDeprecated(type, oldName, newName) {\n    if (newName) {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been replaced with \"${ newName }\". Fixing, but please upgrade as it will soon be removed.`);\n    } else {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been removed. Fixing, but please upgrade as it will soon be removed.`);\n    }\n  },\n  flipPixels: (pixels, width, height) => {\n    const halfHeight = height / 2 | 0; \n    const bytesPerRow = width * 4;\n    const temp = new Uint8ClampedArray(width * 4);\n    const result = pixels.slice(0);\n    for (let y = 0; y < halfHeight; ++y) {\n      const topOffset = y * bytesPerRow;\n      const bottomOffset = (height - y - 1) * bytesPerRow;\n\n      temp.set(result.subarray(topOffset, topOffset + bytesPerRow));\n\n      result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);\n\n      result.set(temp, bottomOffset);\n    }\n    return result;\n  },\n  erectPackedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erect2DPackedFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xStart = y * width;\n      const xEnd = xStart + width;\n      yResults[y] = array.subarray(xStart, xEnd);\n    }\n    return yResults;\n  },\n  erect3DPackedFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xStart = (z * height * width) + y * width;\n        const xEnd = xStart + width;\n        yResults[y] = array.subarray(xStart, xEnd);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectMemoryOptimizedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erectMemoryOptimized2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const offset = y * width;\n      yResults[y] = array.subarray(offset, offset + width);\n    }\n    return yResults;\n  },\n  erectMemoryOptimized3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const offset = (z * height * width) + (y * width);\n        yResults[y] = array.subarray(offset, offset + width);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectFloat: (array, width) => {\n    const xResults = new Float32Array(width);\n    let i = 0;\n    for (let x = 0; x < width; x++) {\n      xResults[x] = array[i];\n      i += 4;\n    }\n    return xResults;\n  },\n  erect2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    let i = 0;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Float32Array(width);\n      for (let x = 0; x < width; x++) {\n        xResults[x] = array[i];\n        i += 4;\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    let i = 0;\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Float32Array(width);\n        for (let x = 0; x < width; x++) {\n          xResults[x] = array[i];\n          i += 4;\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray2: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 2);\n    }\n    return xResults;\n  },\n  erect2DArray2: (array, width, height) => {\n    const yResults = new Array(height);\n    const XResultsMax = width * 4;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * XResultsMax;\n      let i = 0;\n      for (let x = 0; x < XResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 2);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray2: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 2);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray3: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 3);\n    }\n    return xResults;\n  },\n  erect2DArray3: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 3);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray3: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 3);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray4: (array, width) => {\n    const xResults = new Array(array);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 4);\n    }\n    return xResults;\n  },\n  erect2DArray4: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 4);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray4: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 4);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n\n  flattenFunctionToString: (source, settings) => {\n    const { findDependency, thisLookup, doNotDefine } = settings;\n    let flattened = settings.flattened;\n    if (!flattened) {\n      flattened = settings.flattened = {};\n    }\n    const ast = acorn.parse(source);\n    const functionDependencies = [];\n    let indent = 0;\n\n    function flatten(ast) {\n      if (Array.isArray(ast)) {\n        const results = [];\n        for (let i = 0; i < ast.length; i++) {\n          results.push(flatten(ast[i]));\n        }\n        return results.join('');\n      }\n      switch (ast.type) {\n        case 'Program':\n          return flatten(ast.body) + (ast.body[0].type === 'VariableDeclaration' ? ';' : '');\n        case 'FunctionDeclaration':\n          return `function ${ast.id.name}(${ast.params.map(flatten).join(', ')}) ${ flatten(ast.body) }`;\n        case 'BlockStatement': {\n          const result = [];\n          indent += 2;\n          for (let i = 0; i < ast.body.length; i++) {\n            const flat = flatten(ast.body[i]);\n            if (flat) {\n              result.push(' '.repeat(indent) + flat, ';\\n');\n            }\n          }\n          indent -= 2;\n          return `{\\n${result.join('')}}`;\n        }\n        case 'VariableDeclaration':\n          const declarations = utils.normalizeDeclarations(ast)\n            .map(flatten)\n            .filter(r => r !== null);\n          if (declarations.length < 1) {\n            return '';\n          } else {\n            return `${ast.kind} ${declarations.join(',')}`;\n          }\n          case 'VariableDeclarator':\n            if (ast.init.object && ast.init.object.type === 'ThisExpression') {\n              const lookup = thisLookup(ast.init.property.name, true);\n              if (lookup) {\n                return `${ast.id.name} = ${flatten(ast.init)}`;\n              } else {\n                return null;\n              }\n            } else {\n              return `${ast.id.name} = ${flatten(ast.init)}`;\n            }\n            case 'CallExpression': {\n              if (ast.callee.property.name === 'subarray') {\n                return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              }\n              if (ast.callee.object.name === 'gl' || ast.callee.object.name === 'context') {\n                return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              }\n              if (ast.callee.object.type === 'ThisExpression') {\n                functionDependencies.push(findDependency('this', ast.callee.property.name));\n                return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              } else if (ast.callee.object.name) {\n                const foundSource = findDependency(ast.callee.object.name, ast.callee.property.name);\n                if (foundSource === null) {\n                  return `${ast.callee.object.name}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n                } else {\n                  functionDependencies.push(foundSource);\n                  return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n                }\n              } else if (ast.callee.object.type === 'MemberExpression') {\n                return `${flatten(ast.callee.object)}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              } else {\n                throw new Error('unknown ast.callee');\n              }\n            }\n            case 'ReturnStatement':\n              return `return ${flatten(ast.argument)}`;\n            case 'BinaryExpression':\n              return `(${flatten(ast.left)}${ast.operator}${flatten(ast.right)})`;\n            case 'UnaryExpression':\n              if (ast.prefix) {\n                return `${ast.operator} ${flatten(ast.argument)}`;\n              } else {\n                return `${flatten(ast.argument)} ${ast.operator}`;\n              }\n              case 'ExpressionStatement':\n                return `${flatten(ast.expression)}`;\n              case 'SequenceExpression':\n                return `(${flatten(ast.expressions)})`;\n              case 'ArrowFunctionExpression':\n                return `(${ast.params.map(flatten).join(', ')}) => ${flatten(ast.body)}`;\n              case 'Literal':\n                return ast.raw;\n              case 'Identifier':\n                return ast.name;\n              case 'MemberExpression':\n                if (ast.object.type === 'ThisExpression') {\n                  return thisLookup(ast.property.name);\n                }\n                if (ast.computed) {\n                  return `${flatten(ast.object)}[${flatten(ast.property)}]`;\n                }\n                return flatten(ast.object) + '.' + flatten(ast.property);\n              case 'ThisExpression':\n                return 'this';\n              case 'NewExpression':\n                return `new ${flatten(ast.callee)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              case 'ForStatement':\n                return `for (${flatten(ast.init)};${flatten(ast.test)};${flatten(ast.update)}) ${flatten(ast.body)}`;\n              case 'AssignmentExpression':\n                return `${flatten(ast.left)}${ast.operator}${flatten(ast.right)}`;\n              case 'UpdateExpression':\n                return `${flatten(ast.argument)}${ast.operator}`;\n              case 'IfStatement':\n                return `if (${flatten(ast.test)}) ${flatten(ast.consequent)}`;\n              case 'ThrowStatement':\n                return `throw ${flatten(ast.argument)}`;\n              case 'ObjectPattern':\n                return ast.properties.map(flatten).join(', ');\n              case 'ArrayPattern':\n                return ast.elements.map(flatten).join(', ');\n              case 'DebuggerStatement':\n                return 'debugger;';\n              case 'ConditionalExpression':\n                return `${flatten(ast.test)}?${flatten(ast.consequent)}:${flatten(ast.alternate)}`;\n              case 'Property':\n                if (ast.kind === 'init') {\n                  return flatten(ast.key);\n                }\n      }\n      throw new Error(`unhandled ast.type of ${ ast.type }`);\n    }\n    const result = flatten(ast);\n    if (functionDependencies.length > 0) {\n      const flattenedFunctionDependencies = [];\n      for (let i = 0; i < functionDependencies.length; i++) {\n        const functionDependency = functionDependencies[i];\n        if (!flattened[functionDependency]) {\n          flattened[functionDependency] = true;\n        }\n        functionDependency ? flattenedFunctionDependencies.push(utils.flattenFunctionToString(functionDependency, settings) + '\\n') : '';\n      }\n      return flattenedFunctionDependencies.join('') + result;\n    }\n    return result;\n  },\n\n  normalizeDeclarations: (ast) => {\n    if (ast.type !== 'VariableDeclaration') throw new Error('Ast is not of type \"VariableDeclaration\"');\n    const normalizedDeclarations = [];\n    for (let declarationIndex = 0; declarationIndex < ast.declarations.length; declarationIndex++) {\n      const declaration = ast.declarations[declarationIndex];\n      if (declaration.id && declaration.id.type === 'ObjectPattern' && declaration.id.properties) {\n        const { properties } = declaration.id;\n        for (let propertyIndex = 0; propertyIndex < properties.length; propertyIndex++) {\n          const property = properties[propertyIndex];\n          if (property.value.type === 'ObjectPattern' && property.value.properties) {\n            for (let subPropertyIndex = 0; subPropertyIndex < property.value.properties.length; subPropertyIndex++) {\n              const subProperty = property.value.properties[subPropertyIndex];\n              if (subProperty.type === 'Property') {\n                normalizedDeclarations.push({\n                  type: 'VariableDeclarator',\n                  id: {\n                    type: 'Identifier',\n                    name: subProperty.key.name\n                  },\n                  init: {\n                    type: 'MemberExpression',\n                    object: {\n                      type: 'MemberExpression',\n                      object: declaration.init,\n                      property: {\n                        type: 'Identifier',\n                        name: property.key.name\n                      },\n                      computed: false\n                    },\n                    property: {\n                      type: 'Identifier',\n                      name: subProperty.key.name\n                    },\n                    computed: false\n                  }\n                });\n              } else {\n                throw new Error('unexpected state');\n              }\n            }\n          } else if (property.value.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: property.value && property.value.name ? property.value.name : property.key.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Identifier',\n                  name: property.key.name\n                },\n                computed: false\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else if (declaration.id && declaration.id.type === 'ArrayPattern' && declaration.id.elements) {\n        const { elements } = declaration.id;\n        for (let elementIndex = 0; elementIndex < elements.length; elementIndex++) {\n          const element = elements[elementIndex];\n          if (element.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: element.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Literal',\n                  value: elementIndex,\n                  raw: elementIndex.toString(),\n                  start: element.start,\n                  end: element.end\n                },\n                computed: true\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else {\n        normalizedDeclarations.push(declaration);\n      }\n    }\n    return normalizedDeclarations;\n  },\n\n  splitHTMLImageToRGB: (gpu, image) => {\n    const rKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.r * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const gKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.g * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const bKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.b * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const aKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.a * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const result = [\n      rKernel(image),\n      gKernel(image),\n      bKernel(image),\n      aKernel(image),\n    ];\n    result.rKernel = rKernel;\n    result.gKernel = gKernel;\n    result.bKernel = bKernel;\n    result.aKernel = aKernel;\n    result.gpu = gpu;\n    return result;\n  },\n\n  splitRGBAToCanvases: (gpu, rgba, width, height) => {\n    const visualKernelR = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(pixel.r / 255, 0, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelR(rgba);\n\n    const visualKernelG = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, pixel.g / 255, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelG(rgba);\n\n    const visualKernelB = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, 0, pixel.b / 255, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelB(rgba);\n\n    const visualKernelA = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(255, 255, 255, pixel.a / 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelA(rgba);\n    return [\n      visualKernelR.canvas,\n      visualKernelG.canvas,\n      visualKernelB.canvas,\n      visualKernelA.canvas,\n    ];\n  },\n\n  getMinifySafeName: (fn) => {\n    try {\n      const ast = acorn.parse(`const value = ${fn.toString()}`);\n      const { init } = ast.body[0].declarations[0];\n      return init.body.name || init.body.body[0].argument.name;\n    } catch (e) {\n      throw new Error('Unrecognized function type.  Please use `() => yourFunctionVariableHere` or function() { return yourFunctionVariableHere; }');\n    }\n  },\n  sanitizeName: function(name) {\n    if (dollarSign.test(name)) {\n      name = name.replace(dollarSign, 'S_S');\n    }\n    if (doubleUnderscore.test(name)) {\n      name = name.replace(doubleUnderscore, 'U_U');\n    } else if (singleUnderscore.test(name)) {\n      name = name.replace(singleUnderscore, 'u_u');\n    }\n    return name;\n  }\n};\n\nconst dollarSign = /\\$/;\nconst doubleUnderscore = /__/;\nconst singleUnderscore = /_/;\n\nconst _systemEndianness = utils.getSystemEndianness();\n\nmodule.exports = {\n  utils\n};\n},{\"./input\":109,\"./texture\":112,\"acorn\":1}]},{},[106])(106)\n});\n"
  },
  {
    "path": "dist/gpu-browser.js",
    "content": "/**\n * gpu.js\n * http://gpu.rocks/\n *\n * GPU Accelerated JavaScript\n *\n * @version 2.16.0\n * @date Thu Feb 13 2025 11:46:48 GMT-0800 (Pacific Standard Time)\n *\n * @license MIT\n * The MIT License\n *\n * Copyright (c) 2025 gpu.js Team\n */(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.GPU = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n  typeof define === 'function' && define.amd ? define(['exports'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.acorn = {}));\n})(this, (function (exports) { 'use strict';\n\n  var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 80, 3, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 343, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 726, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];\n\n  var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 2, 60, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 42, 9, 8936, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 496, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4153, 7, 221, 3, 5761, 15, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 4191];\n\n  var nonASCIIidentifierChars = \"\\u200c\\u200d\\xb7\\u0300-\\u036f\\u0387\\u0483-\\u0487\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u0669\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u06f0-\\u06f9\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07c0-\\u07c9\\u07eb-\\u07f3\\u07fd\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u0897-\\u089f\\u08ca-\\u08e1\\u08e3-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962\\u0963\\u0966-\\u096f\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09cb-\\u09cd\\u09d7\\u09e2\\u09e3\\u09e6-\\u09ef\\u09fe\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a66-\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2\\u0ae3\\u0ae6-\\u0aef\\u0afa-\\u0aff\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b55-\\u0b57\\u0b62\\u0b63\\u0b66-\\u0b6f\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0be6-\\u0bef\\u0c00-\\u0c04\\u0c3c\\u0c3e-\\u0c44\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0c66-\\u0c6f\\u0c81-\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0ce6-\\u0cef\\u0cf3\\u0d00-\\u0d03\\u0d3b\\u0d3c\\u0d3e-\\u0d44\\u0d46-\\u0d48\\u0d4a-\\u0d4d\\u0d57\\u0d62\\u0d63\\u0d66-\\u0d6f\\u0d81-\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0de6-\\u0def\\u0df2\\u0df3\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0e50-\\u0e59\\u0eb1\\u0eb4-\\u0ebc\\u0ec8-\\u0ece\\u0ed0-\\u0ed9\\u0f18\\u0f19\\u0f20-\\u0f29\\u0f35\\u0f37\\u0f39\\u0f3e\\u0f3f\\u0f71-\\u0f84\\u0f86\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102b-\\u103e\\u1040-\\u1049\\u1056-\\u1059\\u105e-\\u1060\\u1062-\\u1064\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f-\\u109d\\u135d-\\u135f\\u1369-\\u1371\\u1712-\\u1715\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b4-\\u17d3\\u17dd\\u17e0-\\u17e9\\u180b-\\u180d\\u180f-\\u1819\\u18a9\\u1920-\\u192b\\u1930-\\u193b\\u1946-\\u194f\\u19d0-\\u19da\\u1a17-\\u1a1b\\u1a55-\\u1a5e\\u1a60-\\u1a7c\\u1a7f-\\u1a89\\u1a90-\\u1a99\\u1ab0-\\u1abd\\u1abf-\\u1ace\\u1b00-\\u1b04\\u1b34-\\u1b44\\u1b50-\\u1b59\\u1b6b-\\u1b73\\u1b80-\\u1b82\\u1ba1-\\u1bad\\u1bb0-\\u1bb9\\u1be6-\\u1bf3\\u1c24-\\u1c37\\u1c40-\\u1c49\\u1c50-\\u1c59\\u1cd0-\\u1cd2\\u1cd4-\\u1ce8\\u1ced\\u1cf4\\u1cf7-\\u1cf9\\u1dc0-\\u1dff\\u200c\\u200d\\u203f\\u2040\\u2054\\u20d0-\\u20dc\\u20e1\\u20e5-\\u20f0\\u2cef-\\u2cf1\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\u30fb\\ua620-\\ua629\\ua66f\\ua674-\\ua67d\\ua69e\\ua69f\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua823-\\ua827\\ua82c\\ua880\\ua881\\ua8b4-\\ua8c5\\ua8d0-\\ua8d9\\ua8e0-\\ua8f1\\ua8ff-\\ua909\\ua926-\\ua92d\\ua947-\\ua953\\ua980-\\ua983\\ua9b3-\\ua9c0\\ua9d0-\\ua9d9\\ua9e5\\ua9f0-\\ua9f9\\uaa29-\\uaa36\\uaa43\\uaa4c\\uaa4d\\uaa50-\\uaa59\\uaa7b-\\uaa7d\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uaaeb-\\uaaef\\uaaf5\\uaaf6\\uabe3-\\uabea\\uabec\\uabed\\uabf0-\\uabf9\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe2f\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff10-\\uff19\\uff3f\\uff65\";\n\n  var nonASCIIidentifierStartChars = \"\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u037f\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u052f\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05d0-\\u05ea\\u05ef-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086a\\u0870-\\u0887\\u0889-\\u088e\\u08a0-\\u08c9\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u09fc\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0af9\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c39\\u0c3d\\u0c58-\\u0c5a\\u0c5d\\u0c60\\u0c61\\u0c80\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cdd\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d04-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d54-\\u0d56\\u0d5f-\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e86-\\u0e8a\\u0e8c-\\u0ea3\\u0ea5\\u0ea7-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f5\\u13f8-\\u13fd\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f8\\u1700-\\u1711\\u171f-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1878\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191e\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19b0-\\u19c9\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4c\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1c80-\\u1c8a\\u1c90-\\u1cba\\u1cbd-\\u1cbf\\u1ce9-\\u1cec\\u1cee-\\u1cf3\\u1cf5\\u1cf6\\u1cfa\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2118-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u2c00-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303c\\u3041-\\u3096\\u309b-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312f\\u3131-\\u318e\\u31a0-\\u31bf\\u31f0-\\u31ff\\u3400-\\u4dbf\\u4e00-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua69d\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua7cd\\ua7d0\\ua7d1\\ua7d3\\ua7d5-\\ua7dc\\ua7f2-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua8fd\\ua8fe\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\ua9e0-\\ua9e4\\ua9e6-\\ua9ef\\ua9fa-\\ua9fe\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa7e-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uab30-\\uab5a\\uab5c-\\uab69\\uab70-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc\";\n\n\n\n  var reservedWords = {\n    3: \"abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile\",\n    5: \"class enum extends super const export import\",\n    6: \"enum\",\n    strict: \"implements interface let package private protected public static yield\",\n    strictBind: \"eval arguments\"\n  };\n\n\n  var ecma5AndLessKeywords = \"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this\";\n\n  var keywords$1 = {\n    5: ecma5AndLessKeywords,\n    \"5module\": ecma5AndLessKeywords + \" export import\",\n    6: ecma5AndLessKeywords + \" const class extends export import super\"\n  };\n\n  var keywordRelationalOperator = /^in(stanceof)?$/;\n\n\n  var nonASCIIidentifierStart = new RegExp(\"[\" + nonASCIIidentifierStartChars + \"]\");\n  var nonASCIIidentifier = new RegExp(\"[\" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + \"]\");\n\n  function isInAstralSet(code, set) {\n    var pos = 0x10000;\n    for (var i = 0; i < set.length; i += 2) {\n      pos += set[i];\n      if (pos > code) { return false }\n      pos += set[i + 1];\n      if (pos >= code) { return true }\n    }\n    return false\n  }\n\n\n  function isIdentifierStart(code, astral) {\n    if (code < 65) { return code === 36 }\n    if (code < 91) { return true }\n    if (code < 97) { return code === 95 }\n    if (code < 123) { return true }\n    if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }\n    if (astral === false) { return false }\n    return isInAstralSet(code, astralIdentifierStartCodes)\n  }\n\n\n  function isIdentifierChar(code, astral) {\n    if (code < 48) { return code === 36 }\n    if (code < 58) { return true }\n    if (code < 65) { return false }\n    if (code < 91) { return true }\n    if (code < 97) { return code === 95 }\n    if (code < 123) { return true }\n    if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }\n    if (astral === false) { return false }\n    return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)\n  }\n\n\n\n\n\n  var TokenType = function TokenType(label, conf) {\n    if ( conf === void 0 ) conf = {};\n\n    this.label = label;\n    this.keyword = conf.keyword;\n    this.beforeExpr = !!conf.beforeExpr;\n    this.startsExpr = !!conf.startsExpr;\n    this.isLoop = !!conf.isLoop;\n    this.isAssign = !!conf.isAssign;\n    this.prefix = !!conf.prefix;\n    this.postfix = !!conf.postfix;\n    this.binop = conf.binop || null;\n    this.updateContext = null;\n  };\n\n  function binop(name, prec) {\n    return new TokenType(name, {beforeExpr: true, binop: prec})\n  }\n  var beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};\n\n\n  var keywords = {};\n\n  function kw(name, options) {\n    if ( options === void 0 ) options = {};\n\n    options.keyword = name;\n    return keywords[name] = new TokenType(name, options)\n  }\n\n  var types$1 = {\n    num: new TokenType(\"num\", startsExpr),\n    regexp: new TokenType(\"regexp\", startsExpr),\n    string: new TokenType(\"string\", startsExpr),\n    name: new TokenType(\"name\", startsExpr),\n    privateId: new TokenType(\"privateId\", startsExpr),\n    eof: new TokenType(\"eof\"),\n\n    bracketL: new TokenType(\"[\", {beforeExpr: true, startsExpr: true}),\n    bracketR: new TokenType(\"]\"),\n    braceL: new TokenType(\"{\", {beforeExpr: true, startsExpr: true}),\n    braceR: new TokenType(\"}\"),\n    parenL: new TokenType(\"(\", {beforeExpr: true, startsExpr: true}),\n    parenR: new TokenType(\")\"),\n    comma: new TokenType(\",\", beforeExpr),\n    semi: new TokenType(\";\", beforeExpr),\n    colon: new TokenType(\":\", beforeExpr),\n    dot: new TokenType(\".\"),\n    question: new TokenType(\"?\", beforeExpr),\n    questionDot: new TokenType(\"?.\"),\n    arrow: new TokenType(\"=>\", beforeExpr),\n    template: new TokenType(\"template\"),\n    invalidTemplate: new TokenType(\"invalidTemplate\"),\n    ellipsis: new TokenType(\"...\", beforeExpr),\n    backQuote: new TokenType(\"`\", startsExpr),\n    dollarBraceL: new TokenType(\"${\", {beforeExpr: true, startsExpr: true}),\n\n\n    eq: new TokenType(\"=\", {beforeExpr: true, isAssign: true}),\n    assign: new TokenType(\"_=\", {beforeExpr: true, isAssign: true}),\n    incDec: new TokenType(\"++/--\", {prefix: true, postfix: true, startsExpr: true}),\n    prefix: new TokenType(\"!/~\", {beforeExpr: true, prefix: true, startsExpr: true}),\n    logicalOR: binop(\"||\", 1),\n    logicalAND: binop(\"&&\", 2),\n    bitwiseOR: binop(\"|\", 3),\n    bitwiseXOR: binop(\"^\", 4),\n    bitwiseAND: binop(\"&\", 5),\n    equality: binop(\"==/!=/===/!==\", 6),\n    relational: binop(\"</>/<=/>=\", 7),\n    bitShift: binop(\"<</>>/>>>\", 8),\n    plusMin: new TokenType(\"+/-\", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),\n    modulo: binop(\"%\", 10),\n    star: binop(\"*\", 10),\n    slash: binop(\"/\", 10),\n    starstar: new TokenType(\"**\", {beforeExpr: true}),\n    coalesce: binop(\"??\", 1),\n\n    _break: kw(\"break\"),\n    _case: kw(\"case\", beforeExpr),\n    _catch: kw(\"catch\"),\n    _continue: kw(\"continue\"),\n    _debugger: kw(\"debugger\"),\n    _default: kw(\"default\", beforeExpr),\n    _do: kw(\"do\", {isLoop: true, beforeExpr: true}),\n    _else: kw(\"else\", beforeExpr),\n    _finally: kw(\"finally\"),\n    _for: kw(\"for\", {isLoop: true}),\n    _function: kw(\"function\", startsExpr),\n    _if: kw(\"if\"),\n    _return: kw(\"return\", beforeExpr),\n    _switch: kw(\"switch\"),\n    _throw: kw(\"throw\", beforeExpr),\n    _try: kw(\"try\"),\n    _var: kw(\"var\"),\n    _const: kw(\"const\"),\n    _while: kw(\"while\", {isLoop: true}),\n    _with: kw(\"with\"),\n    _new: kw(\"new\", {beforeExpr: true, startsExpr: true}),\n    _this: kw(\"this\", startsExpr),\n    _super: kw(\"super\", startsExpr),\n    _class: kw(\"class\", startsExpr),\n    _extends: kw(\"extends\", beforeExpr),\n    _export: kw(\"export\"),\n    _import: kw(\"import\", startsExpr),\n    _null: kw(\"null\", startsExpr),\n    _true: kw(\"true\", startsExpr),\n    _false: kw(\"false\", startsExpr),\n    _in: kw(\"in\", {beforeExpr: true, binop: 7}),\n    _instanceof: kw(\"instanceof\", {beforeExpr: true, binop: 7}),\n    _typeof: kw(\"typeof\", {beforeExpr: true, prefix: true, startsExpr: true}),\n    _void: kw(\"void\", {beforeExpr: true, prefix: true, startsExpr: true}),\n    _delete: kw(\"delete\", {beforeExpr: true, prefix: true, startsExpr: true})\n  };\n\n\n  var lineBreak = /\\r\\n?|\\n|\\u2028|\\u2029/;\n  var lineBreakG = new RegExp(lineBreak.source, \"g\");\n\n  function isNewLine(code) {\n    return code === 10 || code === 13 || code === 0x2028 || code === 0x2029\n  }\n\n  function nextLineBreak(code, from, end) {\n    if ( end === void 0 ) end = code.length;\n\n    for (var i = from; i < end; i++) {\n      var next = code.charCodeAt(i);\n      if (isNewLine(next))\n        { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 }\n    }\n    return -1\n  }\n\n  var nonASCIIwhitespace = /[\\u1680\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]/;\n\n  var skipWhiteSpace = /(?:\\s|\\/\\/.*|\\/\\*[^]*?\\*\\/)*/g;\n\n  var ref = Object.prototype;\n  var hasOwnProperty = ref.hasOwnProperty;\n  var toString = ref.toString;\n\n  var hasOwn = Object.hasOwn || (function (obj, propName) { return (\n    hasOwnProperty.call(obj, propName)\n  ); });\n\n  var isArray = Array.isArray || (function (obj) { return (\n    toString.call(obj) === \"[object Array]\"\n  ); });\n\n  var regexpCache = Object.create(null);\n\n  function wordsRegexp(words) {\n    return regexpCache[words] || (regexpCache[words] = new RegExp(\"^(?:\" + words.replace(/ /g, \"|\") + \")$\"))\n  }\n\n  function codePointToString(code) {\n    if (code <= 0xFFFF) { return String.fromCharCode(code) }\n    code -= 0x10000;\n    return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)\n  }\n\n  var loneSurrogate = /(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/;\n\n\n  var Position = function Position(line, col) {\n    this.line = line;\n    this.column = col;\n  };\n\n  Position.prototype.offset = function offset (n) {\n    return new Position(this.line, this.column + n)\n  };\n\n  var SourceLocation = function SourceLocation(p, start, end) {\n    this.start = start;\n    this.end = end;\n    if (p.sourceFile !== null) { this.source = p.sourceFile; }\n  };\n\n\n  function getLineInfo(input, offset) {\n    for (var line = 1, cur = 0;;) {\n      var nextBreak = nextLineBreak(input, cur, offset);\n      if (nextBreak < 0) { return new Position(line, offset - cur) }\n      ++line;\n      cur = nextBreak;\n    }\n  }\n\n\n  var defaultOptions = {\n    ecmaVersion: null,\n    sourceType: \"script\",\n    onInsertedSemicolon: null,\n    onTrailingComma: null,\n    allowReserved: null,\n    allowReturnOutsideFunction: false,\n    allowImportExportEverywhere: false,\n    allowAwaitOutsideFunction: null,\n    allowSuperOutsideMethod: null,\n    allowHashBang: false,\n    checkPrivateFields: true,\n    locations: false,\n    onToken: null,\n    onComment: null,\n    ranges: false,\n    program: null,\n    sourceFile: null,\n    directSourceFile: null,\n    preserveParens: false\n  };\n\n\n  var warnedAboutEcmaVersion = false;\n\n  function getOptions(opts) {\n    var options = {};\n\n    for (var opt in defaultOptions)\n      { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; }\n\n    if (options.ecmaVersion === \"latest\") {\n      options.ecmaVersion = 1e8;\n    } else if (options.ecmaVersion == null) {\n      if (!warnedAboutEcmaVersion && typeof console === \"object\" && console.warn) {\n        warnedAboutEcmaVersion = true;\n        console.warn(\"Since Acorn 8.0.0, options.ecmaVersion is required.\\nDefaulting to 2020, but this will stop working in the future.\");\n      }\n      options.ecmaVersion = 11;\n    } else if (options.ecmaVersion >= 2015) {\n      options.ecmaVersion -= 2009;\n    }\n\n    if (options.allowReserved == null)\n      { options.allowReserved = options.ecmaVersion < 5; }\n\n    if (!opts || opts.allowHashBang == null)\n      { options.allowHashBang = options.ecmaVersion >= 14; }\n\n    if (isArray(options.onToken)) {\n      var tokens = options.onToken;\n      options.onToken = function (token) { return tokens.push(token); };\n    }\n    if (isArray(options.onComment))\n      { options.onComment = pushComment(options, options.onComment); }\n\n    return options\n  }\n\n  function pushComment(options, array) {\n    return function(block, text, start, end, startLoc, endLoc) {\n      var comment = {\n        type: block ? \"Block\" : \"Line\",\n        value: text,\n        start: start,\n        end: end\n      };\n      if (options.locations)\n        { comment.loc = new SourceLocation(this, startLoc, endLoc); }\n      if (options.ranges)\n        { comment.range = [start, end]; }\n      array.push(comment);\n    }\n  }\n\n  var\n      SCOPE_TOP = 1,\n      SCOPE_FUNCTION = 2,\n      SCOPE_ASYNC = 4,\n      SCOPE_GENERATOR = 8,\n      SCOPE_ARROW = 16,\n      SCOPE_SIMPLE_CATCH = 32,\n      SCOPE_SUPER = 64,\n      SCOPE_DIRECT_SUPER = 128,\n      SCOPE_CLASS_STATIC_BLOCK = 256,\n      SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK;\n\n  function functionFlags(async, generator) {\n    return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)\n  }\n\n  var\n      BIND_NONE = 0, \n      BIND_VAR = 1, \n      BIND_LEXICAL = 2, \n      BIND_FUNCTION = 3, \n      BIND_SIMPLE_CATCH = 4, \n      BIND_OUTSIDE = 5; \n\n  var Parser = function Parser(options, input, startPos) {\n    this.options = options = getOptions(options);\n    this.sourceFile = options.sourceFile;\n    this.keywords = wordsRegexp(keywords$1[options.ecmaVersion >= 6 ? 6 : options.sourceType === \"module\" ? \"5module\" : 5]);\n    var reserved = \"\";\n    if (options.allowReserved !== true) {\n      reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3];\n      if (options.sourceType === \"module\") { reserved += \" await\"; }\n    }\n    this.reservedWords = wordsRegexp(reserved);\n    var reservedStrict = (reserved ? reserved + \" \" : \"\") + reservedWords.strict;\n    this.reservedWordsStrict = wordsRegexp(reservedStrict);\n    this.reservedWordsStrictBind = wordsRegexp(reservedStrict + \" \" + reservedWords.strictBind);\n    this.input = String(input);\n\n    this.containsEsc = false;\n\n\n    if (startPos) {\n      this.pos = startPos;\n      this.lineStart = this.input.lastIndexOf(\"\\n\", startPos - 1) + 1;\n      this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;\n    } else {\n      this.pos = this.lineStart = 0;\n      this.curLine = 1;\n    }\n\n    this.type = types$1.eof;\n    this.value = null;\n    this.start = this.end = this.pos;\n    this.startLoc = this.endLoc = this.curPosition();\n\n    this.lastTokEndLoc = this.lastTokStartLoc = null;\n    this.lastTokStart = this.lastTokEnd = this.pos;\n\n    this.context = this.initialContext();\n    this.exprAllowed = true;\n\n    this.inModule = options.sourceType === \"module\";\n    this.strict = this.inModule || this.strictDirective(this.pos);\n\n    this.potentialArrowAt = -1;\n    this.potentialArrowInForAwait = false;\n\n    this.yieldPos = this.awaitPos = this.awaitIdentPos = 0;\n    this.labels = [];\n    this.undefinedExports = Object.create(null);\n\n    if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === \"#!\")\n      { this.skipLineComment(2); }\n\n    this.scopeStack = [];\n    this.enterScope(SCOPE_TOP);\n\n    this.regexpState = null;\n\n    this.privateNameStack = [];\n  };\n\n  var prototypeAccessors = { inFunction: { configurable: true },inGenerator: { configurable: true },inAsync: { configurable: true },canAwait: { configurable: true },allowSuper: { configurable: true },allowDirectSuper: { configurable: true },treatFunctionsAsVar: { configurable: true },allowNewDotTarget: { configurable: true },inClassStaticBlock: { configurable: true } };\n\n  Parser.prototype.parse = function parse () {\n    var node = this.options.program || this.startNode();\n    this.nextToken();\n    return this.parseTopLevel(node)\n  };\n\n  prototypeAccessors.inFunction.get = function () { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 };\n\n  prototypeAccessors.inGenerator.get = function () { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 && !this.currentVarScope().inClassFieldInit };\n\n  prototypeAccessors.inAsync.get = function () { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 && !this.currentVarScope().inClassFieldInit };\n\n  prototypeAccessors.canAwait.get = function () {\n    for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n      var scope = this.scopeStack[i];\n      if (scope.inClassFieldInit || scope.flags & SCOPE_CLASS_STATIC_BLOCK) { return false }\n      if (scope.flags & SCOPE_FUNCTION) { return (scope.flags & SCOPE_ASYNC) > 0 }\n    }\n    return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction\n  };\n\n  prototypeAccessors.allowSuper.get = function () {\n    var ref = this.currentThisScope();\n      var flags = ref.flags;\n      var inClassFieldInit = ref.inClassFieldInit;\n    return (flags & SCOPE_SUPER) > 0 || inClassFieldInit || this.options.allowSuperOutsideMethod\n  };\n\n  prototypeAccessors.allowDirectSuper.get = function () { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 };\n\n  prototypeAccessors.treatFunctionsAsVar.get = function () { return this.treatFunctionsAsVarInScope(this.currentScope()) };\n\n  prototypeAccessors.allowNewDotTarget.get = function () {\n    var ref = this.currentThisScope();\n      var flags = ref.flags;\n      var inClassFieldInit = ref.inClassFieldInit;\n    return (flags & (SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK)) > 0 || inClassFieldInit\n  };\n\n  prototypeAccessors.inClassStaticBlock.get = function () {\n    return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0\n  };\n\n  Parser.extend = function extend () {\n      var plugins = [], len = arguments.length;\n      while ( len-- ) plugins[ len ] = arguments[ len ];\n\n    var cls = this;\n    for (var i = 0; i < plugins.length; i++) { cls = plugins[i](cls); }\n    return cls\n  };\n\n  Parser.parse = function parse (input, options) {\n    return new this(options, input).parse()\n  };\n\n  Parser.parseExpressionAt = function parseExpressionAt (input, pos, options) {\n    var parser = new this(options, input, pos);\n    parser.nextToken();\n    return parser.parseExpression()\n  };\n\n  Parser.tokenizer = function tokenizer (input, options) {\n    return new this(options, input)\n  };\n\n  Object.defineProperties( Parser.prototype, prototypeAccessors );\n\n  var pp$9 = Parser.prototype;\n\n\n  var literal = /^(?:'((?:\\\\[^]|[^'\\\\])*?)'|\"((?:\\\\[^]|[^\"\\\\])*?)\")/;\n  pp$9.strictDirective = function(start) {\n    if (this.options.ecmaVersion < 5) { return false }\n    for (;;) {\n      skipWhiteSpace.lastIndex = start;\n      start += skipWhiteSpace.exec(this.input)[0].length;\n      var match = literal.exec(this.input.slice(start));\n      if (!match) { return false }\n      if ((match[1] || match[2]) === \"use strict\") {\n        skipWhiteSpace.lastIndex = start + match[0].length;\n        var spaceAfter = skipWhiteSpace.exec(this.input), end = spaceAfter.index + spaceAfter[0].length;\n        var next = this.input.charAt(end);\n        return next === \";\" || next === \"}\" ||\n          (lineBreak.test(spaceAfter[0]) &&\n           !(/[(`.[+\\-/*%<>=,?^&]/.test(next) || next === \"!\" && this.input.charAt(end + 1) === \"=\"))\n      }\n      start += match[0].length;\n\n      skipWhiteSpace.lastIndex = start;\n      start += skipWhiteSpace.exec(this.input)[0].length;\n      if (this.input[start] === \";\")\n        { start++; }\n    }\n  };\n\n\n  pp$9.eat = function(type) {\n    if (this.type === type) {\n      this.next();\n      return true\n    } else {\n      return false\n    }\n  };\n\n\n  pp$9.isContextual = function(name) {\n    return this.type === types$1.name && this.value === name && !this.containsEsc\n  };\n\n\n  pp$9.eatContextual = function(name) {\n    if (!this.isContextual(name)) { return false }\n    this.next();\n    return true\n  };\n\n\n  pp$9.expectContextual = function(name) {\n    if (!this.eatContextual(name)) { this.unexpected(); }\n  };\n\n\n  pp$9.canInsertSemicolon = function() {\n    return this.type === types$1.eof ||\n      this.type === types$1.braceR ||\n      lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n  };\n\n  pp$9.insertSemicolon = function() {\n    if (this.canInsertSemicolon()) {\n      if (this.options.onInsertedSemicolon)\n        { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }\n      return true\n    }\n  };\n\n\n  pp$9.semicolon = function() {\n    if (!this.eat(types$1.semi) && !this.insertSemicolon()) { this.unexpected(); }\n  };\n\n  pp$9.afterTrailingComma = function(tokType, notNext) {\n    if (this.type === tokType) {\n      if (this.options.onTrailingComma)\n        { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }\n      if (!notNext)\n        { this.next(); }\n      return true\n    }\n  };\n\n\n  pp$9.expect = function(type) {\n    this.eat(type) || this.unexpected();\n  };\n\n\n  pp$9.unexpected = function(pos) {\n    this.raise(pos != null ? pos : this.start, \"Unexpected token\");\n  };\n\n  var DestructuringErrors = function DestructuringErrors() {\n    this.shorthandAssign =\n    this.trailingComma =\n    this.parenthesizedAssign =\n    this.parenthesizedBind =\n    this.doubleProto =\n      -1;\n  };\n\n  pp$9.checkPatternErrors = function(refDestructuringErrors, isAssign) {\n    if (!refDestructuringErrors) { return }\n    if (refDestructuringErrors.trailingComma > -1)\n      { this.raiseRecoverable(refDestructuringErrors.trailingComma, \"Comma is not permitted after the rest element\"); }\n    var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;\n    if (parens > -1) { this.raiseRecoverable(parens, isAssign ? \"Assigning to rvalue\" : \"Parenthesized pattern\"); }\n  };\n\n  pp$9.checkExpressionErrors = function(refDestructuringErrors, andThrow) {\n    if (!refDestructuringErrors) { return false }\n    var shorthandAssign = refDestructuringErrors.shorthandAssign;\n    var doubleProto = refDestructuringErrors.doubleProto;\n    if (!andThrow) { return shorthandAssign >= 0 || doubleProto >= 0 }\n    if (shorthandAssign >= 0)\n      { this.raise(shorthandAssign, \"Shorthand property assignments are valid only in destructuring patterns\"); }\n    if (doubleProto >= 0)\n      { this.raiseRecoverable(doubleProto, \"Redefinition of __proto__ property\"); }\n  };\n\n  pp$9.checkYieldAwaitInDefaultParams = function() {\n    if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))\n      { this.raise(this.yieldPos, \"Yield expression cannot be a default value\"); }\n    if (this.awaitPos)\n      { this.raise(this.awaitPos, \"Await expression cannot be a default value\"); }\n  };\n\n  pp$9.isSimpleAssignTarget = function(expr) {\n    if (expr.type === \"ParenthesizedExpression\")\n      { return this.isSimpleAssignTarget(expr.expression) }\n    return expr.type === \"Identifier\" || expr.type === \"MemberExpression\"\n  };\n\n  var pp$8 = Parser.prototype;\n\n\n\n  pp$8.parseTopLevel = function(node) {\n    var exports = Object.create(null);\n    if (!node.body) { node.body = []; }\n    while (this.type !== types$1.eof) {\n      var stmt = this.parseStatement(null, true, exports);\n      node.body.push(stmt);\n    }\n    if (this.inModule)\n      { for (var i = 0, list = Object.keys(this.undefinedExports); i < list.length; i += 1)\n        {\n          var name = list[i];\n\n          this.raiseRecoverable(this.undefinedExports[name].start, (\"Export '\" + name + \"' is not defined\"));\n        } }\n    this.adaptDirectivePrologue(node.body);\n    this.next();\n    node.sourceType = this.options.sourceType;\n    return this.finishNode(node, \"Program\")\n  };\n\n  var loopLabel = {kind: \"loop\"}, switchLabel = {kind: \"switch\"};\n\n  pp$8.isLet = function(context) {\n    if (this.options.ecmaVersion < 6 || !this.isContextual(\"let\")) { return false }\n    skipWhiteSpace.lastIndex = this.pos;\n    var skip = skipWhiteSpace.exec(this.input);\n    var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);\n    if (nextCh === 91 || nextCh === 92) { return true } \n    if (context) { return false }\n\n    if (nextCh === 123 || nextCh > 0xd7ff && nextCh < 0xdc00) { return true } \n    if (isIdentifierStart(nextCh, true)) {\n      var pos = next + 1;\n      while (isIdentifierChar(nextCh = this.input.charCodeAt(pos), true)) { ++pos; }\n      if (nextCh === 92 || nextCh > 0xd7ff && nextCh < 0xdc00) { return true }\n      var ident = this.input.slice(next, pos);\n      if (!keywordRelationalOperator.test(ident)) { return true }\n    }\n    return false\n  };\n\n  pp$8.isAsyncFunction = function() {\n    if (this.options.ecmaVersion < 8 || !this.isContextual(\"async\"))\n      { return false }\n\n    skipWhiteSpace.lastIndex = this.pos;\n    var skip = skipWhiteSpace.exec(this.input);\n    var next = this.pos + skip[0].length, after;\n    return !lineBreak.test(this.input.slice(this.pos, next)) &&\n      this.input.slice(next, next + 8) === \"function\" &&\n      (next + 8 === this.input.length ||\n       !(isIdentifierChar(after = this.input.charCodeAt(next + 8)) || after > 0xd7ff && after < 0xdc00))\n  };\n\n\n  pp$8.parseStatement = function(context, topLevel, exports) {\n    var starttype = this.type, node = this.startNode(), kind;\n\n    if (this.isLet(context)) {\n      starttype = types$1._var;\n      kind = \"let\";\n    }\n\n\n    switch (starttype) {\n    case types$1._break: case types$1._continue: return this.parseBreakContinueStatement(node, starttype.keyword)\n    case types$1._debugger: return this.parseDebuggerStatement(node)\n    case types$1._do: return this.parseDoStatement(node)\n    case types$1._for: return this.parseForStatement(node)\n    case types$1._function:\n      if ((context && (this.strict || context !== \"if\" && context !== \"label\")) && this.options.ecmaVersion >= 6) { this.unexpected(); }\n      return this.parseFunctionStatement(node, false, !context)\n    case types$1._class:\n      if (context) { this.unexpected(); }\n      return this.parseClass(node, true)\n    case types$1._if: return this.parseIfStatement(node)\n    case types$1._return: return this.parseReturnStatement(node)\n    case types$1._switch: return this.parseSwitchStatement(node)\n    case types$1._throw: return this.parseThrowStatement(node)\n    case types$1._try: return this.parseTryStatement(node)\n    case types$1._const: case types$1._var:\n      kind = kind || this.value;\n      if (context && kind !== \"var\") { this.unexpected(); }\n      return this.parseVarStatement(node, kind)\n    case types$1._while: return this.parseWhileStatement(node)\n    case types$1._with: return this.parseWithStatement(node)\n    case types$1.braceL: return this.parseBlock(true, node)\n    case types$1.semi: return this.parseEmptyStatement(node)\n    case types$1._export:\n    case types$1._import:\n      if (this.options.ecmaVersion > 10 && starttype === types$1._import) {\n        skipWhiteSpace.lastIndex = this.pos;\n        var skip = skipWhiteSpace.exec(this.input);\n        var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);\n        if (nextCh === 40 || nextCh === 46) \n          { return this.parseExpressionStatement(node, this.parseExpression()) }\n      }\n\n      if (!this.options.allowImportExportEverywhere) {\n        if (!topLevel)\n          { this.raise(this.start, \"'import' and 'export' may only appear at the top level\"); }\n        if (!this.inModule)\n          { this.raise(this.start, \"'import' and 'export' may appear only with 'sourceType: module'\"); }\n      }\n      return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)\n\n    default:\n      if (this.isAsyncFunction()) {\n        if (context) { this.unexpected(); }\n        this.next();\n        return this.parseFunctionStatement(node, true, !context)\n      }\n\n      var maybeName = this.value, expr = this.parseExpression();\n      if (starttype === types$1.name && expr.type === \"Identifier\" && this.eat(types$1.colon))\n        { return this.parseLabeledStatement(node, maybeName, expr, context) }\n      else { return this.parseExpressionStatement(node, expr) }\n    }\n  };\n\n  pp$8.parseBreakContinueStatement = function(node, keyword) {\n    var isBreak = keyword === \"break\";\n    this.next();\n    if (this.eat(types$1.semi) || this.insertSemicolon()) { node.label = null; }\n    else if (this.type !== types$1.name) { this.unexpected(); }\n    else {\n      node.label = this.parseIdent();\n      this.semicolon();\n    }\n\n    var i = 0;\n    for (; i < this.labels.length; ++i) {\n      var lab = this.labels[i];\n      if (node.label == null || lab.name === node.label.name) {\n        if (lab.kind != null && (isBreak || lab.kind === \"loop\")) { break }\n        if (node.label && isBreak) { break }\n      }\n    }\n    if (i === this.labels.length) { this.raise(node.start, \"Unsyntactic \" + keyword); }\n    return this.finishNode(node, isBreak ? \"BreakStatement\" : \"ContinueStatement\")\n  };\n\n  pp$8.parseDebuggerStatement = function(node) {\n    this.next();\n    this.semicolon();\n    return this.finishNode(node, \"DebuggerStatement\")\n  };\n\n  pp$8.parseDoStatement = function(node) {\n    this.next();\n    this.labels.push(loopLabel);\n    node.body = this.parseStatement(\"do\");\n    this.labels.pop();\n    this.expect(types$1._while);\n    node.test = this.parseParenExpression();\n    if (this.options.ecmaVersion >= 6)\n      { this.eat(types$1.semi); }\n    else\n      { this.semicolon(); }\n    return this.finishNode(node, \"DoWhileStatement\")\n  };\n\n\n  pp$8.parseForStatement = function(node) {\n    this.next();\n    var awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual(\"await\")) ? this.lastTokStart : -1;\n    this.labels.push(loopLabel);\n    this.enterScope(0);\n    this.expect(types$1.parenL);\n    if (this.type === types$1.semi) {\n      if (awaitAt > -1) { this.unexpected(awaitAt); }\n      return this.parseFor(node, null)\n    }\n    var isLet = this.isLet();\n    if (this.type === types$1._var || this.type === types$1._const || isLet) {\n      var init$1 = this.startNode(), kind = isLet ? \"let\" : this.value;\n      this.next();\n      this.parseVar(init$1, true, kind);\n      this.finishNode(init$1, \"VariableDeclaration\");\n      if ((this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) && init$1.declarations.length === 1) {\n        if (this.options.ecmaVersion >= 9) {\n          if (this.type === types$1._in) {\n            if (awaitAt > -1) { this.unexpected(awaitAt); }\n          } else { node.await = awaitAt > -1; }\n        }\n        return this.parseForIn(node, init$1)\n      }\n      if (awaitAt > -1) { this.unexpected(awaitAt); }\n      return this.parseFor(node, init$1)\n    }\n    var startsWithLet = this.isContextual(\"let\"), isForOf = false;\n    var containsEsc = this.containsEsc;\n    var refDestructuringErrors = new DestructuringErrors;\n    var initPos = this.start;\n    var init = awaitAt > -1\n      ? this.parseExprSubscripts(refDestructuringErrors, \"await\")\n      : this.parseExpression(true, refDestructuringErrors);\n    if (this.type === types$1._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) {\n      if (awaitAt > -1) { \n        if (this.type === types$1._in) { this.unexpected(awaitAt); }\n        node.await = true;\n      } else if (isForOf && this.options.ecmaVersion >= 8) {\n        if (init.start === initPos && !containsEsc && init.type === \"Identifier\" && init.name === \"async\") { this.unexpected(); }\n        else if (this.options.ecmaVersion >= 9) { node.await = false; }\n      }\n      if (startsWithLet && isForOf) { this.raise(init.start, \"The left-hand side of a for-of loop may not start with 'let'.\"); }\n      this.toAssignable(init, false, refDestructuringErrors);\n      this.checkLValPattern(init);\n      return this.parseForIn(node, init)\n    } else {\n      this.checkExpressionErrors(refDestructuringErrors, true);\n    }\n    if (awaitAt > -1) { this.unexpected(awaitAt); }\n    return this.parseFor(node, init)\n  };\n\n  pp$8.parseFunctionStatement = function(node, isAsync, declarationPosition) {\n    this.next();\n    return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)\n  };\n\n  pp$8.parseIfStatement = function(node) {\n    this.next();\n    node.test = this.parseParenExpression();\n    node.consequent = this.parseStatement(\"if\");\n    node.alternate = this.eat(types$1._else) ? this.parseStatement(\"if\") : null;\n    return this.finishNode(node, \"IfStatement\")\n  };\n\n  pp$8.parseReturnStatement = function(node) {\n    if (!this.inFunction && !this.options.allowReturnOutsideFunction)\n      { this.raise(this.start, \"'return' outside of function\"); }\n    this.next();\n\n\n    if (this.eat(types$1.semi) || this.insertSemicolon()) { node.argument = null; }\n    else { node.argument = this.parseExpression(); this.semicolon(); }\n    return this.finishNode(node, \"ReturnStatement\")\n  };\n\n  pp$8.parseSwitchStatement = function(node) {\n    this.next();\n    node.discriminant = this.parseParenExpression();\n    node.cases = [];\n    this.expect(types$1.braceL);\n    this.labels.push(switchLabel);\n    this.enterScope(0);\n\n\n    var cur;\n    for (var sawDefault = false; this.type !== types$1.braceR;) {\n      if (this.type === types$1._case || this.type === types$1._default) {\n        var isCase = this.type === types$1._case;\n        if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n        node.cases.push(cur = this.startNode());\n        cur.consequent = [];\n        this.next();\n        if (isCase) {\n          cur.test = this.parseExpression();\n        } else {\n          if (sawDefault) { this.raiseRecoverable(this.lastTokStart, \"Multiple default clauses\"); }\n          sawDefault = true;\n          cur.test = null;\n        }\n        this.expect(types$1.colon);\n      } else {\n        if (!cur) { this.unexpected(); }\n        cur.consequent.push(this.parseStatement(null));\n      }\n    }\n    this.exitScope();\n    if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n    this.next(); \n    this.labels.pop();\n    return this.finishNode(node, \"SwitchStatement\")\n  };\n\n  pp$8.parseThrowStatement = function(node) {\n    this.next();\n    if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))\n      { this.raise(this.lastTokEnd, \"Illegal newline after throw\"); }\n    node.argument = this.parseExpression();\n    this.semicolon();\n    return this.finishNode(node, \"ThrowStatement\")\n  };\n\n\n  var empty$1 = [];\n\n  pp$8.parseCatchClauseParam = function() {\n    var param = this.parseBindingAtom();\n    var simple = param.type === \"Identifier\";\n    this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);\n    this.checkLValPattern(param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);\n    this.expect(types$1.parenR);\n\n    return param\n  };\n\n  pp$8.parseTryStatement = function(node) {\n    this.next();\n    node.block = this.parseBlock();\n    node.handler = null;\n    if (this.type === types$1._catch) {\n      var clause = this.startNode();\n      this.next();\n      if (this.eat(types$1.parenL)) {\n        clause.param = this.parseCatchClauseParam();\n      } else {\n        if (this.options.ecmaVersion < 10) { this.unexpected(); }\n        clause.param = null;\n        this.enterScope(0);\n      }\n      clause.body = this.parseBlock(false);\n      this.exitScope();\n      node.handler = this.finishNode(clause, \"CatchClause\");\n    }\n    node.finalizer = this.eat(types$1._finally) ? this.parseBlock() : null;\n    if (!node.handler && !node.finalizer)\n      { this.raise(node.start, \"Missing catch or finally clause\"); }\n    return this.finishNode(node, \"TryStatement\")\n  };\n\n  pp$8.parseVarStatement = function(node, kind, allowMissingInitializer) {\n    this.next();\n    this.parseVar(node, false, kind, allowMissingInitializer);\n    this.semicolon();\n    return this.finishNode(node, \"VariableDeclaration\")\n  };\n\n  pp$8.parseWhileStatement = function(node) {\n    this.next();\n    node.test = this.parseParenExpression();\n    this.labels.push(loopLabel);\n    node.body = this.parseStatement(\"while\");\n    this.labels.pop();\n    return this.finishNode(node, \"WhileStatement\")\n  };\n\n  pp$8.parseWithStatement = function(node) {\n    if (this.strict) { this.raise(this.start, \"'with' in strict mode\"); }\n    this.next();\n    node.object = this.parseParenExpression();\n    node.body = this.parseStatement(\"with\");\n    return this.finishNode(node, \"WithStatement\")\n  };\n\n  pp$8.parseEmptyStatement = function(node) {\n    this.next();\n    return this.finishNode(node, \"EmptyStatement\")\n  };\n\n  pp$8.parseLabeledStatement = function(node, maybeName, expr, context) {\n    for (var i$1 = 0, list = this.labels; i$1 < list.length; i$1 += 1)\n      {\n      var label = list[i$1];\n\n      if (label.name === maybeName)\n        { this.raise(expr.start, \"Label '\" + maybeName + \"' is already declared\");\n    } }\n    var kind = this.type.isLoop ? \"loop\" : this.type === types$1._switch ? \"switch\" : null;\n    for (var i = this.labels.length - 1; i >= 0; i--) {\n      var label$1 = this.labels[i];\n      if (label$1.statementStart === node.start) {\n        label$1.statementStart = this.start;\n        label$1.kind = kind;\n      } else { break }\n    }\n    this.labels.push({name: maybeName, kind: kind, statementStart: this.start});\n    node.body = this.parseStatement(context ? context.indexOf(\"label\") === -1 ? context + \"label\" : context : \"label\");\n    this.labels.pop();\n    node.label = expr;\n    return this.finishNode(node, \"LabeledStatement\")\n  };\n\n  pp$8.parseExpressionStatement = function(node, expr) {\n    node.expression = expr;\n    this.semicolon();\n    return this.finishNode(node, \"ExpressionStatement\")\n  };\n\n\n  pp$8.parseBlock = function(createNewLexicalScope, node, exitStrict) {\n    if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;\n    if ( node === void 0 ) node = this.startNode();\n\n    node.body = [];\n    this.expect(types$1.braceL);\n    if (createNewLexicalScope) { this.enterScope(0); }\n    while (this.type !== types$1.braceR) {\n      var stmt = this.parseStatement(null);\n      node.body.push(stmt);\n    }\n    if (exitStrict) { this.strict = false; }\n    this.next();\n    if (createNewLexicalScope) { this.exitScope(); }\n    return this.finishNode(node, \"BlockStatement\")\n  };\n\n\n  pp$8.parseFor = function(node, init) {\n    node.init = init;\n    this.expect(types$1.semi);\n    node.test = this.type === types$1.semi ? null : this.parseExpression();\n    this.expect(types$1.semi);\n    node.update = this.type === types$1.parenR ? null : this.parseExpression();\n    this.expect(types$1.parenR);\n    node.body = this.parseStatement(\"for\");\n    this.exitScope();\n    this.labels.pop();\n    return this.finishNode(node, \"ForStatement\")\n  };\n\n\n  pp$8.parseForIn = function(node, init) {\n    var isForIn = this.type === types$1._in;\n    this.next();\n\n    if (\n      init.type === \"VariableDeclaration\" &&\n      init.declarations[0].init != null &&\n      (\n        !isForIn ||\n        this.options.ecmaVersion < 8 ||\n        this.strict ||\n        init.kind !== \"var\" ||\n        init.declarations[0].id.type !== \"Identifier\"\n      )\n    ) {\n      this.raise(\n        init.start,\n        ((isForIn ? \"for-in\" : \"for-of\") + \" loop variable declaration may not have an initializer\")\n      );\n    }\n    node.left = init;\n    node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();\n    this.expect(types$1.parenR);\n    node.body = this.parseStatement(\"for\");\n    this.exitScope();\n    this.labels.pop();\n    return this.finishNode(node, isForIn ? \"ForInStatement\" : \"ForOfStatement\")\n  };\n\n\n  pp$8.parseVar = function(node, isFor, kind, allowMissingInitializer) {\n    node.declarations = [];\n    node.kind = kind;\n    for (;;) {\n      var decl = this.startNode();\n      this.parseVarId(decl, kind);\n      if (this.eat(types$1.eq)) {\n        decl.init = this.parseMaybeAssign(isFor);\n      } else if (!allowMissingInitializer && kind === \"const\" && !(this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\")))) {\n        this.unexpected();\n      } else if (!allowMissingInitializer && decl.id.type !== \"Identifier\" && !(isFor && (this.type === types$1._in || this.isContextual(\"of\")))) {\n        this.raise(this.lastTokEnd, \"Complex binding patterns require an initialization value\");\n      } else {\n        decl.init = null;\n      }\n      node.declarations.push(this.finishNode(decl, \"VariableDeclarator\"));\n      if (!this.eat(types$1.comma)) { break }\n    }\n    return node\n  };\n\n  pp$8.parseVarId = function(decl, kind) {\n    decl.id = this.parseBindingAtom();\n    this.checkLValPattern(decl.id, kind === \"var\" ? BIND_VAR : BIND_LEXICAL, false);\n  };\n\n  var FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4;\n\n\n  pp$8.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {\n    this.initFunction(node);\n    if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {\n      if (this.type === types$1.star && (statement & FUNC_HANGING_STATEMENT))\n        { this.unexpected(); }\n      node.generator = this.eat(types$1.star);\n    }\n    if (this.options.ecmaVersion >= 8)\n      { node.async = !!isAsync; }\n\n    if (statement & FUNC_STATEMENT) {\n      node.id = (statement & FUNC_NULLABLE_ID) && this.type !== types$1.name ? null : this.parseIdent();\n      if (node.id && !(statement & FUNC_HANGING_STATEMENT))\n        { this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION); }\n    }\n\n    var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n    this.yieldPos = 0;\n    this.awaitPos = 0;\n    this.awaitIdentPos = 0;\n    this.enterScope(functionFlags(node.async, node.generator));\n\n    if (!(statement & FUNC_STATEMENT))\n      { node.id = this.type === types$1.name ? this.parseIdent() : null; }\n\n    this.parseFunctionParams(node);\n    this.parseFunctionBody(node, allowExpressionBody, false, forInit);\n\n    this.yieldPos = oldYieldPos;\n    this.awaitPos = oldAwaitPos;\n    this.awaitIdentPos = oldAwaitIdentPos;\n    return this.finishNode(node, (statement & FUNC_STATEMENT) ? \"FunctionDeclaration\" : \"FunctionExpression\")\n  };\n\n  pp$8.parseFunctionParams = function(node) {\n    this.expect(types$1.parenL);\n    node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n    this.checkYieldAwaitInDefaultParams();\n  };\n\n\n  pp$8.parseClass = function(node, isStatement) {\n    this.next();\n\n    var oldStrict = this.strict;\n    this.strict = true;\n\n    this.parseClassId(node, isStatement);\n    this.parseClassSuper(node);\n    var privateNameMap = this.enterClassBody();\n    var classBody = this.startNode();\n    var hadConstructor = false;\n    classBody.body = [];\n    this.expect(types$1.braceL);\n    while (this.type !== types$1.braceR) {\n      var element = this.parseClassElement(node.superClass !== null);\n      if (element) {\n        classBody.body.push(element);\n        if (element.type === \"MethodDefinition\" && element.kind === \"constructor\") {\n          if (hadConstructor) { this.raiseRecoverable(element.start, \"Duplicate constructor in the same class\"); }\n          hadConstructor = true;\n        } else if (element.key && element.key.type === \"PrivateIdentifier\" && isPrivateNameConflicted(privateNameMap, element)) {\n          this.raiseRecoverable(element.key.start, (\"Identifier '#\" + (element.key.name) + \"' has already been declared\"));\n        }\n      }\n    }\n    this.strict = oldStrict;\n    this.next();\n    node.body = this.finishNode(classBody, \"ClassBody\");\n    this.exitClassBody();\n    return this.finishNode(node, isStatement ? \"ClassDeclaration\" : \"ClassExpression\")\n  };\n\n  pp$8.parseClassElement = function(constructorAllowsSuper) {\n    if (this.eat(types$1.semi)) { return null }\n\n    var ecmaVersion = this.options.ecmaVersion;\n    var node = this.startNode();\n    var keyName = \"\";\n    var isGenerator = false;\n    var isAsync = false;\n    var kind = \"method\";\n    var isStatic = false;\n\n    if (this.eatContextual(\"static\")) {\n      if (ecmaVersion >= 13 && this.eat(types$1.braceL)) {\n        this.parseClassStaticBlock(node);\n        return node\n      }\n      if (this.isClassElementNameStart() || this.type === types$1.star) {\n        isStatic = true;\n      } else {\n        keyName = \"static\";\n      }\n    }\n    node.static = isStatic;\n    if (!keyName && ecmaVersion >= 8 && this.eatContextual(\"async\")) {\n      if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {\n        isAsync = true;\n      } else {\n        keyName = \"async\";\n      }\n    }\n    if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {\n      isGenerator = true;\n    }\n    if (!keyName && !isAsync && !isGenerator) {\n      var lastValue = this.value;\n      if (this.eatContextual(\"get\") || this.eatContextual(\"set\")) {\n        if (this.isClassElementNameStart()) {\n          kind = lastValue;\n        } else {\n          keyName = lastValue;\n        }\n      }\n    }\n\n    if (keyName) {\n      node.computed = false;\n      node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);\n      node.key.name = keyName;\n      this.finishNode(node.key, \"Identifier\");\n    } else {\n      this.parseClassElementName(node);\n    }\n\n    if (ecmaVersion < 13 || this.type === types$1.parenL || kind !== \"method\" || isGenerator || isAsync) {\n      var isConstructor = !node.static && checkKeyName(node, \"constructor\");\n      var allowsDirectSuper = isConstructor && constructorAllowsSuper;\n      if (isConstructor && kind !== \"method\") { this.raise(node.key.start, \"Constructor can't have get/set modifier\"); }\n      node.kind = isConstructor ? \"constructor\" : kind;\n      this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper);\n    } else {\n      this.parseClassField(node);\n    }\n\n    return node\n  };\n\n  pp$8.isClassElementNameStart = function() {\n    return (\n      this.type === types$1.name ||\n      this.type === types$1.privateId ||\n      this.type === types$1.num ||\n      this.type === types$1.string ||\n      this.type === types$1.bracketL ||\n      this.type.keyword\n    )\n  };\n\n  pp$8.parseClassElementName = function(element) {\n    if (this.type === types$1.privateId) {\n      if (this.value === \"constructor\") {\n        this.raise(this.start, \"Classes can't have an element named '#constructor'\");\n      }\n      element.computed = false;\n      element.key = this.parsePrivateIdent();\n    } else {\n      this.parsePropertyName(element);\n    }\n  };\n\n  pp$8.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {\n    var key = method.key;\n    if (method.kind === \"constructor\") {\n      if (isGenerator) { this.raise(key.start, \"Constructor can't be a generator\"); }\n      if (isAsync) { this.raise(key.start, \"Constructor can't be an async method\"); }\n    } else if (method.static && checkKeyName(method, \"prototype\")) {\n      this.raise(key.start, \"Classes may not have a static property named prototype\");\n    }\n\n    var value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper);\n\n    if (method.kind === \"get\" && value.params.length !== 0)\n      { this.raiseRecoverable(value.start, \"getter should have no params\"); }\n    if (method.kind === \"set\" && value.params.length !== 1)\n      { this.raiseRecoverable(value.start, \"setter should have exactly one param\"); }\n    if (method.kind === \"set\" && value.params[0].type === \"RestElement\")\n      { this.raiseRecoverable(value.params[0].start, \"Setter cannot use rest params\"); }\n\n    return this.finishNode(method, \"MethodDefinition\")\n  };\n\n  pp$8.parseClassField = function(field) {\n    if (checkKeyName(field, \"constructor\")) {\n      this.raise(field.key.start, \"Classes can't have a field named 'constructor'\");\n    } else if (field.static && checkKeyName(field, \"prototype\")) {\n      this.raise(field.key.start, \"Classes can't have a static field named 'prototype'\");\n    }\n\n    if (this.eat(types$1.eq)) {\n      var scope = this.currentThisScope();\n      var inClassFieldInit = scope.inClassFieldInit;\n      scope.inClassFieldInit = true;\n      field.value = this.parseMaybeAssign();\n      scope.inClassFieldInit = inClassFieldInit;\n    } else {\n      field.value = null;\n    }\n    this.semicolon();\n\n    return this.finishNode(field, \"PropertyDefinition\")\n  };\n\n  pp$8.parseClassStaticBlock = function(node) {\n    node.body = [];\n\n    var oldLabels = this.labels;\n    this.labels = [];\n    this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER);\n    while (this.type !== types$1.braceR) {\n      var stmt = this.parseStatement(null);\n      node.body.push(stmt);\n    }\n    this.next();\n    this.exitScope();\n    this.labels = oldLabels;\n\n    return this.finishNode(node, \"StaticBlock\")\n  };\n\n  pp$8.parseClassId = function(node, isStatement) {\n    if (this.type === types$1.name) {\n      node.id = this.parseIdent();\n      if (isStatement)\n        { this.checkLValSimple(node.id, BIND_LEXICAL, false); }\n    } else {\n      if (isStatement === true)\n        { this.unexpected(); }\n      node.id = null;\n    }\n  };\n\n  pp$8.parseClassSuper = function(node) {\n    node.superClass = this.eat(types$1._extends) ? this.parseExprSubscripts(null, false) : null;\n  };\n\n  pp$8.enterClassBody = function() {\n    var element = {declared: Object.create(null), used: []};\n    this.privateNameStack.push(element);\n    return element.declared\n  };\n\n  pp$8.exitClassBody = function() {\n    var ref = this.privateNameStack.pop();\n    var declared = ref.declared;\n    var used = ref.used;\n    if (!this.options.checkPrivateFields) { return }\n    var len = this.privateNameStack.length;\n    var parent = len === 0 ? null : this.privateNameStack[len - 1];\n    for (var i = 0; i < used.length; ++i) {\n      var id = used[i];\n      if (!hasOwn(declared, id.name)) {\n        if (parent) {\n          parent.used.push(id);\n        } else {\n          this.raiseRecoverable(id.start, (\"Private field '#\" + (id.name) + \"' must be declared in an enclosing class\"));\n        }\n      }\n    }\n  };\n\n  function isPrivateNameConflicted(privateNameMap, element) {\n    var name = element.key.name;\n    var curr = privateNameMap[name];\n\n    var next = \"true\";\n    if (element.type === \"MethodDefinition\" && (element.kind === \"get\" || element.kind === \"set\")) {\n      next = (element.static ? \"s\" : \"i\") + element.kind;\n    }\n\n    if (\n      curr === \"iget\" && next === \"iset\" ||\n      curr === \"iset\" && next === \"iget\" ||\n      curr === \"sget\" && next === \"sset\" ||\n      curr === \"sset\" && next === \"sget\"\n    ) {\n      privateNameMap[name] = \"true\";\n      return false\n    } else if (!curr) {\n      privateNameMap[name] = next;\n      return false\n    } else {\n      return true\n    }\n  }\n\n  function checkKeyName(node, name) {\n    var computed = node.computed;\n    var key = node.key;\n    return !computed && (\n      key.type === \"Identifier\" && key.name === name ||\n      key.type === \"Literal\" && key.value === name\n    )\n  }\n\n\n  pp$8.parseExportAllDeclaration = function(node, exports) {\n    if (this.options.ecmaVersion >= 11) {\n      if (this.eatContextual(\"as\")) {\n        node.exported = this.parseModuleExportName();\n        this.checkExport(exports, node.exported, this.lastTokStart);\n      } else {\n        node.exported = null;\n      }\n    }\n    this.expectContextual(\"from\");\n    if (this.type !== types$1.string) { this.unexpected(); }\n    node.source = this.parseExprAtom();\n    if (this.options.ecmaVersion >= 16)\n      { node.attributes = this.parseWithClause(); }\n    this.semicolon();\n    return this.finishNode(node, \"ExportAllDeclaration\")\n  };\n\n  pp$8.parseExport = function(node, exports) {\n    this.next();\n    if (this.eat(types$1.star)) {\n      return this.parseExportAllDeclaration(node, exports)\n    }\n    if (this.eat(types$1._default)) { \n      this.checkExport(exports, \"default\", this.lastTokStart);\n      node.declaration = this.parseExportDefaultDeclaration();\n      return this.finishNode(node, \"ExportDefaultDeclaration\")\n    }\n    if (this.shouldParseExportStatement()) {\n      node.declaration = this.parseExportDeclaration(node);\n      if (node.declaration.type === \"VariableDeclaration\")\n        { this.checkVariableExport(exports, node.declaration.declarations); }\n      else\n        { this.checkExport(exports, node.declaration.id, node.declaration.id.start); }\n      node.specifiers = [];\n      node.source = null;\n    } else { \n      node.declaration = null;\n      node.specifiers = this.parseExportSpecifiers(exports);\n      if (this.eatContextual(\"from\")) {\n        if (this.type !== types$1.string) { this.unexpected(); }\n        node.source = this.parseExprAtom();\n        if (this.options.ecmaVersion >= 16)\n          { node.attributes = this.parseWithClause(); }\n      } else {\n        for (var i = 0, list = node.specifiers; i < list.length; i += 1) {\n          var spec = list[i];\n\n          this.checkUnreserved(spec.local);\n          this.checkLocalExport(spec.local);\n\n          if (spec.local.type === \"Literal\") {\n            this.raise(spec.local.start, \"A string literal cannot be used as an exported binding without `from`.\");\n          }\n        }\n\n        node.source = null;\n      }\n      this.semicolon();\n    }\n    return this.finishNode(node, \"ExportNamedDeclaration\")\n  };\n\n  pp$8.parseExportDeclaration = function(node) {\n    return this.parseStatement(null)\n  };\n\n  pp$8.parseExportDefaultDeclaration = function() {\n    var isAsync;\n    if (this.type === types$1._function || (isAsync = this.isAsyncFunction())) {\n      var fNode = this.startNode();\n      this.next();\n      if (isAsync) { this.next(); }\n      return this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)\n    } else if (this.type === types$1._class) {\n      var cNode = this.startNode();\n      return this.parseClass(cNode, \"nullableID\")\n    } else {\n      var declaration = this.parseMaybeAssign();\n      this.semicolon();\n      return declaration\n    }\n  };\n\n  pp$8.checkExport = function(exports, name, pos) {\n    if (!exports) { return }\n    if (typeof name !== \"string\")\n      { name = name.type === \"Identifier\" ? name.name : name.value; }\n    if (hasOwn(exports, name))\n      { this.raiseRecoverable(pos, \"Duplicate export '\" + name + \"'\"); }\n    exports[name] = true;\n  };\n\n  pp$8.checkPatternExport = function(exports, pat) {\n    var type = pat.type;\n    if (type === \"Identifier\")\n      { this.checkExport(exports, pat, pat.start); }\n    else if (type === \"ObjectPattern\")\n      { for (var i = 0, list = pat.properties; i < list.length; i += 1)\n        {\n          var prop = list[i];\n\n          this.checkPatternExport(exports, prop);\n        } }\n    else if (type === \"ArrayPattern\")\n      { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {\n        var elt = list$1[i$1];\n\n          if (elt) { this.checkPatternExport(exports, elt); }\n      } }\n    else if (type === \"Property\")\n      { this.checkPatternExport(exports, pat.value); }\n    else if (type === \"AssignmentPattern\")\n      { this.checkPatternExport(exports, pat.left); }\n    else if (type === \"RestElement\")\n      { this.checkPatternExport(exports, pat.argument); }\n  };\n\n  pp$8.checkVariableExport = function(exports, decls) {\n    if (!exports) { return }\n    for (var i = 0, list = decls; i < list.length; i += 1)\n      {\n      var decl = list[i];\n\n      this.checkPatternExport(exports, decl.id);\n    }\n  };\n\n  pp$8.shouldParseExportStatement = function() {\n    return this.type.keyword === \"var\" ||\n      this.type.keyword === \"const\" ||\n      this.type.keyword === \"class\" ||\n      this.type.keyword === \"function\" ||\n      this.isLet() ||\n      this.isAsyncFunction()\n  };\n\n\n  pp$8.parseExportSpecifier = function(exports) {\n    var node = this.startNode();\n    node.local = this.parseModuleExportName();\n\n    node.exported = this.eatContextual(\"as\") ? this.parseModuleExportName() : node.local;\n    this.checkExport(\n      exports,\n      node.exported,\n      node.exported.start\n    );\n\n    return this.finishNode(node, \"ExportSpecifier\")\n  };\n\n  pp$8.parseExportSpecifiers = function(exports) {\n    var nodes = [], first = true;\n    this.expect(types$1.braceL);\n    while (!this.eat(types$1.braceR)) {\n      if (!first) {\n        this.expect(types$1.comma);\n        if (this.afterTrailingComma(types$1.braceR)) { break }\n      } else { first = false; }\n\n      nodes.push(this.parseExportSpecifier(exports));\n    }\n    return nodes\n  };\n\n\n  pp$8.parseImport = function(node) {\n    this.next();\n\n    if (this.type === types$1.string) {\n      node.specifiers = empty$1;\n      node.source = this.parseExprAtom();\n    } else {\n      node.specifiers = this.parseImportSpecifiers();\n      this.expectContextual(\"from\");\n      node.source = this.type === types$1.string ? this.parseExprAtom() : this.unexpected();\n    }\n    if (this.options.ecmaVersion >= 16)\n      { node.attributes = this.parseWithClause(); }\n    this.semicolon();\n    return this.finishNode(node, \"ImportDeclaration\")\n  };\n\n\n  pp$8.parseImportSpecifier = function() {\n    var node = this.startNode();\n    node.imported = this.parseModuleExportName();\n\n    if (this.eatContextual(\"as\")) {\n      node.local = this.parseIdent();\n    } else {\n      this.checkUnreserved(node.imported);\n      node.local = node.imported;\n    }\n    this.checkLValSimple(node.local, BIND_LEXICAL);\n\n    return this.finishNode(node, \"ImportSpecifier\")\n  };\n\n  pp$8.parseImportDefaultSpecifier = function() {\n    var node = this.startNode();\n    node.local = this.parseIdent();\n    this.checkLValSimple(node.local, BIND_LEXICAL);\n    return this.finishNode(node, \"ImportDefaultSpecifier\")\n  };\n\n  pp$8.parseImportNamespaceSpecifier = function() {\n    var node = this.startNode();\n    this.next();\n    this.expectContextual(\"as\");\n    node.local = this.parseIdent();\n    this.checkLValSimple(node.local, BIND_LEXICAL);\n    return this.finishNode(node, \"ImportNamespaceSpecifier\")\n  };\n\n  pp$8.parseImportSpecifiers = function() {\n    var nodes = [], first = true;\n    if (this.type === types$1.name) {\n      nodes.push(this.parseImportDefaultSpecifier());\n      if (!this.eat(types$1.comma)) { return nodes }\n    }\n    if (this.type === types$1.star) {\n      nodes.push(this.parseImportNamespaceSpecifier());\n      return nodes\n    }\n    this.expect(types$1.braceL);\n    while (!this.eat(types$1.braceR)) {\n      if (!first) {\n        this.expect(types$1.comma);\n        if (this.afterTrailingComma(types$1.braceR)) { break }\n      } else { first = false; }\n\n      nodes.push(this.parseImportSpecifier());\n    }\n    return nodes\n  };\n\n  pp$8.parseWithClause = function() {\n    var nodes = [];\n    if (!this.eat(types$1._with)) {\n      return nodes\n    }\n    this.expect(types$1.braceL);\n    var attributeKeys = {};\n    var first = true;\n    while (!this.eat(types$1.braceR)) {\n      if (!first) {\n        this.expect(types$1.comma);\n        if (this.afterTrailingComma(types$1.braceR)) { break }\n      } else { first = false; }\n\n      var attr = this.parseImportAttribute();\n      var keyName = attr.key.type === \"Identifier\" ? attr.key.name : attr.key.value;\n      if (hasOwn(attributeKeys, keyName))\n        { this.raiseRecoverable(attr.key.start, \"Duplicate attribute key '\" + keyName + \"'\"); }\n      attributeKeys[keyName] = true;\n      nodes.push(attr);\n    }\n    return nodes\n  };\n\n  pp$8.parseImportAttribute = function() {\n    var node = this.startNode();\n    node.key = this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\");\n    this.expect(types$1.colon);\n    if (this.type !== types$1.string) {\n      this.unexpected();\n    }\n    node.value = this.parseExprAtom();\n    return this.finishNode(node, \"ImportAttribute\")\n  };\n\n  pp$8.parseModuleExportName = function() {\n    if (this.options.ecmaVersion >= 13 && this.type === types$1.string) {\n      var stringLiteral = this.parseLiteral(this.value);\n      if (loneSurrogate.test(stringLiteral.value)) {\n        this.raise(stringLiteral.start, \"An export name cannot include a lone surrogate.\");\n      }\n      return stringLiteral\n    }\n    return this.parseIdent(true)\n  };\n\n  pp$8.adaptDirectivePrologue = function(statements) {\n    for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {\n      statements[i].directive = statements[i].expression.raw.slice(1, -1);\n    }\n  };\n  pp$8.isDirectiveCandidate = function(statement) {\n    return (\n      this.options.ecmaVersion >= 5 &&\n      statement.type === \"ExpressionStatement\" &&\n      statement.expression.type === \"Literal\" &&\n      typeof statement.expression.value === \"string\" &&\n      (this.input[statement.start] === \"\\\"\" || this.input[statement.start] === \"'\")\n    )\n  };\n\n  var pp$7 = Parser.prototype;\n\n\n  pp$7.toAssignable = function(node, isBinding, refDestructuringErrors) {\n    if (this.options.ecmaVersion >= 6 && node) {\n      switch (node.type) {\n      case \"Identifier\":\n        if (this.inAsync && node.name === \"await\")\n          { this.raise(node.start, \"Cannot use 'await' as identifier inside an async function\"); }\n        break\n\n      case \"ObjectPattern\":\n      case \"ArrayPattern\":\n      case \"AssignmentPattern\":\n      case \"RestElement\":\n        break\n\n      case \"ObjectExpression\":\n        node.type = \"ObjectPattern\";\n        if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n        for (var i = 0, list = node.properties; i < list.length; i += 1) {\n          var prop = list[i];\n\n        this.toAssignable(prop, isBinding);\n          if (\n            prop.type === \"RestElement\" &&\n            (prop.argument.type === \"ArrayPattern\" || prop.argument.type === \"ObjectPattern\")\n          ) {\n            this.raise(prop.argument.start, \"Unexpected token\");\n          }\n        }\n        break\n\n      case \"Property\":\n        if (node.kind !== \"init\") { this.raise(node.key.start, \"Object pattern can't contain getter or setter\"); }\n        this.toAssignable(node.value, isBinding);\n        break\n\n      case \"ArrayExpression\":\n        node.type = \"ArrayPattern\";\n        if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n        this.toAssignableList(node.elements, isBinding);\n        break\n\n      case \"SpreadElement\":\n        node.type = \"RestElement\";\n        this.toAssignable(node.argument, isBinding);\n        if (node.argument.type === \"AssignmentPattern\")\n          { this.raise(node.argument.start, \"Rest elements cannot have a default value\"); }\n        break\n\n      case \"AssignmentExpression\":\n        if (node.operator !== \"=\") { this.raise(node.left.end, \"Only '=' operator can be used for specifying default value.\"); }\n        node.type = \"AssignmentPattern\";\n        delete node.operator;\n        this.toAssignable(node.left, isBinding);\n        break\n\n      case \"ParenthesizedExpression\":\n        this.toAssignable(node.expression, isBinding, refDestructuringErrors);\n        break\n\n      case \"ChainExpression\":\n        this.raiseRecoverable(node.start, \"Optional chaining cannot appear in left-hand side\");\n        break\n\n      case \"MemberExpression\":\n        if (!isBinding) { break }\n\n      default:\n        this.raise(node.start, \"Assigning to rvalue\");\n      }\n    } else if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n    return node\n  };\n\n\n  pp$7.toAssignableList = function(exprList, isBinding) {\n    var end = exprList.length;\n    for (var i = 0; i < end; i++) {\n      var elt = exprList[i];\n      if (elt) { this.toAssignable(elt, isBinding); }\n    }\n    if (end) {\n      var last = exprList[end - 1];\n      if (this.options.ecmaVersion === 6 && isBinding && last && last.type === \"RestElement\" && last.argument.type !== \"Identifier\")\n        { this.unexpected(last.argument.start); }\n    }\n    return exprList\n  };\n\n\n  pp$7.parseSpread = function(refDestructuringErrors) {\n    var node = this.startNode();\n    this.next();\n    node.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n    return this.finishNode(node, \"SpreadElement\")\n  };\n\n  pp$7.parseRestBinding = function() {\n    var node = this.startNode();\n    this.next();\n\n    if (this.options.ecmaVersion === 6 && this.type !== types$1.name)\n      { this.unexpected(); }\n\n    node.argument = this.parseBindingAtom();\n\n    return this.finishNode(node, \"RestElement\")\n  };\n\n\n  pp$7.parseBindingAtom = function() {\n    if (this.options.ecmaVersion >= 6) {\n      switch (this.type) {\n      case types$1.bracketL:\n        var node = this.startNode();\n        this.next();\n        node.elements = this.parseBindingList(types$1.bracketR, true, true);\n        return this.finishNode(node, \"ArrayPattern\")\n\n      case types$1.braceL:\n        return this.parseObj(true)\n      }\n    }\n    return this.parseIdent()\n  };\n\n  pp$7.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowModifiers) {\n    var elts = [], first = true;\n    while (!this.eat(close)) {\n      if (first) { first = false; }\n      else { this.expect(types$1.comma); }\n      if (allowEmpty && this.type === types$1.comma) {\n        elts.push(null);\n      } else if (allowTrailingComma && this.afterTrailingComma(close)) {\n        break\n      } else if (this.type === types$1.ellipsis) {\n        var rest = this.parseRestBinding();\n        this.parseBindingListItem(rest);\n        elts.push(rest);\n        if (this.type === types$1.comma) { this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\"); }\n        this.expect(close);\n        break\n      } else {\n        elts.push(this.parseAssignableListItem(allowModifiers));\n      }\n    }\n    return elts\n  };\n\n  pp$7.parseAssignableListItem = function(allowModifiers) {\n    var elem = this.parseMaybeDefault(this.start, this.startLoc);\n    this.parseBindingListItem(elem);\n    return elem\n  };\n\n  pp$7.parseBindingListItem = function(param) {\n    return param\n  };\n\n\n  pp$7.parseMaybeDefault = function(startPos, startLoc, left) {\n    left = left || this.parseBindingAtom();\n    if (this.options.ecmaVersion < 6 || !this.eat(types$1.eq)) { return left }\n    var node = this.startNodeAt(startPos, startLoc);\n    node.left = left;\n    node.right = this.parseMaybeAssign();\n    return this.finishNode(node, \"AssignmentPattern\")\n  };\n\n\n  pp$7.checkLValSimple = function(expr, bindingType, checkClashes) {\n    if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n    var isBind = bindingType !== BIND_NONE;\n\n    switch (expr.type) {\n    case \"Identifier\":\n      if (this.strict && this.reservedWordsStrictBind.test(expr.name))\n        { this.raiseRecoverable(expr.start, (isBind ? \"Binding \" : \"Assigning to \") + expr.name + \" in strict mode\"); }\n      if (isBind) {\n        if (bindingType === BIND_LEXICAL && expr.name === \"let\")\n          { this.raiseRecoverable(expr.start, \"let is disallowed as a lexically bound name\"); }\n        if (checkClashes) {\n          if (hasOwn(checkClashes, expr.name))\n            { this.raiseRecoverable(expr.start, \"Argument name clash\"); }\n          checkClashes[expr.name] = true;\n        }\n        if (bindingType !== BIND_OUTSIDE) { this.declareName(expr.name, bindingType, expr.start); }\n      }\n      break\n\n    case \"ChainExpression\":\n      this.raiseRecoverable(expr.start, \"Optional chaining cannot appear in left-hand side\");\n      break\n\n    case \"MemberExpression\":\n      if (isBind) { this.raiseRecoverable(expr.start, \"Binding member expression\"); }\n      break\n\n    case \"ParenthesizedExpression\":\n      if (isBind) { this.raiseRecoverable(expr.start, \"Binding parenthesized expression\"); }\n      return this.checkLValSimple(expr.expression, bindingType, checkClashes)\n\n    default:\n      this.raise(expr.start, (isBind ? \"Binding\" : \"Assigning to\") + \" rvalue\");\n    }\n  };\n\n  pp$7.checkLValPattern = function(expr, bindingType, checkClashes) {\n    if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n    switch (expr.type) {\n    case \"ObjectPattern\":\n      for (var i = 0, list = expr.properties; i < list.length; i += 1) {\n        var prop = list[i];\n\n      this.checkLValInnerPattern(prop, bindingType, checkClashes);\n      }\n      break\n\n    case \"ArrayPattern\":\n      for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {\n        var elem = list$1[i$1];\n\n      if (elem) { this.checkLValInnerPattern(elem, bindingType, checkClashes); }\n      }\n      break\n\n    default:\n      this.checkLValSimple(expr, bindingType, checkClashes);\n    }\n  };\n\n  pp$7.checkLValInnerPattern = function(expr, bindingType, checkClashes) {\n    if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n    switch (expr.type) {\n    case \"Property\":\n      this.checkLValInnerPattern(expr.value, bindingType, checkClashes);\n      break\n\n    case \"AssignmentPattern\":\n      this.checkLValPattern(expr.left, bindingType, checkClashes);\n      break\n\n    case \"RestElement\":\n      this.checkLValPattern(expr.argument, bindingType, checkClashes);\n      break\n\n    default:\n      this.checkLValPattern(expr, bindingType, checkClashes);\n    }\n  };\n\n\n\n  var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {\n    this.token = token;\n    this.isExpr = !!isExpr;\n    this.preserveSpace = !!preserveSpace;\n    this.override = override;\n    this.generator = !!generator;\n  };\n\n  var types = {\n    b_stat: new TokContext(\"{\", false),\n    b_expr: new TokContext(\"{\", true),\n    b_tmpl: new TokContext(\"${\", false),\n    p_stat: new TokContext(\"(\", false),\n    p_expr: new TokContext(\"(\", true),\n    q_tmpl: new TokContext(\"`\", true, true, function (p) { return p.tryReadTemplateToken(); }),\n    f_stat: new TokContext(\"function\", false),\n    f_expr: new TokContext(\"function\", true),\n    f_expr_gen: new TokContext(\"function\", true, false, null, true),\n    f_gen: new TokContext(\"function\", false, false, null, true)\n  };\n\n  var pp$6 = Parser.prototype;\n\n  pp$6.initialContext = function() {\n    return [types.b_stat]\n  };\n\n  pp$6.curContext = function() {\n    return this.context[this.context.length - 1]\n  };\n\n  pp$6.braceIsBlock = function(prevType) {\n    var parent = this.curContext();\n    if (parent === types.f_expr || parent === types.f_stat)\n      { return true }\n    if (prevType === types$1.colon && (parent === types.b_stat || parent === types.b_expr))\n      { return !parent.isExpr }\n\n    if (prevType === types$1._return || prevType === types$1.name && this.exprAllowed)\n      { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }\n    if (prevType === types$1._else || prevType === types$1.semi || prevType === types$1.eof || prevType === types$1.parenR || prevType === types$1.arrow)\n      { return true }\n    if (prevType === types$1.braceL)\n      { return parent === types.b_stat }\n    if (prevType === types$1._var || prevType === types$1._const || prevType === types$1.name)\n      { return false }\n    return !this.exprAllowed\n  };\n\n  pp$6.inGeneratorContext = function() {\n    for (var i = this.context.length - 1; i >= 1; i--) {\n      var context = this.context[i];\n      if (context.token === \"function\")\n        { return context.generator }\n    }\n    return false\n  };\n\n  pp$6.updateContext = function(prevType) {\n    var update, type = this.type;\n    if (type.keyword && prevType === types$1.dot)\n      { this.exprAllowed = false; }\n    else if (update = type.updateContext)\n      { update.call(this, prevType); }\n    else\n      { this.exprAllowed = type.beforeExpr; }\n  };\n\n\n  pp$6.overrideContext = function(tokenCtx) {\n    if (this.curContext() !== tokenCtx) {\n      this.context[this.context.length - 1] = tokenCtx;\n    }\n  };\n\n\n  types$1.parenR.updateContext = types$1.braceR.updateContext = function() {\n    if (this.context.length === 1) {\n      this.exprAllowed = true;\n      return\n    }\n    var out = this.context.pop();\n    if (out === types.b_stat && this.curContext().token === \"function\") {\n      out = this.context.pop();\n    }\n    this.exprAllowed = !out.isExpr;\n  };\n\n  types$1.braceL.updateContext = function(prevType) {\n    this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);\n    this.exprAllowed = true;\n  };\n\n  types$1.dollarBraceL.updateContext = function() {\n    this.context.push(types.b_tmpl);\n    this.exprAllowed = true;\n  };\n\n  types$1.parenL.updateContext = function(prevType) {\n    var statementParens = prevType === types$1._if || prevType === types$1._for || prevType === types$1._with || prevType === types$1._while;\n    this.context.push(statementParens ? types.p_stat : types.p_expr);\n    this.exprAllowed = true;\n  };\n\n  types$1.incDec.updateContext = function() {\n  };\n\n  types$1._function.updateContext = types$1._class.updateContext = function(prevType) {\n    if (prevType.beforeExpr && prevType !== types$1._else &&\n        !(prevType === types$1.semi && this.curContext() !== types.p_stat) &&\n        !(prevType === types$1._return && lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) &&\n        !((prevType === types$1.colon || prevType === types$1.braceL) && this.curContext() === types.b_stat))\n      { this.context.push(types.f_expr); }\n    else\n      { this.context.push(types.f_stat); }\n    this.exprAllowed = false;\n  };\n\n  types$1.colon.updateContext = function() {\n    if (this.curContext().token === \"function\") { this.context.pop(); }\n    this.exprAllowed = true;\n  };\n\n  types$1.backQuote.updateContext = function() {\n    if (this.curContext() === types.q_tmpl)\n      { this.context.pop(); }\n    else\n      { this.context.push(types.q_tmpl); }\n    this.exprAllowed = false;\n  };\n\n  types$1.star.updateContext = function(prevType) {\n    if (prevType === types$1._function) {\n      var index = this.context.length - 1;\n      if (this.context[index] === types.f_expr)\n        { this.context[index] = types.f_expr_gen; }\n      else\n        { this.context[index] = types.f_gen; }\n    }\n    this.exprAllowed = true;\n  };\n\n  types$1.name.updateContext = function(prevType) {\n    var allowed = false;\n    if (this.options.ecmaVersion >= 6 && prevType !== types$1.dot) {\n      if (this.value === \"of\" && !this.exprAllowed ||\n          this.value === \"yield\" && this.inGeneratorContext())\n        { allowed = true; }\n    }\n    this.exprAllowed = allowed;\n  };\n\n\n\n  var pp$5 = Parser.prototype;\n\n\n  pp$5.checkPropClash = function(prop, propHash, refDestructuringErrors) {\n    if (this.options.ecmaVersion >= 9 && prop.type === \"SpreadElement\")\n      { return }\n    if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))\n      { return }\n    var key = prop.key;\n    var name;\n    switch (key.type) {\n    case \"Identifier\": name = key.name; break\n    case \"Literal\": name = String(key.value); break\n    default: return\n    }\n    var kind = prop.kind;\n    if (this.options.ecmaVersion >= 6) {\n      if (name === \"__proto__\" && kind === \"init\") {\n        if (propHash.proto) {\n          if (refDestructuringErrors) {\n            if (refDestructuringErrors.doubleProto < 0) {\n              refDestructuringErrors.doubleProto = key.start;\n            }\n          } else {\n            this.raiseRecoverable(key.start, \"Redefinition of __proto__ property\");\n          }\n        }\n        propHash.proto = true;\n      }\n      return\n    }\n    name = \"$\" + name;\n    var other = propHash[name];\n    if (other) {\n      var redefinition;\n      if (kind === \"init\") {\n        redefinition = this.strict && other.init || other.get || other.set;\n      } else {\n        redefinition = other.init || other[kind];\n      }\n      if (redefinition)\n        { this.raiseRecoverable(key.start, \"Redefinition of property\"); }\n    } else {\n      other = propHash[name] = {\n        init: false,\n        get: false,\n        set: false\n      };\n    }\n    other[kind] = true;\n  };\n\n\n\n\n  pp$5.parseExpression = function(forInit, refDestructuringErrors) {\n    var startPos = this.start, startLoc = this.startLoc;\n    var expr = this.parseMaybeAssign(forInit, refDestructuringErrors);\n    if (this.type === types$1.comma) {\n      var node = this.startNodeAt(startPos, startLoc);\n      node.expressions = [expr];\n      while (this.eat(types$1.comma)) { node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors)); }\n      return this.finishNode(node, \"SequenceExpression\")\n    }\n    return expr\n  };\n\n\n  pp$5.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {\n    if (this.isContextual(\"yield\")) {\n      if (this.inGenerator) { return this.parseYield(forInit) }\n      else { this.exprAllowed = false; }\n    }\n\n    var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1;\n    if (refDestructuringErrors) {\n      oldParenAssign = refDestructuringErrors.parenthesizedAssign;\n      oldTrailingComma = refDestructuringErrors.trailingComma;\n      oldDoubleProto = refDestructuringErrors.doubleProto;\n      refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;\n    } else {\n      refDestructuringErrors = new DestructuringErrors;\n      ownDestructuringErrors = true;\n    }\n\n    var startPos = this.start, startLoc = this.startLoc;\n    if (this.type === types$1.parenL || this.type === types$1.name) {\n      this.potentialArrowAt = this.start;\n      this.potentialArrowInForAwait = forInit === \"await\";\n    }\n    var left = this.parseMaybeConditional(forInit, refDestructuringErrors);\n    if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }\n    if (this.type.isAssign) {\n      var node = this.startNodeAt(startPos, startLoc);\n      node.operator = this.value;\n      if (this.type === types$1.eq)\n        { left = this.toAssignable(left, false, refDestructuringErrors); }\n      if (!ownDestructuringErrors) {\n        refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1;\n      }\n      if (refDestructuringErrors.shorthandAssign >= left.start)\n        { refDestructuringErrors.shorthandAssign = -1; } \n      if (this.type === types$1.eq)\n        { this.checkLValPattern(left); }\n      else\n        { this.checkLValSimple(left); }\n      node.left = left;\n      this.next();\n      node.right = this.parseMaybeAssign(forInit);\n      if (oldDoubleProto > -1) { refDestructuringErrors.doubleProto = oldDoubleProto; }\n      return this.finishNode(node, \"AssignmentExpression\")\n    } else {\n      if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }\n    }\n    if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }\n    if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }\n    return left\n  };\n\n\n  pp$5.parseMaybeConditional = function(forInit, refDestructuringErrors) {\n    var startPos = this.start, startLoc = this.startLoc;\n    var expr = this.parseExprOps(forInit, refDestructuringErrors);\n    if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n    if (this.eat(types$1.question)) {\n      var node = this.startNodeAt(startPos, startLoc);\n      node.test = expr;\n      node.consequent = this.parseMaybeAssign();\n      this.expect(types$1.colon);\n      node.alternate = this.parseMaybeAssign(forInit);\n      return this.finishNode(node, \"ConditionalExpression\")\n    }\n    return expr\n  };\n\n\n  pp$5.parseExprOps = function(forInit, refDestructuringErrors) {\n    var startPos = this.start, startLoc = this.startLoc;\n    var expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit);\n    if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n    return expr.start === startPos && expr.type === \"ArrowFunctionExpression\" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)\n  };\n\n\n  pp$5.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit) {\n    var prec = this.type.binop;\n    if (prec != null && (!forInit || this.type !== types$1._in)) {\n      if (prec > minPrec) {\n        var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;\n        var coalesce = this.type === types$1.coalesce;\n        if (coalesce) {\n          prec = types$1.logicalAND.binop;\n        }\n        var op = this.value;\n        this.next();\n        var startPos = this.start, startLoc = this.startLoc;\n        var right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit);\n        var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);\n        if ((logical && this.type === types$1.coalesce) || (coalesce && (this.type === types$1.logicalOR || this.type === types$1.logicalAND))) {\n          this.raiseRecoverable(this.start, \"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses\");\n        }\n        return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit)\n      }\n    }\n    return left\n  };\n\n  pp$5.buildBinary = function(startPos, startLoc, left, right, op, logical) {\n    if (right.type === \"PrivateIdentifier\") { this.raise(right.start, \"Private identifier can only be left side of binary expression\"); }\n    var node = this.startNodeAt(startPos, startLoc);\n    node.left = left;\n    node.operator = op;\n    node.right = right;\n    return this.finishNode(node, logical ? \"LogicalExpression\" : \"BinaryExpression\")\n  };\n\n\n  pp$5.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {\n    var startPos = this.start, startLoc = this.startLoc, expr;\n    if (this.isContextual(\"await\") && this.canAwait) {\n      expr = this.parseAwait(forInit);\n      sawUnary = true;\n    } else if (this.type.prefix) {\n      var node = this.startNode(), update = this.type === types$1.incDec;\n      node.operator = this.value;\n      node.prefix = true;\n      this.next();\n      node.argument = this.parseMaybeUnary(null, true, update, forInit);\n      this.checkExpressionErrors(refDestructuringErrors, true);\n      if (update) { this.checkLValSimple(node.argument); }\n      else if (this.strict && node.operator === \"delete\" && isLocalVariableAccess(node.argument))\n        { this.raiseRecoverable(node.start, \"Deleting local variable in strict mode\"); }\n      else if (node.operator === \"delete\" && isPrivateFieldAccess(node.argument))\n        { this.raiseRecoverable(node.start, \"Private fields can not be deleted\"); }\n      else { sawUnary = true; }\n      expr = this.finishNode(node, update ? \"UpdateExpression\" : \"UnaryExpression\");\n    } else if (!sawUnary && this.type === types$1.privateId) {\n      if ((forInit || this.privateNameStack.length === 0) && this.options.checkPrivateFields) { this.unexpected(); }\n      expr = this.parsePrivateIdent();\n      if (this.type !== types$1._in) { this.unexpected(); }\n    } else {\n      expr = this.parseExprSubscripts(refDestructuringErrors, forInit);\n      if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n      while (this.type.postfix && !this.canInsertSemicolon()) {\n        var node$1 = this.startNodeAt(startPos, startLoc);\n        node$1.operator = this.value;\n        node$1.prefix = false;\n        node$1.argument = expr;\n        this.checkLValSimple(expr);\n        this.next();\n        expr = this.finishNode(node$1, \"UpdateExpression\");\n      }\n    }\n\n    if (!incDec && this.eat(types$1.starstar)) {\n      if (sawUnary)\n        { this.unexpected(this.lastTokStart); }\n      else\n        { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), \"**\", false) }\n    } else {\n      return expr\n    }\n  };\n\n  function isLocalVariableAccess(node) {\n    return (\n      node.type === \"Identifier\" ||\n      node.type === \"ParenthesizedExpression\" && isLocalVariableAccess(node.expression)\n    )\n  }\n\n  function isPrivateFieldAccess(node) {\n    return (\n      node.type === \"MemberExpression\" && node.property.type === \"PrivateIdentifier\" ||\n      node.type === \"ChainExpression\" && isPrivateFieldAccess(node.expression) ||\n      node.type === \"ParenthesizedExpression\" && isPrivateFieldAccess(node.expression)\n    )\n  }\n\n\n  pp$5.parseExprSubscripts = function(refDestructuringErrors, forInit) {\n    var startPos = this.start, startLoc = this.startLoc;\n    var expr = this.parseExprAtom(refDestructuringErrors, forInit);\n    if (expr.type === \"ArrowFunctionExpression\" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== \")\")\n      { return expr }\n    var result = this.parseSubscripts(expr, startPos, startLoc, false, forInit);\n    if (refDestructuringErrors && result.type === \"MemberExpression\") {\n      if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }\n      if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }\n      if (refDestructuringErrors.trailingComma >= result.start) { refDestructuringErrors.trailingComma = -1; }\n    }\n    return result\n  };\n\n  pp$5.parseSubscripts = function(base, startPos, startLoc, noCalls, forInit) {\n    var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === \"Identifier\" && base.name === \"async\" &&\n        this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 &&\n        this.potentialArrowAt === base.start;\n    var optionalChained = false;\n\n    while (true) {\n      var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit);\n\n      if (element.optional) { optionalChained = true; }\n      if (element === base || element.type === \"ArrowFunctionExpression\") {\n        if (optionalChained) {\n          var chainNode = this.startNodeAt(startPos, startLoc);\n          chainNode.expression = element;\n          element = this.finishNode(chainNode, \"ChainExpression\");\n        }\n        return element\n      }\n\n      base = element;\n    }\n  };\n\n  pp$5.shouldParseAsyncArrow = function() {\n    return !this.canInsertSemicolon() && this.eat(types$1.arrow)\n  };\n\n  pp$5.parseSubscriptAsyncArrow = function(startPos, startLoc, exprList, forInit) {\n    return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true, forInit)\n  };\n\n  pp$5.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {\n    var optionalSupported = this.options.ecmaVersion >= 11;\n    var optional = optionalSupported && this.eat(types$1.questionDot);\n    if (noCalls && optional) { this.raise(this.lastTokStart, \"Optional chaining cannot appear in the callee of new expressions\"); }\n\n    var computed = this.eat(types$1.bracketL);\n    if (computed || (optional && this.type !== types$1.parenL && this.type !== types$1.backQuote) || this.eat(types$1.dot)) {\n      var node = this.startNodeAt(startPos, startLoc);\n      node.object = base;\n      if (computed) {\n        node.property = this.parseExpression();\n        this.expect(types$1.bracketR);\n      } else if (this.type === types$1.privateId && base.type !== \"Super\") {\n        node.property = this.parsePrivateIdent();\n      } else {\n        node.property = this.parseIdent(this.options.allowReserved !== \"never\");\n      }\n      node.computed = !!computed;\n      if (optionalSupported) {\n        node.optional = optional;\n      }\n      base = this.finishNode(node, \"MemberExpression\");\n    } else if (!noCalls && this.eat(types$1.parenL)) {\n      var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n      this.yieldPos = 0;\n      this.awaitPos = 0;\n      this.awaitIdentPos = 0;\n      var exprList = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);\n      if (maybeAsyncArrow && !optional && this.shouldParseAsyncArrow()) {\n        this.checkPatternErrors(refDestructuringErrors, false);\n        this.checkYieldAwaitInDefaultParams();\n        if (this.awaitIdentPos > 0)\n          { this.raise(this.awaitIdentPos, \"Cannot use 'await' as identifier inside an async function\"); }\n        this.yieldPos = oldYieldPos;\n        this.awaitPos = oldAwaitPos;\n        this.awaitIdentPos = oldAwaitIdentPos;\n        return this.parseSubscriptAsyncArrow(startPos, startLoc, exprList, forInit)\n      }\n      this.checkExpressionErrors(refDestructuringErrors, true);\n      this.yieldPos = oldYieldPos || this.yieldPos;\n      this.awaitPos = oldAwaitPos || this.awaitPos;\n      this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;\n      var node$1 = this.startNodeAt(startPos, startLoc);\n      node$1.callee = base;\n      node$1.arguments = exprList;\n      if (optionalSupported) {\n        node$1.optional = optional;\n      }\n      base = this.finishNode(node$1, \"CallExpression\");\n    } else if (this.type === types$1.backQuote) {\n      if (optional || optionalChained) {\n        this.raise(this.start, \"Optional chaining cannot appear in the tag of tagged template expressions\");\n      }\n      var node$2 = this.startNodeAt(startPos, startLoc);\n      node$2.tag = base;\n      node$2.quasi = this.parseTemplate({isTagged: true});\n      base = this.finishNode(node$2, \"TaggedTemplateExpression\");\n    }\n    return base\n  };\n\n\n  pp$5.parseExprAtom = function(refDestructuringErrors, forInit, forNew) {\n    if (this.type === types$1.slash) { this.readRegexp(); }\n\n    var node, canBeArrow = this.potentialArrowAt === this.start;\n    switch (this.type) {\n    case types$1._super:\n      if (!this.allowSuper)\n        { this.raise(this.start, \"'super' keyword outside a method\"); }\n      node = this.startNode();\n      this.next();\n      if (this.type === types$1.parenL && !this.allowDirectSuper)\n        { this.raise(node.start, \"super() call outside constructor of a subclass\"); }\n      if (this.type !== types$1.dot && this.type !== types$1.bracketL && this.type !== types$1.parenL)\n        { this.unexpected(); }\n      return this.finishNode(node, \"Super\")\n\n    case types$1._this:\n      node = this.startNode();\n      this.next();\n      return this.finishNode(node, \"ThisExpression\")\n\n    case types$1.name:\n      var startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc;\n      var id = this.parseIdent(false);\n      if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === \"async\" && !this.canInsertSemicolon() && this.eat(types$1._function)) {\n        this.overrideContext(types.f_expr);\n        return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true, forInit)\n      }\n      if (canBeArrow && !this.canInsertSemicolon()) {\n        if (this.eat(types$1.arrow))\n          { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false, forInit) }\n        if (this.options.ecmaVersion >= 8 && id.name === \"async\" && this.type === types$1.name && !containsEsc &&\n            (!this.potentialArrowInForAwait || this.value !== \"of\" || this.containsEsc)) {\n          id = this.parseIdent(false);\n          if (this.canInsertSemicolon() || !this.eat(types$1.arrow))\n            { this.unexpected(); }\n          return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true, forInit)\n        }\n      }\n      return id\n\n    case types$1.regexp:\n      var value = this.value;\n      node = this.parseLiteral(value.value);\n      node.regex = {pattern: value.pattern, flags: value.flags};\n      return node\n\n    case types$1.num: case types$1.string:\n      return this.parseLiteral(this.value)\n\n    case types$1._null: case types$1._true: case types$1._false:\n      node = this.startNode();\n      node.value = this.type === types$1._null ? null : this.type === types$1._true;\n      node.raw = this.type.keyword;\n      this.next();\n      return this.finishNode(node, \"Literal\")\n\n    case types$1.parenL:\n      var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow, forInit);\n      if (refDestructuringErrors) {\n        if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))\n          { refDestructuringErrors.parenthesizedAssign = start; }\n        if (refDestructuringErrors.parenthesizedBind < 0)\n          { refDestructuringErrors.parenthesizedBind = start; }\n      }\n      return expr\n\n    case types$1.bracketL:\n      node = this.startNode();\n      this.next();\n      node.elements = this.parseExprList(types$1.bracketR, true, true, refDestructuringErrors);\n      return this.finishNode(node, \"ArrayExpression\")\n\n    case types$1.braceL:\n      this.overrideContext(types.b_expr);\n      return this.parseObj(false, refDestructuringErrors)\n\n    case types$1._function:\n      node = this.startNode();\n      this.next();\n      return this.parseFunction(node, 0)\n\n    case types$1._class:\n      return this.parseClass(this.startNode(), false)\n\n    case types$1._new:\n      return this.parseNew()\n\n    case types$1.backQuote:\n      return this.parseTemplate()\n\n    case types$1._import:\n      if (this.options.ecmaVersion >= 11) {\n        return this.parseExprImport(forNew)\n      } else {\n        return this.unexpected()\n      }\n\n    default:\n      return this.parseExprAtomDefault()\n    }\n  };\n\n  pp$5.parseExprAtomDefault = function() {\n    this.unexpected();\n  };\n\n  pp$5.parseExprImport = function(forNew) {\n    var node = this.startNode();\n\n    if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword import\"); }\n    this.next();\n\n    if (this.type === types$1.parenL && !forNew) {\n      return this.parseDynamicImport(node)\n    } else if (this.type === types$1.dot) {\n      var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n      meta.name = \"import\";\n      node.meta = this.finishNode(meta, \"Identifier\");\n      return this.parseImportMeta(node)\n    } else {\n      this.unexpected();\n    }\n  };\n\n  pp$5.parseDynamicImport = function(node) {\n    this.next(); \n\n    node.source = this.parseMaybeAssign();\n\n    if (this.options.ecmaVersion >= 16) {\n      if (!this.eat(types$1.parenR)) {\n        this.expect(types$1.comma);\n        if (!this.afterTrailingComma(types$1.parenR)) {\n          node.options = this.parseMaybeAssign();\n          if (!this.eat(types$1.parenR)) {\n            this.expect(types$1.comma);\n            if (!this.afterTrailingComma(types$1.parenR)) {\n              this.unexpected();\n            }\n          }\n        } else {\n          node.options = null;\n        }\n      } else {\n        node.options = null;\n      }\n    } else {\n      if (!this.eat(types$1.parenR)) {\n        var errorPos = this.start;\n        if (this.eat(types$1.comma) && this.eat(types$1.parenR)) {\n          this.raiseRecoverable(errorPos, \"Trailing comma is not allowed in import()\");\n        } else {\n          this.unexpected(errorPos);\n        }\n      }\n    }\n\n    return this.finishNode(node, \"ImportExpression\")\n  };\n\n  pp$5.parseImportMeta = function(node) {\n    this.next(); \n\n    var containsEsc = this.containsEsc;\n    node.property = this.parseIdent(true);\n\n    if (node.property.name !== \"meta\")\n      { this.raiseRecoverable(node.property.start, \"The only valid meta property for import is 'import.meta'\"); }\n    if (containsEsc)\n      { this.raiseRecoverable(node.start, \"'import.meta' must not contain escaped characters\"); }\n    if (this.options.sourceType !== \"module\" && !this.options.allowImportExportEverywhere)\n      { this.raiseRecoverable(node.start, \"Cannot use 'import.meta' outside a module\"); }\n\n    return this.finishNode(node, \"MetaProperty\")\n  };\n\n  pp$5.parseLiteral = function(value) {\n    var node = this.startNode();\n    node.value = value;\n    node.raw = this.input.slice(this.start, this.end);\n    if (node.raw.charCodeAt(node.raw.length - 1) === 110) { node.bigint = node.raw.slice(0, -1).replace(/_/g, \"\"); }\n    this.next();\n    return this.finishNode(node, \"Literal\")\n  };\n\n  pp$5.parseParenExpression = function() {\n    this.expect(types$1.parenL);\n    var val = this.parseExpression();\n    this.expect(types$1.parenR);\n    return val\n  };\n\n  pp$5.shouldParseArrow = function(exprList) {\n    return !this.canInsertSemicolon()\n  };\n\n  pp$5.parseParenAndDistinguishExpression = function(canBeArrow, forInit) {\n    var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;\n    if (this.options.ecmaVersion >= 6) {\n      this.next();\n\n      var innerStartPos = this.start, innerStartLoc = this.startLoc;\n      var exprList = [], first = true, lastIsComma = false;\n      var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart;\n      this.yieldPos = 0;\n      this.awaitPos = 0;\n      while (this.type !== types$1.parenR) {\n        first ? first = false : this.expect(types$1.comma);\n        if (allowTrailingComma && this.afterTrailingComma(types$1.parenR, true)) {\n          lastIsComma = true;\n          break\n        } else if (this.type === types$1.ellipsis) {\n          spreadStart = this.start;\n          exprList.push(this.parseParenItem(this.parseRestBinding()));\n          if (this.type === types$1.comma) {\n            this.raiseRecoverable(\n              this.start,\n              \"Comma is not permitted after the rest element\"\n            );\n          }\n          break\n        } else {\n          exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));\n        }\n      }\n      var innerEndPos = this.lastTokEnd, innerEndLoc = this.lastTokEndLoc;\n      this.expect(types$1.parenR);\n\n      if (canBeArrow && this.shouldParseArrow(exprList) && this.eat(types$1.arrow)) {\n        this.checkPatternErrors(refDestructuringErrors, false);\n        this.checkYieldAwaitInDefaultParams();\n        this.yieldPos = oldYieldPos;\n        this.awaitPos = oldAwaitPos;\n        return this.parseParenArrowList(startPos, startLoc, exprList, forInit)\n      }\n\n      if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }\n      if (spreadStart) { this.unexpected(spreadStart); }\n      this.checkExpressionErrors(refDestructuringErrors, true);\n      this.yieldPos = oldYieldPos || this.yieldPos;\n      this.awaitPos = oldAwaitPos || this.awaitPos;\n\n      if (exprList.length > 1) {\n        val = this.startNodeAt(innerStartPos, innerStartLoc);\n        val.expressions = exprList;\n        this.finishNodeAt(val, \"SequenceExpression\", innerEndPos, innerEndLoc);\n      } else {\n        val = exprList[0];\n      }\n    } else {\n      val = this.parseParenExpression();\n    }\n\n    if (this.options.preserveParens) {\n      var par = this.startNodeAt(startPos, startLoc);\n      par.expression = val;\n      return this.finishNode(par, \"ParenthesizedExpression\")\n    } else {\n      return val\n    }\n  };\n\n  pp$5.parseParenItem = function(item) {\n    return item\n  };\n\n  pp$5.parseParenArrowList = function(startPos, startLoc, exprList, forInit) {\n    return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false, forInit)\n  };\n\n\n  var empty = [];\n\n  pp$5.parseNew = function() {\n    if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword new\"); }\n    var node = this.startNode();\n    this.next();\n    if (this.options.ecmaVersion >= 6 && this.type === types$1.dot) {\n      var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n      meta.name = \"new\";\n      node.meta = this.finishNode(meta, \"Identifier\");\n      this.next();\n      var containsEsc = this.containsEsc;\n      node.property = this.parseIdent(true);\n      if (node.property.name !== \"target\")\n        { this.raiseRecoverable(node.property.start, \"The only valid meta property for new is 'new.target'\"); }\n      if (containsEsc)\n        { this.raiseRecoverable(node.start, \"'new.target' must not contain escaped characters\"); }\n      if (!this.allowNewDotTarget)\n        { this.raiseRecoverable(node.start, \"'new.target' can only be used in functions and class static block\"); }\n      return this.finishNode(node, \"MetaProperty\")\n    }\n    var startPos = this.start, startLoc = this.startLoc;\n    node.callee = this.parseSubscripts(this.parseExprAtom(null, false, true), startPos, startLoc, true, false);\n    if (this.eat(types$1.parenL)) { node.arguments = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false); }\n    else { node.arguments = empty; }\n    return this.finishNode(node, \"NewExpression\")\n  };\n\n\n  pp$5.parseTemplateElement = function(ref) {\n    var isTagged = ref.isTagged;\n\n    var elem = this.startNode();\n    if (this.type === types$1.invalidTemplate) {\n      if (!isTagged) {\n        this.raiseRecoverable(this.start, \"Bad escape sequence in untagged template literal\");\n      }\n      elem.value = {\n        raw: this.value.replace(/\\r\\n?/g, \"\\n\"),\n        cooked: null\n      };\n    } else {\n      elem.value = {\n        raw: this.input.slice(this.start, this.end).replace(/\\r\\n?/g, \"\\n\"),\n        cooked: this.value\n      };\n    }\n    this.next();\n    elem.tail = this.type === types$1.backQuote;\n    return this.finishNode(elem, \"TemplateElement\")\n  };\n\n  pp$5.parseTemplate = function(ref) {\n    if ( ref === void 0 ) ref = {};\n    var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;\n\n    var node = this.startNode();\n    this.next();\n    node.expressions = [];\n    var curElt = this.parseTemplateElement({isTagged: isTagged});\n    node.quasis = [curElt];\n    while (!curElt.tail) {\n      if (this.type === types$1.eof) { this.raise(this.pos, \"Unterminated template literal\"); }\n      this.expect(types$1.dollarBraceL);\n      node.expressions.push(this.parseExpression());\n      this.expect(types$1.braceR);\n      node.quasis.push(curElt = this.parseTemplateElement({isTagged: isTagged}));\n    }\n    this.next();\n    return this.finishNode(node, \"TemplateLiteral\")\n  };\n\n  pp$5.isAsyncProp = function(prop) {\n    return !prop.computed && prop.key.type === \"Identifier\" && prop.key.name === \"async\" &&\n      (this.type === types$1.name || this.type === types$1.num || this.type === types$1.string || this.type === types$1.bracketL || this.type.keyword || (this.options.ecmaVersion >= 9 && this.type === types$1.star)) &&\n      !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n  };\n\n\n  pp$5.parseObj = function(isPattern, refDestructuringErrors) {\n    var node = this.startNode(), first = true, propHash = {};\n    node.properties = [];\n    this.next();\n    while (!this.eat(types$1.braceR)) {\n      if (!first) {\n        this.expect(types$1.comma);\n        if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types$1.braceR)) { break }\n      } else { first = false; }\n\n      var prop = this.parseProperty(isPattern, refDestructuringErrors);\n      if (!isPattern) { this.checkPropClash(prop, propHash, refDestructuringErrors); }\n      node.properties.push(prop);\n    }\n    return this.finishNode(node, isPattern ? \"ObjectPattern\" : \"ObjectExpression\")\n  };\n\n  pp$5.parseProperty = function(isPattern, refDestructuringErrors) {\n    var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;\n    if (this.options.ecmaVersion >= 9 && this.eat(types$1.ellipsis)) {\n      if (isPattern) {\n        prop.argument = this.parseIdent(false);\n        if (this.type === types$1.comma) {\n          this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\");\n        }\n        return this.finishNode(prop, \"RestElement\")\n      }\n      prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n      if (this.type === types$1.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {\n        refDestructuringErrors.trailingComma = this.start;\n      }\n      return this.finishNode(prop, \"SpreadElement\")\n    }\n    if (this.options.ecmaVersion >= 6) {\n      prop.method = false;\n      prop.shorthand = false;\n      if (isPattern || refDestructuringErrors) {\n        startPos = this.start;\n        startLoc = this.startLoc;\n      }\n      if (!isPattern)\n        { isGenerator = this.eat(types$1.star); }\n    }\n    var containsEsc = this.containsEsc;\n    this.parsePropertyName(prop);\n    if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {\n      isAsync = true;\n      isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);\n      this.parsePropertyName(prop);\n    } else {\n      isAsync = false;\n    }\n    this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);\n    return this.finishNode(prop, \"Property\")\n  };\n\n  pp$5.parseGetterSetter = function(prop) {\n    prop.kind = prop.key.name;\n    this.parsePropertyName(prop);\n    prop.value = this.parseMethod(false);\n    var paramCount = prop.kind === \"get\" ? 0 : 1;\n    if (prop.value.params.length !== paramCount) {\n      var start = prop.value.start;\n      if (prop.kind === \"get\")\n        { this.raiseRecoverable(start, \"getter should have no params\"); }\n      else\n        { this.raiseRecoverable(start, \"setter should have exactly one param\"); }\n    } else {\n      if (prop.kind === \"set\" && prop.value.params[0].type === \"RestElement\")\n        { this.raiseRecoverable(prop.value.params[0].start, \"Setter cannot use rest params\"); }\n    }\n  };\n\n  pp$5.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {\n    if ((isGenerator || isAsync) && this.type === types$1.colon)\n      { this.unexpected(); }\n\n    if (this.eat(types$1.colon)) {\n      prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);\n      prop.kind = \"init\";\n    } else if (this.options.ecmaVersion >= 6 && this.type === types$1.parenL) {\n      if (isPattern) { this.unexpected(); }\n      prop.kind = \"init\";\n      prop.method = true;\n      prop.value = this.parseMethod(isGenerator, isAsync);\n    } else if (!isPattern && !containsEsc &&\n               this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === \"Identifier\" &&\n               (prop.key.name === \"get\" || prop.key.name === \"set\") &&\n               (this.type !== types$1.comma && this.type !== types$1.braceR && this.type !== types$1.eq)) {\n      if (isGenerator || isAsync) { this.unexpected(); }\n      this.parseGetterSetter(prop);\n    } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === \"Identifier\") {\n      if (isGenerator || isAsync) { this.unexpected(); }\n      this.checkUnreserved(prop.key);\n      if (prop.key.name === \"await\" && !this.awaitIdentPos)\n        { this.awaitIdentPos = startPos; }\n      prop.kind = \"init\";\n      if (isPattern) {\n        prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n      } else if (this.type === types$1.eq && refDestructuringErrors) {\n        if (refDestructuringErrors.shorthandAssign < 0)\n          { refDestructuringErrors.shorthandAssign = this.start; }\n        prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n      } else {\n        prop.value = this.copyNode(prop.key);\n      }\n      prop.shorthand = true;\n    } else { this.unexpected(); }\n  };\n\n  pp$5.parsePropertyName = function(prop) {\n    if (this.options.ecmaVersion >= 6) {\n      if (this.eat(types$1.bracketL)) {\n        prop.computed = true;\n        prop.key = this.parseMaybeAssign();\n        this.expect(types$1.bracketR);\n        return prop.key\n      } else {\n        prop.computed = false;\n      }\n    }\n    return prop.key = this.type === types$1.num || this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\")\n  };\n\n\n  pp$5.initFunction = function(node) {\n    node.id = null;\n    if (this.options.ecmaVersion >= 6) { node.generator = node.expression = false; }\n    if (this.options.ecmaVersion >= 8) { node.async = false; }\n  };\n\n\n  pp$5.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {\n    var node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n    this.initFunction(node);\n    if (this.options.ecmaVersion >= 6)\n      { node.generator = isGenerator; }\n    if (this.options.ecmaVersion >= 8)\n      { node.async = !!isAsync; }\n\n    this.yieldPos = 0;\n    this.awaitPos = 0;\n    this.awaitIdentPos = 0;\n    this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0));\n\n    this.expect(types$1.parenL);\n    node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n    this.checkYieldAwaitInDefaultParams();\n    this.parseFunctionBody(node, false, true, false);\n\n    this.yieldPos = oldYieldPos;\n    this.awaitPos = oldAwaitPos;\n    this.awaitIdentPos = oldAwaitIdentPos;\n    return this.finishNode(node, \"FunctionExpression\")\n  };\n\n\n  pp$5.parseArrowExpression = function(node, params, isAsync, forInit) {\n    var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n    this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW);\n    this.initFunction(node);\n    if (this.options.ecmaVersion >= 8) { node.async = !!isAsync; }\n\n    this.yieldPos = 0;\n    this.awaitPos = 0;\n    this.awaitIdentPos = 0;\n\n    node.params = this.toAssignableList(params, true);\n    this.parseFunctionBody(node, true, false, forInit);\n\n    this.yieldPos = oldYieldPos;\n    this.awaitPos = oldAwaitPos;\n    this.awaitIdentPos = oldAwaitIdentPos;\n    return this.finishNode(node, \"ArrowFunctionExpression\")\n  };\n\n\n  pp$5.parseFunctionBody = function(node, isArrowFunction, isMethod, forInit) {\n    var isExpression = isArrowFunction && this.type !== types$1.braceL;\n    var oldStrict = this.strict, useStrict = false;\n\n    if (isExpression) {\n      node.body = this.parseMaybeAssign(forInit);\n      node.expression = true;\n      this.checkParams(node, false);\n    } else {\n      var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);\n      if (!oldStrict || nonSimple) {\n        useStrict = this.strictDirective(this.end);\n        if (useStrict && nonSimple)\n          { this.raiseRecoverable(node.start, \"Illegal 'use strict' directive in function with non-simple parameter list\"); }\n      }\n      var oldLabels = this.labels;\n      this.labels = [];\n      if (useStrict) { this.strict = true; }\n\n      this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));\n      if (this.strict && node.id) { this.checkLValSimple(node.id, BIND_OUTSIDE); }\n      node.body = this.parseBlock(false, undefined, useStrict && !oldStrict);\n      node.expression = false;\n      this.adaptDirectivePrologue(node.body.body);\n      this.labels = oldLabels;\n    }\n    this.exitScope();\n  };\n\n  pp$5.isSimpleParamList = function(params) {\n    for (var i = 0, list = params; i < list.length; i += 1)\n      {\n      var param = list[i];\n\n      if (param.type !== \"Identifier\") { return false\n    } }\n    return true\n  };\n\n\n  pp$5.checkParams = function(node, allowDuplicates) {\n    var nameHash = Object.create(null);\n    for (var i = 0, list = node.params; i < list.length; i += 1)\n      {\n      var param = list[i];\n\n      this.checkLValInnerPattern(param, BIND_VAR, allowDuplicates ? null : nameHash);\n    }\n  };\n\n\n  pp$5.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {\n    var elts = [], first = true;\n    while (!this.eat(close)) {\n      if (!first) {\n        this.expect(types$1.comma);\n        if (allowTrailingComma && this.afterTrailingComma(close)) { break }\n      } else { first = false; }\n\n      var elt = (void 0);\n      if (allowEmpty && this.type === types$1.comma)\n        { elt = null; }\n      else if (this.type === types$1.ellipsis) {\n        elt = this.parseSpread(refDestructuringErrors);\n        if (refDestructuringErrors && this.type === types$1.comma && refDestructuringErrors.trailingComma < 0)\n          { refDestructuringErrors.trailingComma = this.start; }\n      } else {\n        elt = this.parseMaybeAssign(false, refDestructuringErrors);\n      }\n      elts.push(elt);\n    }\n    return elts\n  };\n\n  pp$5.checkUnreserved = function(ref) {\n    var start = ref.start;\n    var end = ref.end;\n    var name = ref.name;\n\n    if (this.inGenerator && name === \"yield\")\n      { this.raiseRecoverable(start, \"Cannot use 'yield' as identifier inside a generator\"); }\n    if (this.inAsync && name === \"await\")\n      { this.raiseRecoverable(start, \"Cannot use 'await' as identifier inside an async function\"); }\n    if (this.currentThisScope().inClassFieldInit && name === \"arguments\")\n      { this.raiseRecoverable(start, \"Cannot use 'arguments' in class field initializer\"); }\n    if (this.inClassStaticBlock && (name === \"arguments\" || name === \"await\"))\n      { this.raise(start, (\"Cannot use \" + name + \" in class static initialization block\")); }\n    if (this.keywords.test(name))\n      { this.raise(start, (\"Unexpected keyword '\" + name + \"'\")); }\n    if (this.options.ecmaVersion < 6 &&\n      this.input.slice(start, end).indexOf(\"\\\\\") !== -1) { return }\n    var re = this.strict ? this.reservedWordsStrict : this.reservedWords;\n    if (re.test(name)) {\n      if (!this.inAsync && name === \"await\")\n        { this.raiseRecoverable(start, \"Cannot use keyword 'await' outside an async function\"); }\n      this.raiseRecoverable(start, (\"The keyword '\" + name + \"' is reserved\"));\n    }\n  };\n\n\n  pp$5.parseIdent = function(liberal) {\n    var node = this.parseIdentNode();\n    this.next(!!liberal);\n    this.finishNode(node, \"Identifier\");\n    if (!liberal) {\n      this.checkUnreserved(node);\n      if (node.name === \"await\" && !this.awaitIdentPos)\n        { this.awaitIdentPos = node.start; }\n    }\n    return node\n  };\n\n  pp$5.parseIdentNode = function() {\n    var node = this.startNode();\n    if (this.type === types$1.name) {\n      node.name = this.value;\n    } else if (this.type.keyword) {\n      node.name = this.type.keyword;\n\n      if ((node.name === \"class\" || node.name === \"function\") &&\n        (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {\n        this.context.pop();\n      }\n      this.type = types$1.name;\n    } else {\n      this.unexpected();\n    }\n    return node\n  };\n\n  pp$5.parsePrivateIdent = function() {\n    var node = this.startNode();\n    if (this.type === types$1.privateId) {\n      node.name = this.value;\n    } else {\n      this.unexpected();\n    }\n    this.next();\n    this.finishNode(node, \"PrivateIdentifier\");\n\n    if (this.options.checkPrivateFields) {\n      if (this.privateNameStack.length === 0) {\n        this.raise(node.start, (\"Private field '#\" + (node.name) + \"' must be declared in an enclosing class\"));\n      } else {\n        this.privateNameStack[this.privateNameStack.length - 1].used.push(node);\n      }\n    }\n\n    return node\n  };\n\n\n  pp$5.parseYield = function(forInit) {\n    if (!this.yieldPos) { this.yieldPos = this.start; }\n\n    var node = this.startNode();\n    this.next();\n    if (this.type === types$1.semi || this.canInsertSemicolon() || (this.type !== types$1.star && !this.type.startsExpr)) {\n      node.delegate = false;\n      node.argument = null;\n    } else {\n      node.delegate = this.eat(types$1.star);\n      node.argument = this.parseMaybeAssign(forInit);\n    }\n    return this.finishNode(node, \"YieldExpression\")\n  };\n\n  pp$5.parseAwait = function(forInit) {\n    if (!this.awaitPos) { this.awaitPos = this.start; }\n\n    var node = this.startNode();\n    this.next();\n    node.argument = this.parseMaybeUnary(null, true, false, forInit);\n    return this.finishNode(node, \"AwaitExpression\")\n  };\n\n  var pp$4 = Parser.prototype;\n\n\n  pp$4.raise = function(pos, message) {\n    var loc = getLineInfo(this.input, pos);\n    message += \" (\" + loc.line + \":\" + loc.column + \")\";\n    var err = new SyntaxError(message);\n    err.pos = pos; err.loc = loc; err.raisedAt = this.pos;\n    throw err\n  };\n\n  pp$4.raiseRecoverable = pp$4.raise;\n\n  pp$4.curPosition = function() {\n    if (this.options.locations) {\n      return new Position(this.curLine, this.pos - this.lineStart)\n    }\n  };\n\n  var pp$3 = Parser.prototype;\n\n  var Scope = function Scope(flags) {\n    this.flags = flags;\n    this.var = [];\n    this.lexical = [];\n    this.functions = [];\n    this.inClassFieldInit = false;\n  };\n\n\n  pp$3.enterScope = function(flags) {\n    this.scopeStack.push(new Scope(flags));\n  };\n\n  pp$3.exitScope = function() {\n    this.scopeStack.pop();\n  };\n\n  pp$3.treatFunctionsAsVarInScope = function(scope) {\n    return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)\n  };\n\n  pp$3.declareName = function(name, bindingType, pos) {\n    var redeclared = false;\n    if (bindingType === BIND_LEXICAL) {\n      var scope = this.currentScope();\n      redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1;\n      scope.lexical.push(name);\n      if (this.inModule && (scope.flags & SCOPE_TOP))\n        { delete this.undefinedExports[name]; }\n    } else if (bindingType === BIND_SIMPLE_CATCH) {\n      var scope$1 = this.currentScope();\n      scope$1.lexical.push(name);\n    } else if (bindingType === BIND_FUNCTION) {\n      var scope$2 = this.currentScope();\n      if (this.treatFunctionsAsVar)\n        { redeclared = scope$2.lexical.indexOf(name) > -1; }\n      else\n        { redeclared = scope$2.lexical.indexOf(name) > -1 || scope$2.var.indexOf(name) > -1; }\n      scope$2.functions.push(name);\n    } else {\n      for (var i = this.scopeStack.length - 1; i >= 0; --i) {\n        var scope$3 = this.scopeStack[i];\n        if (scope$3.lexical.indexOf(name) > -1 && !((scope$3.flags & SCOPE_SIMPLE_CATCH) && scope$3.lexical[0] === name) ||\n            !this.treatFunctionsAsVarInScope(scope$3) && scope$3.functions.indexOf(name) > -1) {\n          redeclared = true;\n          break\n        }\n        scope$3.var.push(name);\n        if (this.inModule && (scope$3.flags & SCOPE_TOP))\n          { delete this.undefinedExports[name]; }\n        if (scope$3.flags & SCOPE_VAR) { break }\n      }\n    }\n    if (redeclared) { this.raiseRecoverable(pos, (\"Identifier '\" + name + \"' has already been declared\")); }\n  };\n\n  pp$3.checkLocalExport = function(id) {\n    if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&\n        this.scopeStack[0].var.indexOf(id.name) === -1) {\n      this.undefinedExports[id.name] = id;\n    }\n  };\n\n  pp$3.currentScope = function() {\n    return this.scopeStack[this.scopeStack.length - 1]\n  };\n\n  pp$3.currentVarScope = function() {\n    for (var i = this.scopeStack.length - 1;; i--) {\n      var scope = this.scopeStack[i];\n      if (scope.flags & SCOPE_VAR) { return scope }\n    }\n  };\n\n  pp$3.currentThisScope = function() {\n    for (var i = this.scopeStack.length - 1;; i--) {\n      var scope = this.scopeStack[i];\n      if (scope.flags & SCOPE_VAR && !(scope.flags & SCOPE_ARROW)) { return scope }\n    }\n  };\n\n  var Node = function Node(parser, pos, loc) {\n    this.type = \"\";\n    this.start = pos;\n    this.end = 0;\n    if (parser.options.locations)\n      { this.loc = new SourceLocation(parser, loc); }\n    if (parser.options.directSourceFile)\n      { this.sourceFile = parser.options.directSourceFile; }\n    if (parser.options.ranges)\n      { this.range = [pos, 0]; }\n  };\n\n\n  var pp$2 = Parser.prototype;\n\n  pp$2.startNode = function() {\n    return new Node(this, this.start, this.startLoc)\n  };\n\n  pp$2.startNodeAt = function(pos, loc) {\n    return new Node(this, pos, loc)\n  };\n\n\n  function finishNodeAt(node, type, pos, loc) {\n    node.type = type;\n    node.end = pos;\n    if (this.options.locations)\n      { node.loc.end = loc; }\n    if (this.options.ranges)\n      { node.range[1] = pos; }\n    return node\n  }\n\n  pp$2.finishNode = function(node, type) {\n    return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)\n  };\n\n\n  pp$2.finishNodeAt = function(node, type, pos, loc) {\n    return finishNodeAt.call(this, node, type, pos, loc)\n  };\n\n  pp$2.copyNode = function(node) {\n    var newNode = new Node(this, node.start, this.startLoc);\n    for (var prop in node) { newNode[prop] = node[prop]; }\n    return newNode\n  };\n\n  var scriptValuesAddedInUnicode = \"Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sunu Sunuwar Todhri Todr Tulu_Tigalari Tutg Unknown Zzzz\";\n\n\n  var ecma9BinaryProperties = \"ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS\";\n  var ecma10BinaryProperties = ecma9BinaryProperties + \" Extended_Pictographic\";\n  var ecma11BinaryProperties = ecma10BinaryProperties;\n  var ecma12BinaryProperties = ecma11BinaryProperties + \" EBase EComp EMod EPres ExtPict\";\n  var ecma13BinaryProperties = ecma12BinaryProperties;\n  var ecma14BinaryProperties = ecma13BinaryProperties;\n\n  var unicodeBinaryProperties = {\n    9: ecma9BinaryProperties,\n    10: ecma10BinaryProperties,\n    11: ecma11BinaryProperties,\n    12: ecma12BinaryProperties,\n    13: ecma13BinaryProperties,\n    14: ecma14BinaryProperties\n  };\n\n  var ecma14BinaryPropertiesOfStrings = \"Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji\";\n\n  var unicodeBinaryPropertiesOfStrings = {\n    9: \"\",\n    10: \"\",\n    11: \"\",\n    12: \"\",\n    13: \"\",\n    14: ecma14BinaryPropertiesOfStrings\n  };\n\n  var unicodeGeneralCategoryValues = \"Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu\";\n\n  var ecma9ScriptValues = \"Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb\";\n  var ecma10ScriptValues = ecma9ScriptValues + \" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd\";\n  var ecma11ScriptValues = ecma10ScriptValues + \" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho\";\n  var ecma12ScriptValues = ecma11ScriptValues + \" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi\";\n  var ecma13ScriptValues = ecma12ScriptValues + \" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith\";\n  var ecma14ScriptValues = ecma13ScriptValues + \" \" + scriptValuesAddedInUnicode;\n\n  var unicodeScriptValues = {\n    9: ecma9ScriptValues,\n    10: ecma10ScriptValues,\n    11: ecma11ScriptValues,\n    12: ecma12ScriptValues,\n    13: ecma13ScriptValues,\n    14: ecma14ScriptValues\n  };\n\n  var data = {};\n  function buildUnicodeData(ecmaVersion) {\n    var d = data[ecmaVersion] = {\n      binary: wordsRegexp(unicodeBinaryProperties[ecmaVersion] + \" \" + unicodeGeneralCategoryValues),\n      binaryOfStrings: wordsRegexp(unicodeBinaryPropertiesOfStrings[ecmaVersion]),\n      nonBinary: {\n        General_Category: wordsRegexp(unicodeGeneralCategoryValues),\n        Script: wordsRegexp(unicodeScriptValues[ecmaVersion])\n      }\n    };\n    d.nonBinary.Script_Extensions = d.nonBinary.Script;\n\n    d.nonBinary.gc = d.nonBinary.General_Category;\n    d.nonBinary.sc = d.nonBinary.Script;\n    d.nonBinary.scx = d.nonBinary.Script_Extensions;\n  }\n\n  for (var i = 0, list = [9, 10, 11, 12, 13, 14]; i < list.length; i += 1) {\n    var ecmaVersion = list[i];\n\n    buildUnicodeData(ecmaVersion);\n  }\n\n  var pp$1 = Parser.prototype;\n\n  var BranchID = function BranchID(parent, base) {\n    this.parent = parent;\n    this.base = base || this;\n  };\n\n  BranchID.prototype.separatedFrom = function separatedFrom (alt) {\n    for (var self = this; self; self = self.parent) {\n      for (var other = alt; other; other = other.parent) {\n        if (self.base === other.base && self !== other) { return true }\n      }\n    }\n    return false\n  };\n\n  BranchID.prototype.sibling = function sibling () {\n    return new BranchID(this.parent, this.base)\n  };\n\n  var RegExpValidationState = function RegExpValidationState(parser) {\n    this.parser = parser;\n    this.validFlags = \"gim\" + (parser.options.ecmaVersion >= 6 ? \"uy\" : \"\") + (parser.options.ecmaVersion >= 9 ? \"s\" : \"\") + (parser.options.ecmaVersion >= 13 ? \"d\" : \"\") + (parser.options.ecmaVersion >= 15 ? \"v\" : \"\");\n    this.unicodeProperties = data[parser.options.ecmaVersion >= 14 ? 14 : parser.options.ecmaVersion];\n    this.source = \"\";\n    this.flags = \"\";\n    this.start = 0;\n    this.switchU = false;\n    this.switchV = false;\n    this.switchN = false;\n    this.pos = 0;\n    this.lastIntValue = 0;\n    this.lastStringValue = \"\";\n    this.lastAssertionIsQuantifiable = false;\n    this.numCapturingParens = 0;\n    this.maxBackReference = 0;\n    this.groupNames = Object.create(null);\n    this.backReferenceNames = [];\n    this.branchID = null;\n  };\n\n  RegExpValidationState.prototype.reset = function reset (start, pattern, flags) {\n    var unicodeSets = flags.indexOf(\"v\") !== -1;\n    var unicode = flags.indexOf(\"u\") !== -1;\n    this.start = start | 0;\n    this.source = pattern + \"\";\n    this.flags = flags;\n    if (unicodeSets && this.parser.options.ecmaVersion >= 15) {\n      this.switchU = true;\n      this.switchV = true;\n      this.switchN = true;\n    } else {\n      this.switchU = unicode && this.parser.options.ecmaVersion >= 6;\n      this.switchV = false;\n      this.switchN = unicode && this.parser.options.ecmaVersion >= 9;\n    }\n  };\n\n  RegExpValidationState.prototype.raise = function raise (message) {\n    this.parser.raiseRecoverable(this.start, (\"Invalid regular expression: /\" + (this.source) + \"/: \" + message));\n  };\n\n  RegExpValidationState.prototype.at = function at (i, forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    var s = this.source;\n    var l = s.length;\n    if (i >= l) {\n      return -1\n    }\n    var c = s.charCodeAt(i);\n    if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {\n      return c\n    }\n    var next = s.charCodeAt(i + 1);\n    return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c\n  };\n\n  RegExpValidationState.prototype.nextIndex = function nextIndex (i, forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    var s = this.source;\n    var l = s.length;\n    if (i >= l) {\n      return l\n    }\n    var c = s.charCodeAt(i), next;\n    if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l ||\n        (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) {\n      return i + 1\n    }\n    return i + 2\n  };\n\n  RegExpValidationState.prototype.current = function current (forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    return this.at(this.pos, forceU)\n  };\n\n  RegExpValidationState.prototype.lookahead = function lookahead (forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    return this.at(this.nextIndex(this.pos, forceU), forceU)\n  };\n\n  RegExpValidationState.prototype.advance = function advance (forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    this.pos = this.nextIndex(this.pos, forceU);\n  };\n\n  RegExpValidationState.prototype.eat = function eat (ch, forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    if (this.current(forceU) === ch) {\n      this.advance(forceU);\n      return true\n    }\n    return false\n  };\n\n  RegExpValidationState.prototype.eatChars = function eatChars (chs, forceU) {\n      if ( forceU === void 0 ) forceU = false;\n\n    var pos = this.pos;\n    for (var i = 0, list = chs; i < list.length; i += 1) {\n      var ch = list[i];\n\n        var current = this.at(pos, forceU);\n      if (current === -1 || current !== ch) {\n        return false\n      }\n      pos = this.nextIndex(pos, forceU);\n    }\n    this.pos = pos;\n    return true\n  };\n\n  pp$1.validateRegExpFlags = function(state) {\n    var validFlags = state.validFlags;\n    var flags = state.flags;\n\n    var u = false;\n    var v = false;\n\n    for (var i = 0; i < flags.length; i++) {\n      var flag = flags.charAt(i);\n      if (validFlags.indexOf(flag) === -1) {\n        this.raise(state.start, \"Invalid regular expression flag\");\n      }\n      if (flags.indexOf(flag, i + 1) > -1) {\n        this.raise(state.start, \"Duplicate regular expression flag\");\n      }\n      if (flag === \"u\") { u = true; }\n      if (flag === \"v\") { v = true; }\n    }\n    if (this.options.ecmaVersion >= 15 && u && v) {\n      this.raise(state.start, \"Invalid regular expression flag\");\n    }\n  };\n\n  function hasProp(obj) {\n    for (var _ in obj) { return true }\n    return false\n  }\n\n  pp$1.validateRegExpPattern = function(state) {\n    this.regexp_pattern(state);\n\n    if (!state.switchN && this.options.ecmaVersion >= 9 && hasProp(state.groupNames)) {\n      state.switchN = true;\n      this.regexp_pattern(state);\n    }\n  };\n\n  pp$1.regexp_pattern = function(state) {\n    state.pos = 0;\n    state.lastIntValue = 0;\n    state.lastStringValue = \"\";\n    state.lastAssertionIsQuantifiable = false;\n    state.numCapturingParens = 0;\n    state.maxBackReference = 0;\n    state.groupNames = Object.create(null);\n    state.backReferenceNames.length = 0;\n    state.branchID = null;\n\n    this.regexp_disjunction(state);\n\n    if (state.pos !== state.source.length) {\n      if (state.eat(0x29 )) {\n        state.raise(\"Unmatched ')'\");\n      }\n      if (state.eat(0x5D ) || state.eat(0x7D )) {\n        state.raise(\"Lone quantifier brackets\");\n      }\n    }\n    if (state.maxBackReference > state.numCapturingParens) {\n      state.raise(\"Invalid escape\");\n    }\n    for (var i = 0, list = state.backReferenceNames; i < list.length; i += 1) {\n      var name = list[i];\n\n      if (!state.groupNames[name]) {\n        state.raise(\"Invalid named capture referenced\");\n      }\n    }\n  };\n\n  pp$1.regexp_disjunction = function(state) {\n    var trackDisjunction = this.options.ecmaVersion >= 16;\n    if (trackDisjunction) { state.branchID = new BranchID(state.branchID, null); }\n    this.regexp_alternative(state);\n    while (state.eat(0x7C )) {\n      if (trackDisjunction) { state.branchID = state.branchID.sibling(); }\n      this.regexp_alternative(state);\n    }\n    if (trackDisjunction) { state.branchID = state.branchID.parent; }\n\n    if (this.regexp_eatQuantifier(state, true)) {\n      state.raise(\"Nothing to repeat\");\n    }\n    if (state.eat(0x7B )) {\n      state.raise(\"Lone quantifier brackets\");\n    }\n  };\n\n  pp$1.regexp_alternative = function(state) {\n    while (state.pos < state.source.length && this.regexp_eatTerm(state)) {}\n  };\n\n  pp$1.regexp_eatTerm = function(state) {\n    if (this.regexp_eatAssertion(state)) {\n      if (state.lastAssertionIsQuantifiable && this.regexp_eatQuantifier(state)) {\n        if (state.switchU) {\n          state.raise(\"Invalid quantifier\");\n        }\n      }\n      return true\n    }\n\n    if (state.switchU ? this.regexp_eatAtom(state) : this.regexp_eatExtendedAtom(state)) {\n      this.regexp_eatQuantifier(state);\n      return true\n    }\n\n    return false\n  };\n\n  pp$1.regexp_eatAssertion = function(state) {\n    var start = state.pos;\n    state.lastAssertionIsQuantifiable = false;\n\n    if (state.eat(0x5E ) || state.eat(0x24 )) {\n      return true\n    }\n\n    if (state.eat(0x5C )) {\n      if (state.eat(0x42 ) || state.eat(0x62 )) {\n        return true\n      }\n      state.pos = start;\n    }\n\n    if (state.eat(0x28 ) && state.eat(0x3F )) {\n      var lookbehind = false;\n      if (this.options.ecmaVersion >= 9) {\n        lookbehind = state.eat(0x3C );\n      }\n      if (state.eat(0x3D ) || state.eat(0x21 )) {\n        this.regexp_disjunction(state);\n        if (!state.eat(0x29 )) {\n          state.raise(\"Unterminated group\");\n        }\n        state.lastAssertionIsQuantifiable = !lookbehind;\n        return true\n      }\n    }\n\n    state.pos = start;\n    return false\n  };\n\n  pp$1.regexp_eatQuantifier = function(state, noError) {\n    if ( noError === void 0 ) noError = false;\n\n    if (this.regexp_eatQuantifierPrefix(state, noError)) {\n      state.eat(0x3F );\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatQuantifierPrefix = function(state, noError) {\n    return (\n      state.eat(0x2A ) ||\n      state.eat(0x2B ) ||\n      state.eat(0x3F ) ||\n      this.regexp_eatBracedQuantifier(state, noError)\n    )\n  };\n  pp$1.regexp_eatBracedQuantifier = function(state, noError) {\n    var start = state.pos;\n    if (state.eat(0x7B )) {\n      var min = 0, max = -1;\n      if (this.regexp_eatDecimalDigits(state)) {\n        min = state.lastIntValue;\n        if (state.eat(0x2C ) && this.regexp_eatDecimalDigits(state)) {\n          max = state.lastIntValue;\n        }\n        if (state.eat(0x7D )) {\n          if (max !== -1 && max < min && !noError) {\n            state.raise(\"numbers out of order in {} quantifier\");\n          }\n          return true\n        }\n      }\n      if (state.switchU && !noError) {\n        state.raise(\"Incomplete quantifier\");\n      }\n      state.pos = start;\n    }\n    return false\n  };\n\n  pp$1.regexp_eatAtom = function(state) {\n    return (\n      this.regexp_eatPatternCharacters(state) ||\n      state.eat(0x2E ) ||\n      this.regexp_eatReverseSolidusAtomEscape(state) ||\n      this.regexp_eatCharacterClass(state) ||\n      this.regexp_eatUncapturingGroup(state) ||\n      this.regexp_eatCapturingGroup(state)\n    )\n  };\n  pp$1.regexp_eatReverseSolidusAtomEscape = function(state) {\n    var start = state.pos;\n    if (state.eat(0x5C )) {\n      if (this.regexp_eatAtomEscape(state)) {\n        return true\n      }\n      state.pos = start;\n    }\n    return false\n  };\n  pp$1.regexp_eatUncapturingGroup = function(state) {\n    var start = state.pos;\n    if (state.eat(0x28 )) {\n      if (state.eat(0x3F )) {\n        if (this.options.ecmaVersion >= 16) {\n          var addModifiers = this.regexp_eatModifiers(state);\n          var hasHyphen = state.eat(0x2D );\n          if (addModifiers || hasHyphen) {\n            for (var i = 0; i < addModifiers.length; i++) {\n              var modifier = addModifiers.charAt(i);\n              if (addModifiers.indexOf(modifier, i + 1) > -1) {\n                state.raise(\"Duplicate regular expression modifiers\");\n              }\n            }\n            if (hasHyphen) {\n              var removeModifiers = this.regexp_eatModifiers(state);\n              if (!addModifiers && !removeModifiers && state.current() === 0x3A ) {\n                state.raise(\"Invalid regular expression modifiers\");\n              }\n              for (var i$1 = 0; i$1 < removeModifiers.length; i$1++) {\n                var modifier$1 = removeModifiers.charAt(i$1);\n                if (\n                  removeModifiers.indexOf(modifier$1, i$1 + 1) > -1 ||\n                  addModifiers.indexOf(modifier$1) > -1\n                ) {\n                  state.raise(\"Duplicate regular expression modifiers\");\n                }\n              }\n            }\n          }\n        }\n        if (state.eat(0x3A )) {\n          this.regexp_disjunction(state);\n          if (state.eat(0x29 )) {\n            return true\n          }\n          state.raise(\"Unterminated group\");\n        }\n      }\n      state.pos = start;\n    }\n    return false\n  };\n  pp$1.regexp_eatCapturingGroup = function(state) {\n    if (state.eat(0x28 )) {\n      if (this.options.ecmaVersion >= 9) {\n        this.regexp_groupSpecifier(state);\n      } else if (state.current() === 0x3F ) {\n        state.raise(\"Invalid group\");\n      }\n      this.regexp_disjunction(state);\n      if (state.eat(0x29 )) {\n        state.numCapturingParens += 1;\n        return true\n      }\n      state.raise(\"Unterminated group\");\n    }\n    return false\n  };\n  pp$1.regexp_eatModifiers = function(state) {\n    var modifiers = \"\";\n    var ch = 0;\n    while ((ch = state.current()) !== -1 && isRegularExpressionModifier(ch)) {\n      modifiers += codePointToString(ch);\n      state.advance();\n    }\n    return modifiers\n  };\n  function isRegularExpressionModifier(ch) {\n    return ch === 0x69  || ch === 0x6d  || ch === 0x73 \n  }\n\n  pp$1.regexp_eatExtendedAtom = function(state) {\n    return (\n      state.eat(0x2E ) ||\n      this.regexp_eatReverseSolidusAtomEscape(state) ||\n      this.regexp_eatCharacterClass(state) ||\n      this.regexp_eatUncapturingGroup(state) ||\n      this.regexp_eatCapturingGroup(state) ||\n      this.regexp_eatInvalidBracedQuantifier(state) ||\n      this.regexp_eatExtendedPatternCharacter(state)\n    )\n  };\n\n  pp$1.regexp_eatInvalidBracedQuantifier = function(state) {\n    if (this.regexp_eatBracedQuantifier(state, true)) {\n      state.raise(\"Nothing to repeat\");\n    }\n    return false\n  };\n\n  pp$1.regexp_eatSyntaxCharacter = function(state) {\n    var ch = state.current();\n    if (isSyntaxCharacter(ch)) {\n      state.lastIntValue = ch;\n      state.advance();\n      return true\n    }\n    return false\n  };\n  function isSyntaxCharacter(ch) {\n    return (\n      ch === 0x24  ||\n      ch >= 0x28  && ch <= 0x2B  ||\n      ch === 0x2E  ||\n      ch === 0x3F  ||\n      ch >= 0x5B  && ch <= 0x5E  ||\n      ch >= 0x7B  && ch <= 0x7D \n    )\n  }\n\n  pp$1.regexp_eatPatternCharacters = function(state) {\n    var start = state.pos;\n    var ch = 0;\n    while ((ch = state.current()) !== -1 && !isSyntaxCharacter(ch)) {\n      state.advance();\n    }\n    return state.pos !== start\n  };\n\n  pp$1.regexp_eatExtendedPatternCharacter = function(state) {\n    var ch = state.current();\n    if (\n      ch !== -1 &&\n      ch !== 0x24  &&\n      !(ch >= 0x28  && ch <= 0x2B ) &&\n      ch !== 0x2E  &&\n      ch !== 0x3F  &&\n      ch !== 0x5B  &&\n      ch !== 0x5E  &&\n      ch !== 0x7C \n    ) {\n      state.advance();\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_groupSpecifier = function(state) {\n    if (state.eat(0x3F )) {\n      if (!this.regexp_eatGroupName(state)) { state.raise(\"Invalid group\"); }\n      var trackDisjunction = this.options.ecmaVersion >= 16;\n      var known = state.groupNames[state.lastStringValue];\n      if (known) {\n        if (trackDisjunction) {\n          for (var i = 0, list = known; i < list.length; i += 1) {\n            var altID = list[i];\n\n            if (!altID.separatedFrom(state.branchID))\n              { state.raise(\"Duplicate capture group name\"); }\n          }\n        } else {\n          state.raise(\"Duplicate capture group name\");\n        }\n      }\n      if (trackDisjunction) {\n        (known || (state.groupNames[state.lastStringValue] = [])).push(state.branchID);\n      } else {\n        state.groupNames[state.lastStringValue] = true;\n      }\n    }\n  };\n\n  pp$1.regexp_eatGroupName = function(state) {\n    state.lastStringValue = \"\";\n    if (state.eat(0x3C )) {\n      if (this.regexp_eatRegExpIdentifierName(state) && state.eat(0x3E )) {\n        return true\n      }\n      state.raise(\"Invalid capture group name\");\n    }\n    return false\n  };\n\n  pp$1.regexp_eatRegExpIdentifierName = function(state) {\n    state.lastStringValue = \"\";\n    if (this.regexp_eatRegExpIdentifierStart(state)) {\n      state.lastStringValue += codePointToString(state.lastIntValue);\n      while (this.regexp_eatRegExpIdentifierPart(state)) {\n        state.lastStringValue += codePointToString(state.lastIntValue);\n      }\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatRegExpIdentifierStart = function(state) {\n    var start = state.pos;\n    var forceU = this.options.ecmaVersion >= 11;\n    var ch = state.current(forceU);\n    state.advance(forceU);\n\n    if (ch === 0x5C  && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n      ch = state.lastIntValue;\n    }\n    if (isRegExpIdentifierStart(ch)) {\n      state.lastIntValue = ch;\n      return true\n    }\n\n    state.pos = start;\n    return false\n  };\n  function isRegExpIdentifierStart(ch) {\n    return isIdentifierStart(ch, true) || ch === 0x24  || ch === 0x5F \n  }\n\n  pp$1.regexp_eatRegExpIdentifierPart = function(state) {\n    var start = state.pos;\n    var forceU = this.options.ecmaVersion >= 11;\n    var ch = state.current(forceU);\n    state.advance(forceU);\n\n    if (ch === 0x5C  && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n      ch = state.lastIntValue;\n    }\n    if (isRegExpIdentifierPart(ch)) {\n      state.lastIntValue = ch;\n      return true\n    }\n\n    state.pos = start;\n    return false\n  };\n  function isRegExpIdentifierPart(ch) {\n    return isIdentifierChar(ch, true) || ch === 0x24  || ch === 0x5F  || ch === 0x200C  || ch === 0x200D \n  }\n\n  pp$1.regexp_eatAtomEscape = function(state) {\n    if (\n      this.regexp_eatBackReference(state) ||\n      this.regexp_eatCharacterClassEscape(state) ||\n      this.regexp_eatCharacterEscape(state) ||\n      (state.switchN && this.regexp_eatKGroupName(state))\n    ) {\n      return true\n    }\n    if (state.switchU) {\n      if (state.current() === 0x63 ) {\n        state.raise(\"Invalid unicode escape\");\n      }\n      state.raise(\"Invalid escape\");\n    }\n    return false\n  };\n  pp$1.regexp_eatBackReference = function(state) {\n    var start = state.pos;\n    if (this.regexp_eatDecimalEscape(state)) {\n      var n = state.lastIntValue;\n      if (state.switchU) {\n        if (n > state.maxBackReference) {\n          state.maxBackReference = n;\n        }\n        return true\n      }\n      if (n <= state.numCapturingParens) {\n        return true\n      }\n      state.pos = start;\n    }\n    return false\n  };\n  pp$1.regexp_eatKGroupName = function(state) {\n    if (state.eat(0x6B )) {\n      if (this.regexp_eatGroupName(state)) {\n        state.backReferenceNames.push(state.lastStringValue);\n        return true\n      }\n      state.raise(\"Invalid named reference\");\n    }\n    return false\n  };\n\n  pp$1.regexp_eatCharacterEscape = function(state) {\n    return (\n      this.regexp_eatControlEscape(state) ||\n      this.regexp_eatCControlLetter(state) ||\n      this.regexp_eatZero(state) ||\n      this.regexp_eatHexEscapeSequence(state) ||\n      this.regexp_eatRegExpUnicodeEscapeSequence(state, false) ||\n      (!state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state)) ||\n      this.regexp_eatIdentityEscape(state)\n    )\n  };\n  pp$1.regexp_eatCControlLetter = function(state) {\n    var start = state.pos;\n    if (state.eat(0x63 )) {\n      if (this.regexp_eatControlLetter(state)) {\n        return true\n      }\n      state.pos = start;\n    }\n    return false\n  };\n  pp$1.regexp_eatZero = function(state) {\n    if (state.current() === 0x30  && !isDecimalDigit(state.lookahead())) {\n      state.lastIntValue = 0;\n      state.advance();\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatControlEscape = function(state) {\n    var ch = state.current();\n    if (ch === 0x74 ) {\n      state.lastIntValue = 0x09; \n      state.advance();\n      return true\n    }\n    if (ch === 0x6E ) {\n      state.lastIntValue = 0x0A; \n      state.advance();\n      return true\n    }\n    if (ch === 0x76 ) {\n      state.lastIntValue = 0x0B; \n      state.advance();\n      return true\n    }\n    if (ch === 0x66 ) {\n      state.lastIntValue = 0x0C; \n      state.advance();\n      return true\n    }\n    if (ch === 0x72 ) {\n      state.lastIntValue = 0x0D; \n      state.advance();\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatControlLetter = function(state) {\n    var ch = state.current();\n    if (isControlLetter(ch)) {\n      state.lastIntValue = ch % 0x20;\n      state.advance();\n      return true\n    }\n    return false\n  };\n  function isControlLetter(ch) {\n    return (\n      (ch >= 0x41  && ch <= 0x5A ) ||\n      (ch >= 0x61  && ch <= 0x7A )\n    )\n  }\n\n  pp$1.regexp_eatRegExpUnicodeEscapeSequence = function(state, forceU) {\n    if ( forceU === void 0 ) forceU = false;\n\n    var start = state.pos;\n    var switchU = forceU || state.switchU;\n\n    if (state.eat(0x75 )) {\n      if (this.regexp_eatFixedHexDigits(state, 4)) {\n        var lead = state.lastIntValue;\n        if (switchU && lead >= 0xD800 && lead <= 0xDBFF) {\n          var leadSurrogateEnd = state.pos;\n          if (state.eat(0x5C ) && state.eat(0x75 ) && this.regexp_eatFixedHexDigits(state, 4)) {\n            var trail = state.lastIntValue;\n            if (trail >= 0xDC00 && trail <= 0xDFFF) {\n              state.lastIntValue = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;\n              return true\n            }\n          }\n          state.pos = leadSurrogateEnd;\n          state.lastIntValue = lead;\n        }\n        return true\n      }\n      if (\n        switchU &&\n        state.eat(0x7B ) &&\n        this.regexp_eatHexDigits(state) &&\n        state.eat(0x7D ) &&\n        isValidUnicode(state.lastIntValue)\n      ) {\n        return true\n      }\n      if (switchU) {\n        state.raise(\"Invalid unicode escape\");\n      }\n      state.pos = start;\n    }\n\n    return false\n  };\n  function isValidUnicode(ch) {\n    return ch >= 0 && ch <= 0x10FFFF\n  }\n\n  pp$1.regexp_eatIdentityEscape = function(state) {\n    if (state.switchU) {\n      if (this.regexp_eatSyntaxCharacter(state)) {\n        return true\n      }\n      if (state.eat(0x2F )) {\n        state.lastIntValue = 0x2F; \n        return true\n      }\n      return false\n    }\n\n    var ch = state.current();\n    if (ch !== 0x63  && (!state.switchN || ch !== 0x6B )) {\n      state.lastIntValue = ch;\n      state.advance();\n      return true\n    }\n\n    return false\n  };\n\n  pp$1.regexp_eatDecimalEscape = function(state) {\n    state.lastIntValue = 0;\n    var ch = state.current();\n    if (ch >= 0x31  && ch <= 0x39 ) {\n      do {\n        state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 );\n        state.advance();\n      } while ((ch = state.current()) >= 0x30  && ch <= 0x39 )\n      return true\n    }\n    return false\n  };\n\n  var CharSetNone = 0; \n  var CharSetOk = 1; \n  var CharSetString = 2; \n\n  pp$1.regexp_eatCharacterClassEscape = function(state) {\n    var ch = state.current();\n\n    if (isCharacterClassEscape(ch)) {\n      state.lastIntValue = -1;\n      state.advance();\n      return CharSetOk\n    }\n\n    var negate = false;\n    if (\n      state.switchU &&\n      this.options.ecmaVersion >= 9 &&\n      ((negate = ch === 0x50 ) || ch === 0x70 )\n    ) {\n      state.lastIntValue = -1;\n      state.advance();\n      var result;\n      if (\n        state.eat(0x7B ) &&\n        (result = this.regexp_eatUnicodePropertyValueExpression(state)) &&\n        state.eat(0x7D )\n      ) {\n        if (negate && result === CharSetString) { state.raise(\"Invalid property name\"); }\n        return result\n      }\n      state.raise(\"Invalid property name\");\n    }\n\n    return CharSetNone\n  };\n\n  function isCharacterClassEscape(ch) {\n    return (\n      ch === 0x64  ||\n      ch === 0x44  ||\n      ch === 0x73  ||\n      ch === 0x53  ||\n      ch === 0x77  ||\n      ch === 0x57 \n    )\n  }\n\n  pp$1.regexp_eatUnicodePropertyValueExpression = function(state) {\n    var start = state.pos;\n\n    if (this.regexp_eatUnicodePropertyName(state) && state.eat(0x3D )) {\n      var name = state.lastStringValue;\n      if (this.regexp_eatUnicodePropertyValue(state)) {\n        var value = state.lastStringValue;\n        this.regexp_validateUnicodePropertyNameAndValue(state, name, value);\n        return CharSetOk\n      }\n    }\n    state.pos = start;\n\n    if (this.regexp_eatLoneUnicodePropertyNameOrValue(state)) {\n      var nameOrValue = state.lastStringValue;\n      return this.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue)\n    }\n    return CharSetNone\n  };\n\n  pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {\n    if (!hasOwn(state.unicodeProperties.nonBinary, name))\n      { state.raise(\"Invalid property name\"); }\n    if (!state.unicodeProperties.nonBinary[name].test(value))\n      { state.raise(\"Invalid property value\"); }\n  };\n\n  pp$1.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {\n    if (state.unicodeProperties.binary.test(nameOrValue)) { return CharSetOk }\n    if (state.switchV && state.unicodeProperties.binaryOfStrings.test(nameOrValue)) { return CharSetString }\n    state.raise(\"Invalid property name\");\n  };\n\n  pp$1.regexp_eatUnicodePropertyName = function(state) {\n    var ch = 0;\n    state.lastStringValue = \"\";\n    while (isUnicodePropertyNameCharacter(ch = state.current())) {\n      state.lastStringValue += codePointToString(ch);\n      state.advance();\n    }\n    return state.lastStringValue !== \"\"\n  };\n\n  function isUnicodePropertyNameCharacter(ch) {\n    return isControlLetter(ch) || ch === 0x5F \n  }\n\n  pp$1.regexp_eatUnicodePropertyValue = function(state) {\n    var ch = 0;\n    state.lastStringValue = \"\";\n    while (isUnicodePropertyValueCharacter(ch = state.current())) {\n      state.lastStringValue += codePointToString(ch);\n      state.advance();\n    }\n    return state.lastStringValue !== \"\"\n  };\n  function isUnicodePropertyValueCharacter(ch) {\n    return isUnicodePropertyNameCharacter(ch) || isDecimalDigit(ch)\n  }\n\n  pp$1.regexp_eatLoneUnicodePropertyNameOrValue = function(state) {\n    return this.regexp_eatUnicodePropertyValue(state)\n  };\n\n  pp$1.regexp_eatCharacterClass = function(state) {\n    if (state.eat(0x5B )) {\n      var negate = state.eat(0x5E );\n      var result = this.regexp_classContents(state);\n      if (!state.eat(0x5D ))\n        { state.raise(\"Unterminated character class\"); }\n      if (negate && result === CharSetString)\n        { state.raise(\"Negated character class may contain strings\"); }\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_classContents = function(state) {\n    if (state.current() === 0x5D ) { return CharSetOk }\n    if (state.switchV) { return this.regexp_classSetExpression(state) }\n    this.regexp_nonEmptyClassRanges(state);\n    return CharSetOk\n  };\n\n  pp$1.regexp_nonEmptyClassRanges = function(state) {\n    while (this.regexp_eatClassAtom(state)) {\n      var left = state.lastIntValue;\n      if (state.eat(0x2D ) && this.regexp_eatClassAtom(state)) {\n        var right = state.lastIntValue;\n        if (state.switchU && (left === -1 || right === -1)) {\n          state.raise(\"Invalid character class\");\n        }\n        if (left !== -1 && right !== -1 && left > right) {\n          state.raise(\"Range out of order in character class\");\n        }\n      }\n    }\n  };\n\n  pp$1.regexp_eatClassAtom = function(state) {\n    var start = state.pos;\n\n    if (state.eat(0x5C )) {\n      if (this.regexp_eatClassEscape(state)) {\n        return true\n      }\n      if (state.switchU) {\n        var ch$1 = state.current();\n        if (ch$1 === 0x63  || isOctalDigit(ch$1)) {\n          state.raise(\"Invalid class escape\");\n        }\n        state.raise(\"Invalid escape\");\n      }\n      state.pos = start;\n    }\n\n    var ch = state.current();\n    if (ch !== 0x5D ) {\n      state.lastIntValue = ch;\n      state.advance();\n      return true\n    }\n\n    return false\n  };\n\n  pp$1.regexp_eatClassEscape = function(state) {\n    var start = state.pos;\n\n    if (state.eat(0x62 )) {\n      state.lastIntValue = 0x08; \n      return true\n    }\n\n    if (state.switchU && state.eat(0x2D )) {\n      state.lastIntValue = 0x2D; \n      return true\n    }\n\n    if (!state.switchU && state.eat(0x63 )) {\n      if (this.regexp_eatClassControlLetter(state)) {\n        return true\n      }\n      state.pos = start;\n    }\n\n    return (\n      this.regexp_eatCharacterClassEscape(state) ||\n      this.regexp_eatCharacterEscape(state)\n    )\n  };\n\n  pp$1.regexp_classSetExpression = function(state) {\n    var result = CharSetOk, subResult;\n    if (this.regexp_eatClassSetRange(state)) ; else if (subResult = this.regexp_eatClassSetOperand(state)) {\n      if (subResult === CharSetString) { result = CharSetString; }\n      var start = state.pos;\n      while (state.eatChars([0x26, 0x26] )) {\n        if (\n          state.current() !== 0x26  &&\n          (subResult = this.regexp_eatClassSetOperand(state))\n        ) {\n          if (subResult !== CharSetString) { result = CharSetOk; }\n          continue\n        }\n        state.raise(\"Invalid character in character class\");\n      }\n      if (start !== state.pos) { return result }\n      while (state.eatChars([0x2D, 0x2D] )) {\n        if (this.regexp_eatClassSetOperand(state)) { continue }\n        state.raise(\"Invalid character in character class\");\n      }\n      if (start !== state.pos) { return result }\n    } else {\n      state.raise(\"Invalid character in character class\");\n    }\n    for (;;) {\n      if (this.regexp_eatClassSetRange(state)) { continue }\n      subResult = this.regexp_eatClassSetOperand(state);\n      if (!subResult) { return result }\n      if (subResult === CharSetString) { result = CharSetString; }\n    }\n  };\n\n  pp$1.regexp_eatClassSetRange = function(state) {\n    var start = state.pos;\n    if (this.regexp_eatClassSetCharacter(state)) {\n      var left = state.lastIntValue;\n      if (state.eat(0x2D ) && this.regexp_eatClassSetCharacter(state)) {\n        var right = state.lastIntValue;\n        if (left !== -1 && right !== -1 && left > right) {\n          state.raise(\"Range out of order in character class\");\n        }\n        return true\n      }\n      state.pos = start;\n    }\n    return false\n  };\n\n  pp$1.regexp_eatClassSetOperand = function(state) {\n    if (this.regexp_eatClassSetCharacter(state)) { return CharSetOk }\n    return this.regexp_eatClassStringDisjunction(state) || this.regexp_eatNestedClass(state)\n  };\n\n  pp$1.regexp_eatNestedClass = function(state) {\n    var start = state.pos;\n    if (state.eat(0x5B )) {\n      var negate = state.eat(0x5E );\n      var result = this.regexp_classContents(state);\n      if (state.eat(0x5D )) {\n        if (negate && result === CharSetString) {\n          state.raise(\"Negated character class may contain strings\");\n        }\n        return result\n      }\n      state.pos = start;\n    }\n    if (state.eat(0x5C )) {\n      var result$1 = this.regexp_eatCharacterClassEscape(state);\n      if (result$1) {\n        return result$1\n      }\n      state.pos = start;\n    }\n    return null\n  };\n\n  pp$1.regexp_eatClassStringDisjunction = function(state) {\n    var start = state.pos;\n    if (state.eatChars([0x5C, 0x71] )) {\n      if (state.eat(0x7B )) {\n        var result = this.regexp_classStringDisjunctionContents(state);\n        if (state.eat(0x7D )) {\n          return result\n        }\n      } else {\n        state.raise(\"Invalid escape\");\n      }\n      state.pos = start;\n    }\n    return null\n  };\n\n  pp$1.regexp_classStringDisjunctionContents = function(state) {\n    var result = this.regexp_classString(state);\n    while (state.eat(0x7C )) {\n      if (this.regexp_classString(state) === CharSetString) { result = CharSetString; }\n    }\n    return result\n  };\n\n  pp$1.regexp_classString = function(state) {\n    var count = 0;\n    while (this.regexp_eatClassSetCharacter(state)) { count++; }\n    return count === 1 ? CharSetOk : CharSetString\n  };\n\n  pp$1.regexp_eatClassSetCharacter = function(state) {\n    var start = state.pos;\n    if (state.eat(0x5C )) {\n      if (\n        this.regexp_eatCharacterEscape(state) ||\n        this.regexp_eatClassSetReservedPunctuator(state)\n      ) {\n        return true\n      }\n      if (state.eat(0x62 )) {\n        state.lastIntValue = 0x08; \n        return true\n      }\n      state.pos = start;\n      return false\n    }\n    var ch = state.current();\n    if (ch < 0 || ch === state.lookahead() && isClassSetReservedDoublePunctuatorCharacter(ch)) { return false }\n    if (isClassSetSyntaxCharacter(ch)) { return false }\n    state.advance();\n    state.lastIntValue = ch;\n    return true\n  };\n\n  function isClassSetReservedDoublePunctuatorCharacter(ch) {\n    return (\n      ch === 0x21  ||\n      ch >= 0x23  && ch <= 0x26  ||\n      ch >= 0x2A  && ch <= 0x2C  ||\n      ch === 0x2E  ||\n      ch >= 0x3A  && ch <= 0x40  ||\n      ch === 0x5E  ||\n      ch === 0x60  ||\n      ch === 0x7E \n    )\n  }\n\n  function isClassSetSyntaxCharacter(ch) {\n    return (\n      ch === 0x28  ||\n      ch === 0x29  ||\n      ch === 0x2D  ||\n      ch === 0x2F  ||\n      ch >= 0x5B  && ch <= 0x5D  ||\n      ch >= 0x7B  && ch <= 0x7D \n    )\n  }\n\n  pp$1.regexp_eatClassSetReservedPunctuator = function(state) {\n    var ch = state.current();\n    if (isClassSetReservedPunctuator(ch)) {\n      state.lastIntValue = ch;\n      state.advance();\n      return true\n    }\n    return false\n  };\n\n  function isClassSetReservedPunctuator(ch) {\n    return (\n      ch === 0x21  ||\n      ch === 0x23  ||\n      ch === 0x25  ||\n      ch === 0x26  ||\n      ch === 0x2C  ||\n      ch === 0x2D  ||\n      ch >= 0x3A  && ch <= 0x3E  ||\n      ch === 0x40  ||\n      ch === 0x60  ||\n      ch === 0x7E \n    )\n  }\n\n  pp$1.regexp_eatClassControlLetter = function(state) {\n    var ch = state.current();\n    if (isDecimalDigit(ch) || ch === 0x5F ) {\n      state.lastIntValue = ch % 0x20;\n      state.advance();\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatHexEscapeSequence = function(state) {\n    var start = state.pos;\n    if (state.eat(0x78 )) {\n      if (this.regexp_eatFixedHexDigits(state, 2)) {\n        return true\n      }\n      if (state.switchU) {\n        state.raise(\"Invalid escape\");\n      }\n      state.pos = start;\n    }\n    return false\n  };\n\n  pp$1.regexp_eatDecimalDigits = function(state) {\n    var start = state.pos;\n    var ch = 0;\n    state.lastIntValue = 0;\n    while (isDecimalDigit(ch = state.current())) {\n      state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 );\n      state.advance();\n    }\n    return state.pos !== start\n  };\n  function isDecimalDigit(ch) {\n    return ch >= 0x30  && ch <= 0x39 \n  }\n\n  pp$1.regexp_eatHexDigits = function(state) {\n    var start = state.pos;\n    var ch = 0;\n    state.lastIntValue = 0;\n    while (isHexDigit(ch = state.current())) {\n      state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n      state.advance();\n    }\n    return state.pos !== start\n  };\n  function isHexDigit(ch) {\n    return (\n      (ch >= 0x30  && ch <= 0x39 ) ||\n      (ch >= 0x41  && ch <= 0x46 ) ||\n      (ch >= 0x61  && ch <= 0x66 )\n    )\n  }\n  function hexToInt(ch) {\n    if (ch >= 0x41  && ch <= 0x46 ) {\n      return 10 + (ch - 0x41 )\n    }\n    if (ch >= 0x61  && ch <= 0x66 ) {\n      return 10 + (ch - 0x61 )\n    }\n    return ch - 0x30 \n  }\n\n  pp$1.regexp_eatLegacyOctalEscapeSequence = function(state) {\n    if (this.regexp_eatOctalDigit(state)) {\n      var n1 = state.lastIntValue;\n      if (this.regexp_eatOctalDigit(state)) {\n        var n2 = state.lastIntValue;\n        if (n1 <= 3 && this.regexp_eatOctalDigit(state)) {\n          state.lastIntValue = n1 * 64 + n2 * 8 + state.lastIntValue;\n        } else {\n          state.lastIntValue = n1 * 8 + n2;\n        }\n      } else {\n        state.lastIntValue = n1;\n      }\n      return true\n    }\n    return false\n  };\n\n  pp$1.regexp_eatOctalDigit = function(state) {\n    var ch = state.current();\n    if (isOctalDigit(ch)) {\n      state.lastIntValue = ch - 0x30; \n      state.advance();\n      return true\n    }\n    state.lastIntValue = 0;\n    return false\n  };\n  function isOctalDigit(ch) {\n    return ch >= 0x30  && ch <= 0x37 \n  }\n\n  pp$1.regexp_eatFixedHexDigits = function(state, length) {\n    var start = state.pos;\n    state.lastIntValue = 0;\n    for (var i = 0; i < length; ++i) {\n      var ch = state.current();\n      if (!isHexDigit(ch)) {\n        state.pos = start;\n        return false\n      }\n      state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n      state.advance();\n    }\n    return true\n  };\n\n\n  var Token = function Token(p) {\n    this.type = p.type;\n    this.value = p.value;\n    this.start = p.start;\n    this.end = p.end;\n    if (p.options.locations)\n      { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }\n    if (p.options.ranges)\n      { this.range = [p.start, p.end]; }\n  };\n\n\n  var pp = Parser.prototype;\n\n\n  pp.next = function(ignoreEscapeSequenceInKeyword) {\n    if (!ignoreEscapeSequenceInKeyword && this.type.keyword && this.containsEsc)\n      { this.raiseRecoverable(this.start, \"Escape sequence in keyword \" + this.type.keyword); }\n    if (this.options.onToken)\n      { this.options.onToken(new Token(this)); }\n\n    this.lastTokEnd = this.end;\n    this.lastTokStart = this.start;\n    this.lastTokEndLoc = this.endLoc;\n    this.lastTokStartLoc = this.startLoc;\n    this.nextToken();\n  };\n\n  pp.getToken = function() {\n    this.next();\n    return new Token(this)\n  };\n\n  if (typeof Symbol !== \"undefined\")\n    { pp[Symbol.iterator] = function() {\n      var this$1$1 = this;\n\n      return {\n        next: function () {\n          var token = this$1$1.getToken();\n          return {\n            done: token.type === types$1.eof,\n            value: token\n          }\n        }\n      }\n    }; }\n\n\n\n  pp.nextToken = function() {\n    var curContext = this.curContext();\n    if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }\n\n    this.start = this.pos;\n    if (this.options.locations) { this.startLoc = this.curPosition(); }\n    if (this.pos >= this.input.length) { return this.finishToken(types$1.eof) }\n\n    if (curContext.override) { return curContext.override(this) }\n    else { this.readToken(this.fullCharCodeAtPos()); }\n  };\n\n  pp.readToken = function(code) {\n    if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 )\n      { return this.readWord() }\n\n    return this.getTokenFromCode(code)\n  };\n\n  pp.fullCharCodeAtPos = function() {\n    var code = this.input.charCodeAt(this.pos);\n    if (code <= 0xd7ff || code >= 0xdc00) { return code }\n    var next = this.input.charCodeAt(this.pos + 1);\n    return next <= 0xdbff || next >= 0xe000 ? code : (code << 10) + next - 0x35fdc00\n  };\n\n  pp.skipBlockComment = function() {\n    var startLoc = this.options.onComment && this.curPosition();\n    var start = this.pos, end = this.input.indexOf(\"*/\", this.pos += 2);\n    if (end === -1) { this.raise(this.pos - 2, \"Unterminated comment\"); }\n    this.pos = end + 2;\n    if (this.options.locations) {\n      for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) {\n        ++this.curLine;\n        pos = this.lineStart = nextBreak;\n      }\n    }\n    if (this.options.onComment)\n      { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,\n                             startLoc, this.curPosition()); }\n  };\n\n  pp.skipLineComment = function(startSkip) {\n    var start = this.pos;\n    var startLoc = this.options.onComment && this.curPosition();\n    var ch = this.input.charCodeAt(this.pos += startSkip);\n    while (this.pos < this.input.length && !isNewLine(ch)) {\n      ch = this.input.charCodeAt(++this.pos);\n    }\n    if (this.options.onComment)\n      { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,\n                             startLoc, this.curPosition()); }\n  };\n\n\n  pp.skipSpace = function() {\n    loop: while (this.pos < this.input.length) {\n      var ch = this.input.charCodeAt(this.pos);\n      switch (ch) {\n      case 32: case 160: \n        ++this.pos;\n        break\n      case 13:\n        if (this.input.charCodeAt(this.pos + 1) === 10) {\n          ++this.pos;\n        }\n      case 10: case 8232: case 8233:\n        ++this.pos;\n        if (this.options.locations) {\n          ++this.curLine;\n          this.lineStart = this.pos;\n        }\n        break\n      case 47: \n        switch (this.input.charCodeAt(this.pos + 1)) {\n        case 42: \n          this.skipBlockComment();\n          break\n        case 47:\n          this.skipLineComment(2);\n          break\n        default:\n          break loop\n        }\n        break\n      default:\n        if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {\n          ++this.pos;\n        } else {\n          break loop\n        }\n      }\n    }\n  };\n\n\n  pp.finishToken = function(type, val) {\n    this.end = this.pos;\n    if (this.options.locations) { this.endLoc = this.curPosition(); }\n    var prevType = this.type;\n    this.type = type;\n    this.value = val;\n\n    this.updateContext(prevType);\n  };\n\n\n  pp.readToken_dot = function() {\n    var next = this.input.charCodeAt(this.pos + 1);\n    if (next >= 48 && next <= 57) { return this.readNumber(true) }\n    var next2 = this.input.charCodeAt(this.pos + 2);\n    if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { \n      this.pos += 3;\n      return this.finishToken(types$1.ellipsis)\n    } else {\n      ++this.pos;\n      return this.finishToken(types$1.dot)\n    }\n  };\n\n  pp.readToken_slash = function() { \n    var next = this.input.charCodeAt(this.pos + 1);\n    if (this.exprAllowed) { ++this.pos; return this.readRegexp() }\n    if (next === 61) { return this.finishOp(types$1.assign, 2) }\n    return this.finishOp(types$1.slash, 1)\n  };\n\n  pp.readToken_mult_modulo_exp = function(code) { \n    var next = this.input.charCodeAt(this.pos + 1);\n    var size = 1;\n    var tokentype = code === 42 ? types$1.star : types$1.modulo;\n\n    if (this.options.ecmaVersion >= 7 && code === 42 && next === 42) {\n      ++size;\n      tokentype = types$1.starstar;\n      next = this.input.charCodeAt(this.pos + 2);\n    }\n\n    if (next === 61) { return this.finishOp(types$1.assign, size + 1) }\n    return this.finishOp(tokentype, size)\n  };\n\n  pp.readToken_pipe_amp = function(code) { \n    var next = this.input.charCodeAt(this.pos + 1);\n    if (next === code) {\n      if (this.options.ecmaVersion >= 12) {\n        var next2 = this.input.charCodeAt(this.pos + 2);\n        if (next2 === 61) { return this.finishOp(types$1.assign, 3) }\n      }\n      return this.finishOp(code === 124 ? types$1.logicalOR : types$1.logicalAND, 2)\n    }\n    if (next === 61) { return this.finishOp(types$1.assign, 2) }\n    return this.finishOp(code === 124 ? types$1.bitwiseOR : types$1.bitwiseAND, 1)\n  };\n\n  pp.readToken_caret = function() { \n    var next = this.input.charCodeAt(this.pos + 1);\n    if (next === 61) { return this.finishOp(types$1.assign, 2) }\n    return this.finishOp(types$1.bitwiseXOR, 1)\n  };\n\n  pp.readToken_plus_min = function(code) { \n    var next = this.input.charCodeAt(this.pos + 1);\n    if (next === code) {\n      if (next === 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 62 &&\n          (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {\n        this.skipLineComment(3);\n        this.skipSpace();\n        return this.nextToken()\n      }\n      return this.finishOp(types$1.incDec, 2)\n    }\n    if (next === 61) { return this.finishOp(types$1.assign, 2) }\n    return this.finishOp(types$1.plusMin, 1)\n  };\n\n  pp.readToken_lt_gt = function(code) { \n    var next = this.input.charCodeAt(this.pos + 1);\n    var size = 1;\n    if (next === code) {\n      size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n      if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n      return this.finishOp(types$1.bitShift, size)\n    }\n    if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n        this.input.charCodeAt(this.pos + 3) === 45) {\n      this.skipLineComment(4);\n      this.skipSpace();\n      return this.nextToken()\n    }\n    if (next === 61) { size = 2; }\n    return this.finishOp(types$1.relational, size)\n  };\n\n  pp.readToken_eq_excl = function(code) { \n    var next = this.input.charCodeAt(this.pos + 1);\n    if (next === 61) { return this.finishOp(types$1.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) }\n    if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { \n      this.pos += 2;\n      return this.finishToken(types$1.arrow)\n    }\n    return this.finishOp(code === 61 ? types$1.eq : types$1.prefix, 1)\n  };\n\n  pp.readToken_question = function() { \n    var ecmaVersion = this.options.ecmaVersion;\n    if (ecmaVersion >= 11) {\n      var next = this.input.charCodeAt(this.pos + 1);\n      if (next === 46) {\n        var next2 = this.input.charCodeAt(this.pos + 2);\n        if (next2 < 48 || next2 > 57) { return this.finishOp(types$1.questionDot, 2) }\n      }\n      if (next === 63) {\n        if (ecmaVersion >= 12) {\n          var next2$1 = this.input.charCodeAt(this.pos + 2);\n          if (next2$1 === 61) { return this.finishOp(types$1.assign, 3) }\n        }\n        return this.finishOp(types$1.coalesce, 2)\n      }\n    }\n    return this.finishOp(types$1.question, 1)\n  };\n\n  pp.readToken_numberSign = function() { \n    var ecmaVersion = this.options.ecmaVersion;\n    var code = 35; \n    if (ecmaVersion >= 13) {\n      ++this.pos;\n      code = this.fullCharCodeAtPos();\n      if (isIdentifierStart(code, true) || code === 92 ) {\n        return this.finishToken(types$1.privateId, this.readWord1())\n      }\n    }\n\n    this.raise(this.pos, \"Unexpected character '\" + codePointToString(code) + \"'\");\n  };\n\n  pp.getTokenFromCode = function(code) {\n    switch (code) {\n    case 46: \n      return this.readToken_dot()\n\n    case 40: ++this.pos; return this.finishToken(types$1.parenL)\n    case 41: ++this.pos; return this.finishToken(types$1.parenR)\n    case 59: ++this.pos; return this.finishToken(types$1.semi)\n    case 44: ++this.pos; return this.finishToken(types$1.comma)\n    case 91: ++this.pos; return this.finishToken(types$1.bracketL)\n    case 93: ++this.pos; return this.finishToken(types$1.bracketR)\n    case 123: ++this.pos; return this.finishToken(types$1.braceL)\n    case 125: ++this.pos; return this.finishToken(types$1.braceR)\n    case 58: ++this.pos; return this.finishToken(types$1.colon)\n\n    case 96: \n      if (this.options.ecmaVersion < 6) { break }\n      ++this.pos;\n      return this.finishToken(types$1.backQuote)\n\n    case 48: \n      var next = this.input.charCodeAt(this.pos + 1);\n      if (next === 120 || next === 88) { return this.readRadixNumber(16) } \n      if (this.options.ecmaVersion >= 6) {\n        if (next === 111 || next === 79) { return this.readRadixNumber(8) } \n        if (next === 98 || next === 66) { return this.readRadixNumber(2) } \n      }\n\n    case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: \n      return this.readNumber(false)\n\n    case 34: case 39: \n      return this.readString(code)\n\n    case 47: \n      return this.readToken_slash()\n\n    case 37: case 42: \n      return this.readToken_mult_modulo_exp(code)\n\n    case 124: case 38: \n      return this.readToken_pipe_amp(code)\n\n    case 94: \n      return this.readToken_caret()\n\n    case 43: case 45: \n      return this.readToken_plus_min(code)\n\n    case 60: case 62: \n      return this.readToken_lt_gt(code)\n\n    case 61: case 33: \n      return this.readToken_eq_excl(code)\n\n    case 63: \n      return this.readToken_question()\n\n    case 126: \n      return this.finishOp(types$1.prefix, 1)\n\n    case 35: \n      return this.readToken_numberSign()\n    }\n\n    this.raise(this.pos, \"Unexpected character '\" + codePointToString(code) + \"'\");\n  };\n\n  pp.finishOp = function(type, size) {\n    var str = this.input.slice(this.pos, this.pos + size);\n    this.pos += size;\n    return this.finishToken(type, str)\n  };\n\n  pp.readRegexp = function() {\n    var escaped, inClass, start = this.pos;\n    for (;;) {\n      if (this.pos >= this.input.length) { this.raise(start, \"Unterminated regular expression\"); }\n      var ch = this.input.charAt(this.pos);\n      if (lineBreak.test(ch)) { this.raise(start, \"Unterminated regular expression\"); }\n      if (!escaped) {\n        if (ch === \"[\") { inClass = true; }\n        else if (ch === \"]\" && inClass) { inClass = false; }\n        else if (ch === \"/\" && !inClass) { break }\n        escaped = ch === \"\\\\\";\n      } else { escaped = false; }\n      ++this.pos;\n    }\n    var pattern = this.input.slice(start, this.pos);\n    ++this.pos;\n    var flagsStart = this.pos;\n    var flags = this.readWord1();\n    if (this.containsEsc) { this.unexpected(flagsStart); }\n\n    var state = this.regexpState || (this.regexpState = new RegExpValidationState(this));\n    state.reset(start, pattern, flags);\n    this.validateRegExpFlags(state);\n    this.validateRegExpPattern(state);\n\n    var value = null;\n    try {\n      value = new RegExp(pattern, flags);\n    } catch (e) {\n    }\n\n    return this.finishToken(types$1.regexp, {pattern: pattern, flags: flags, value: value})\n  };\n\n\n  pp.readInt = function(radix, len, maybeLegacyOctalNumericLiteral) {\n    var allowSeparators = this.options.ecmaVersion >= 12 && len === undefined;\n\n    var isLegacyOctalNumericLiteral = maybeLegacyOctalNumericLiteral && this.input.charCodeAt(this.pos) === 48;\n\n    var start = this.pos, total = 0, lastCode = 0;\n    for (var i = 0, e = len == null ? Infinity : len; i < e; ++i, ++this.pos) {\n      var code = this.input.charCodeAt(this.pos), val = (void 0);\n\n      if (allowSeparators && code === 95) {\n        if (isLegacyOctalNumericLiteral) { this.raiseRecoverable(this.pos, \"Numeric separator is not allowed in legacy octal numeric literals\"); }\n        if (lastCode === 95) { this.raiseRecoverable(this.pos, \"Numeric separator must be exactly one underscore\"); }\n        if (i === 0) { this.raiseRecoverable(this.pos, \"Numeric separator is not allowed at the first of digits\"); }\n        lastCode = code;\n        continue\n      }\n\n      if (code >= 97) { val = code - 97 + 10; } \n      else if (code >= 65) { val = code - 65 + 10; } \n      else if (code >= 48 && code <= 57) { val = code - 48; } \n      else { val = Infinity; }\n      if (val >= radix) { break }\n      lastCode = code;\n      total = total * radix + val;\n    }\n\n    if (allowSeparators && lastCode === 95) { this.raiseRecoverable(this.pos - 1, \"Numeric separator is not allowed at the last of digits\"); }\n    if (this.pos === start || len != null && this.pos - start !== len) { return null }\n\n    return total\n  };\n\n  function stringToNumber(str, isLegacyOctalNumericLiteral) {\n    if (isLegacyOctalNumericLiteral) {\n      return parseInt(str, 8)\n    }\n\n    return parseFloat(str.replace(/_/g, \"\"))\n  }\n\n  function stringToBigInt(str) {\n    if (typeof BigInt !== \"function\") {\n      return null\n    }\n\n    return BigInt(str.replace(/_/g, \"\"))\n  }\n\n  pp.readRadixNumber = function(radix) {\n    var start = this.pos;\n    this.pos += 2; \n    var val = this.readInt(radix);\n    if (val == null) { this.raise(this.start + 2, \"Expected number in radix \" + radix); }\n    if (this.options.ecmaVersion >= 11 && this.input.charCodeAt(this.pos) === 110) {\n      val = stringToBigInt(this.input.slice(start, this.pos));\n      ++this.pos;\n    } else if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n    return this.finishToken(types$1.num, val)\n  };\n\n\n  pp.readNumber = function(startsWithDot) {\n    var start = this.pos;\n    if (!startsWithDot && this.readInt(10, undefined, true) === null) { this.raise(start, \"Invalid number\"); }\n    var octal = this.pos - start >= 2 && this.input.charCodeAt(start) === 48;\n    if (octal && this.strict) { this.raise(start, \"Invalid number\"); }\n    var next = this.input.charCodeAt(this.pos);\n    if (!octal && !startsWithDot && this.options.ecmaVersion >= 11 && next === 110) {\n      var val$1 = stringToBigInt(this.input.slice(start, this.pos));\n      ++this.pos;\n      if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n      return this.finishToken(types$1.num, val$1)\n    }\n    if (octal && /[89]/.test(this.input.slice(start, this.pos))) { octal = false; }\n    if (next === 46 && !octal) { \n      ++this.pos;\n      this.readInt(10);\n      next = this.input.charCodeAt(this.pos);\n    }\n    if ((next === 69 || next === 101) && !octal) { \n      next = this.input.charCodeAt(++this.pos);\n      if (next === 43 || next === 45) { ++this.pos; } \n      if (this.readInt(10) === null) { this.raise(start, \"Invalid number\"); }\n    }\n    if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n\n    var val = stringToNumber(this.input.slice(start, this.pos), octal);\n    return this.finishToken(types$1.num, val)\n  };\n\n\n  pp.readCodePoint = function() {\n    var ch = this.input.charCodeAt(this.pos), code;\n\n    if (ch === 123) { \n      if (this.options.ecmaVersion < 6) { this.unexpected(); }\n      var codePos = ++this.pos;\n      code = this.readHexChar(this.input.indexOf(\"}\", this.pos) - this.pos);\n      ++this.pos;\n      if (code > 0x10FFFF) { this.invalidStringToken(codePos, \"Code point out of bounds\"); }\n    } else {\n      code = this.readHexChar(4);\n    }\n    return code\n  };\n\n  pp.readString = function(quote) {\n    var out = \"\", chunkStart = ++this.pos;\n    for (;;) {\n      if (this.pos >= this.input.length) { this.raise(this.start, \"Unterminated string constant\"); }\n      var ch = this.input.charCodeAt(this.pos);\n      if (ch === quote) { break }\n      if (ch === 92) { \n        out += this.input.slice(chunkStart, this.pos);\n        out += this.readEscapedChar(false);\n        chunkStart = this.pos;\n      } else if (ch === 0x2028 || ch === 0x2029) {\n        if (this.options.ecmaVersion < 10) { this.raise(this.start, \"Unterminated string constant\"); }\n        ++this.pos;\n        if (this.options.locations) {\n          this.curLine++;\n          this.lineStart = this.pos;\n        }\n      } else {\n        if (isNewLine(ch)) { this.raise(this.start, \"Unterminated string constant\"); }\n        ++this.pos;\n      }\n    }\n    out += this.input.slice(chunkStart, this.pos++);\n    return this.finishToken(types$1.string, out)\n  };\n\n\n  var INVALID_TEMPLATE_ESCAPE_ERROR = {};\n\n  pp.tryReadTemplateToken = function() {\n    this.inTemplateElement = true;\n    try {\n      this.readTmplToken();\n    } catch (err) {\n      if (err === INVALID_TEMPLATE_ESCAPE_ERROR) {\n        this.readInvalidTemplateToken();\n      } else {\n        throw err\n      }\n    }\n\n    this.inTemplateElement = false;\n  };\n\n  pp.invalidStringToken = function(position, message) {\n    if (this.inTemplateElement && this.options.ecmaVersion >= 9) {\n      throw INVALID_TEMPLATE_ESCAPE_ERROR\n    } else {\n      this.raise(position, message);\n    }\n  };\n\n  pp.readTmplToken = function() {\n    var out = \"\", chunkStart = this.pos;\n    for (;;) {\n      if (this.pos >= this.input.length) { this.raise(this.start, \"Unterminated template\"); }\n      var ch = this.input.charCodeAt(this.pos);\n      if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { \n        if (this.pos === this.start && (this.type === types$1.template || this.type === types$1.invalidTemplate)) {\n          if (ch === 36) {\n            this.pos += 2;\n            return this.finishToken(types$1.dollarBraceL)\n          } else {\n            ++this.pos;\n            return this.finishToken(types$1.backQuote)\n          }\n        }\n        out += this.input.slice(chunkStart, this.pos);\n        return this.finishToken(types$1.template, out)\n      }\n      if (ch === 92) { \n        out += this.input.slice(chunkStart, this.pos);\n        out += this.readEscapedChar(true);\n        chunkStart = this.pos;\n      } else if (isNewLine(ch)) {\n        out += this.input.slice(chunkStart, this.pos);\n        ++this.pos;\n        switch (ch) {\n        case 13:\n          if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; }\n        case 10:\n          out += \"\\n\";\n          break\n        default:\n          out += String.fromCharCode(ch);\n          break\n        }\n        if (this.options.locations) {\n          ++this.curLine;\n          this.lineStart = this.pos;\n        }\n        chunkStart = this.pos;\n      } else {\n        ++this.pos;\n      }\n    }\n  };\n\n  pp.readInvalidTemplateToken = function() {\n    for (; this.pos < this.input.length; this.pos++) {\n      switch (this.input[this.pos]) {\n      case \"\\\\\":\n        ++this.pos;\n        break\n\n      case \"$\":\n        if (this.input[this.pos + 1] !== \"{\") { break }\n      case \"`\":\n        return this.finishToken(types$1.invalidTemplate, this.input.slice(this.start, this.pos))\n\n      case \"\\r\":\n        if (this.input[this.pos + 1] === \"\\n\") { ++this.pos; }\n      case \"\\n\": case \"\\u2028\": case \"\\u2029\":\n        ++this.curLine;\n        this.lineStart = this.pos + 1;\n        break\n      }\n    }\n    this.raise(this.start, \"Unterminated template\");\n  };\n\n\n  pp.readEscapedChar = function(inTemplate) {\n    var ch = this.input.charCodeAt(++this.pos);\n    ++this.pos;\n    switch (ch) {\n    case 110: return \"\\n\" \n    case 114: return \"\\r\" \n    case 120: return String.fromCharCode(this.readHexChar(2)) \n    case 117: return codePointToString(this.readCodePoint()) \n    case 116: return \"\\t\" \n    case 98: return \"\\b\" \n    case 118: return \"\\u000b\" \n    case 102: return \"\\f\" \n    case 13: if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; } \n    case 10: \n      if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }\n      return \"\"\n    case 56:\n    case 57:\n      if (this.strict) {\n        this.invalidStringToken(\n          this.pos - 1,\n          \"Invalid escape sequence\"\n        );\n      }\n      if (inTemplate) {\n        var codePos = this.pos - 1;\n\n        this.invalidStringToken(\n          codePos,\n          \"Invalid escape sequence in template string\"\n        );\n      }\n    default:\n      if (ch >= 48 && ch <= 55) {\n        var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];\n        var octal = parseInt(octalStr, 8);\n        if (octal > 255) {\n          octalStr = octalStr.slice(0, -1);\n          octal = parseInt(octalStr, 8);\n        }\n        this.pos += octalStr.length - 1;\n        ch = this.input.charCodeAt(this.pos);\n        if ((octalStr !== \"0\" || ch === 56 || ch === 57) && (this.strict || inTemplate)) {\n          this.invalidStringToken(\n            this.pos - 1 - octalStr.length,\n            inTemplate\n              ? \"Octal literal in template string\"\n              : \"Octal literal in strict mode\"\n          );\n        }\n        return String.fromCharCode(octal)\n      }\n      if (isNewLine(ch)) {\n        if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }\n        return \"\"\n      }\n      return String.fromCharCode(ch)\n    }\n  };\n\n\n  pp.readHexChar = function(len) {\n    var codePos = this.pos;\n    var n = this.readInt(16, len);\n    if (n === null) { this.invalidStringToken(codePos, \"Bad character escape sequence\"); }\n    return n\n  };\n\n\n  pp.readWord1 = function() {\n    this.containsEsc = false;\n    var word = \"\", first = true, chunkStart = this.pos;\n    var astral = this.options.ecmaVersion >= 6;\n    while (this.pos < this.input.length) {\n      var ch = this.fullCharCodeAtPos();\n      if (isIdentifierChar(ch, astral)) {\n        this.pos += ch <= 0xffff ? 1 : 2;\n      } else if (ch === 92) { \n        this.containsEsc = true;\n        word += this.input.slice(chunkStart, this.pos);\n        var escStart = this.pos;\n        if (this.input.charCodeAt(++this.pos) !== 117) \n          { this.invalidStringToken(this.pos, \"Expecting Unicode escape sequence \\\\uXXXX\"); }\n        ++this.pos;\n        var esc = this.readCodePoint();\n        if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))\n          { this.invalidStringToken(escStart, \"Invalid Unicode escape\"); }\n        word += codePointToString(esc);\n        chunkStart = this.pos;\n      } else {\n        break\n      }\n      first = false;\n    }\n    return word + this.input.slice(chunkStart, this.pos)\n  };\n\n\n  pp.readWord = function() {\n    var word = this.readWord1();\n    var type = types$1.name;\n    if (this.keywords.test(word)) {\n      type = keywords[word];\n    }\n    return this.finishToken(type, word)\n  };\n\n\n\n  var version = \"8.14.0\";\n\n  Parser.acorn = {\n    Parser: Parser,\n    version: version,\n    defaultOptions: defaultOptions,\n    Position: Position,\n    SourceLocation: SourceLocation,\n    getLineInfo: getLineInfo,\n    Node: Node,\n    TokenType: TokenType,\n    tokTypes: types$1,\n    keywordTypes: keywords,\n    TokContext: TokContext,\n    tokContexts: types,\n    isIdentifierChar: isIdentifierChar,\n    isIdentifierStart: isIdentifierStart,\n    Token: Token,\n    isNewLine: isNewLine,\n    lineBreak: lineBreak,\n    lineBreakG: lineBreakG,\n    nonASCIIwhitespace: nonASCIIwhitespace\n  };\n\n\n  function parse(input, options) {\n    return Parser.parse(input, options)\n  }\n\n\n  function parseExpressionAt(input, pos, options) {\n    return Parser.parseExpressionAt(input, pos, options)\n  }\n\n\n  function tokenizer(input, options) {\n    return Parser.tokenizer(input, options)\n  }\n\n  exports.Node = Node;\n  exports.Parser = Parser;\n  exports.Position = Position;\n  exports.SourceLocation = SourceLocation;\n  exports.TokContext = TokContext;\n  exports.Token = Token;\n  exports.TokenType = TokenType;\n  exports.defaultOptions = defaultOptions;\n  exports.getLineInfo = getLineInfo;\n  exports.isIdentifierChar = isIdentifierChar;\n  exports.isIdentifierStart = isIdentifierStart;\n  exports.isNewLine = isNewLine;\n  exports.keywordTypes = keywords;\n  exports.lineBreak = lineBreak;\n  exports.lineBreakG = lineBreakG;\n  exports.nonASCIIwhitespace = nonASCIIwhitespace;\n  exports.parse = parse;\n  exports.parseExpressionAt = parseExpressionAt;\n  exports.tokContexts = types;\n  exports.tokTypes = types$1;\n  exports.tokenizer = tokenizer;\n  exports.version = version;\n\n}));\n\n},{}],2:[function(require,module,exports){\n\n},{}],3:[function(require,module,exports){\nfunction glWiretap(gl, options = {}) {\n  const {\n    contextName = 'gl',\n    throwGetError,\n    useTrackablePrimitives,\n    readPixelsFile,\n    recording = [],\n    variables = {},\n    onReadPixels,\n    onUnrecognizedArgumentLookup,\n  } = options;\n  const proxy = new Proxy(gl, { get: listen });\n  const contextVariables = [];\n  const entityNames = {};\n  let imageCount = 0;\n  let indent = '';\n  let readPixelsVariableName;\n  return proxy;\n  function listen(obj, property) {\n    switch (property) {\n      case 'addComment': return addComment;\n      case 'checkThrowError': return checkThrowError;\n      case 'getReadPixelsVariableName': return readPixelsVariableName;\n      case 'insertVariable': return insertVariable;\n      case 'reset': return reset;\n      case 'setIndent': return setIndent;\n      case 'toString': return toString;\n      case 'getContextVariableName': return getContextVariableName;\n    }\n    if (typeof gl[property] === 'function') {\n      return function() { \n        switch (property) {\n          case 'getError':\n            if (throwGetError) {\n              recording.push(`${indent}if (${contextName}.getError() !== ${contextName}.NONE) throw new Error('error');`);\n            } else {\n              recording.push(`${indent}${contextName}.getError();`); \n            }\n            return gl.getError();\n          case 'getExtension': {\n            const variableName = `${contextName}Variables${contextVariables.length}`;\n            recording.push(`${indent}const ${variableName} = ${contextName}.getExtension('${arguments[0]}');`);\n            const extension = gl.getExtension(arguments[0]);\n            if (extension && typeof extension === 'object') {\n              const tappedExtension = glExtensionWiretap(extension, {\n                getEntity,\n                useTrackablePrimitives,\n                recording,\n                contextName: variableName,\n                contextVariables,\n                variables,\n                indent,\n                onUnrecognizedArgumentLookup,\n              });\n              contextVariables.push(tappedExtension);\n              return tappedExtension;\n            } else {\n              contextVariables.push(null);\n            }\n            return extension;\n          }\n          case 'readPixels':\n            const i = contextVariables.indexOf(arguments[6]);\n            let targetVariableName;\n            if (i === -1) {\n              const variableName = getVariableName(arguments[6]);\n              if (variableName) {\n                targetVariableName = variableName;\n                recording.push(`${indent}${variableName}`);\n              } else {\n                targetVariableName = `${contextName}Variable${contextVariables.length}`;\n                contextVariables.push(arguments[6]);\n                recording.push(`${indent}const ${targetVariableName} = new ${arguments[6].constructor.name}(${arguments[6].length});`);\n              }\n            } else {\n              targetVariableName = `${contextName}Variable${i}`;\n            }\n            readPixelsVariableName = targetVariableName;\n            const argumentAsStrings = [\n              arguments[0],\n              arguments[1],\n              arguments[2],\n              arguments[3],\n              getEntity(arguments[4]),\n              getEntity(arguments[5]),\n              targetVariableName\n            ];\n            recording.push(`${indent}${contextName}.readPixels(${argumentAsStrings.join(', ')});`);\n            if (readPixelsFile) {\n              writePPM(arguments[2], arguments[3]);\n            }\n            if (onReadPixels) {\n              onReadPixels(targetVariableName, argumentAsStrings);\n            }\n            return gl.readPixels.apply(gl, arguments);\n          case 'drawBuffers':\n            recording.push(`${indent}${contextName}.drawBuffers([${argumentsToString(arguments[0], { contextName, contextVariables, getEntity, addVariable, variables, onUnrecognizedArgumentLookup } )}]);`);\n            return gl.drawBuffers(arguments[0]);\n        }\n        let result = gl[property].apply(gl, arguments);\n        switch (typeof result) {\n          case 'undefined':\n            recording.push(`${indent}${methodCallToString(property, arguments)};`);\n            return;\n          case 'number':\n          case 'boolean':\n            if (useTrackablePrimitives && contextVariables.indexOf(trackablePrimitive(result)) === -1) {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result = trackablePrimitive(result));\n              break;\n            }\n          default:\n            if (result === null) {\n              recording.push(`${methodCallToString(property, arguments)};`);\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n            }\n\n            contextVariables.push(result);\n        }\n        return result;\n      }\n    }\n    entityNames[gl[property]] = property;\n    return gl[property];\n  }\n  function toString() {\n    return recording.join('\\n');\n  }\n  function reset() {\n    while (recording.length > 0) {\n      recording.pop();\n    }\n  }\n  function insertVariable(name, value) {\n    variables[name] = value;\n  }\n  function getEntity(value) {\n    const name = entityNames[value];\n    if (name) {\n      return contextName + '.' + name;\n    }\n    return value;\n  }\n  function setIndent(spaces) {\n    indent = ' '.repeat(spaces);\n  }\n  function addVariable(value, source) {\n    const variableName = `${contextName}Variable${contextVariables.length}`;\n    recording.push(`${indent}const ${variableName} = ${source};`);\n    contextVariables.push(value);\n    return variableName;\n  }\n  function writePPM(width, height) {\n    const sourceVariable = `${contextName}Variable${contextVariables.length}`;\n    const imageVariable = `imageDatum${imageCount}`;\n    recording.push(`${indent}let ${imageVariable} = [\"P3\\\\n# ${readPixelsFile}.ppm\\\\n\", ${width}, ' ', ${height}, \"\\\\n255\\\\n\"].join(\"\");`);\n    recording.push(`${indent}for (let i = 0; i < ${imageVariable}.length; i += 4) {`);\n    recording.push(`${indent}  ${imageVariable} += ${sourceVariable}[i] + ' ' + ${sourceVariable}[i + 1] + ' ' + ${sourceVariable}[i + 2] + ' ';`);\n    recording.push(`${indent}}`);\n    recording.push(`${indent}if (typeof require !== \"undefined\") {`);\n    recording.push(`${indent}  require('fs').writeFileSync('./${readPixelsFile}.ppm', ${imageVariable});`);\n    recording.push(`${indent}}`);\n    imageCount++;\n  }\n  function addComment(value) {\n    recording.push(`${indent}// ${value}`);\n  }\n  function checkThrowError() {\n    recording.push(`${indent}(() => {\n${indent}const error = ${contextName}.getError();\n${indent}if (error !== ${contextName}.NONE) {\n${indent}  const names = Object.getOwnPropertyNames(gl);\n${indent}  for (let i = 0; i < names.length; i++) {\n${indent}    const name = names[i];\n${indent}    if (${contextName}[name] === error) {\n${indent}      throw new Error('${contextName} threw ' + name);\n${indent}    }\n${indent}  }\n${indent}}\n${indent}})();`);\n  }\n  function methodCallToString(method, args) {\n    return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity, addVariable, variables, onUnrecognizedArgumentLookup })})`;\n  }\n\n  function getVariableName(value) {\n    if (variables) {\n      for (const name in variables) {\n        if (variables[name] === value) {\n          return name;\n        }\n      }\n    }\n    return null;\n  }\n\n  function getContextVariableName(value) {\n    const i = contextVariables.indexOf(value);\n    if (i !== -1) {\n      return `${contextName}Variable${i}`;\n    }\n    return null;\n  }\n}\n\nfunction glExtensionWiretap(extension, options) {\n  const proxy = new Proxy(extension, { get: listen });\n  const extensionEntityNames = {};\n  const {\n    contextName,\n    contextVariables,\n    getEntity,\n    useTrackablePrimitives,\n    recording,\n    variables,\n    indent,\n    onUnrecognizedArgumentLookup,\n  } = options;\n  return proxy;\n  function listen(obj, property) {\n    if (typeof obj[property] === 'function') {\n      return function() {\n        switch (property) {\n          case 'drawBuffersWEBGL':\n            recording.push(`${indent}${contextName}.drawBuffersWEBGL([${argumentsToString(arguments[0], { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables, onUnrecognizedArgumentLookup })}]);`);\n            return extension.drawBuffersWEBGL(arguments[0]);\n        }\n        let result = extension[property].apply(extension, arguments);\n        switch (typeof result) {\n          case 'undefined':\n            recording.push(`${indent}${methodCallToString(property, arguments)};`);\n            return;\n          case 'number':\n          case 'boolean':\n            if (useTrackablePrimitives && contextVariables.indexOf(trackablePrimitive(result)) === -1) {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result = trackablePrimitive(result));\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n              contextVariables.push(result);\n            }\n            break;\n          default:\n            if (result === null) {\n              recording.push(`${methodCallToString(property, arguments)};`);\n            } else {\n              recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`);\n            }\n            contextVariables.push(result);\n        }\n        return result;\n      };\n    }\n    extensionEntityNames[extension[property]] = property;\n    return extension[property];\n  }\n\n  function getExtensionEntity(value) {\n    if (extensionEntityNames.hasOwnProperty(value)) {\n      return `${contextName}.${extensionEntityNames[value]}`;\n    }\n    return getEntity(value);\n  }\n\n  function methodCallToString(method, args) {\n    return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables, onUnrecognizedArgumentLookup })})`;\n  }\n\n  function addVariable(value, source) {\n    const variableName = `${contextName}Variable${contextVariables.length}`;\n    contextVariables.push(value);\n    recording.push(`${indent}const ${variableName} = ${source};`);\n    return variableName;\n  }\n}\n\nfunction argumentsToString(args, options) {\n  const { variables, onUnrecognizedArgumentLookup } = options;\n  return (Array.from(args).map((arg) => {\n    const variableName = getVariableName(arg);\n    if (variableName) {\n      return variableName;\n    }\n    return argumentToString(arg, options);\n  }).join(', '));\n\n  function getVariableName(value) {\n    if (variables) {\n      for (const name in variables) {\n        if (!variables.hasOwnProperty(name)) continue;\n        if (variables[name] === value) {\n          return name;\n        }\n      }\n    }\n    if (onUnrecognizedArgumentLookup) {\n      return onUnrecognizedArgumentLookup(value);\n    }\n    return null;\n  }\n}\n\nfunction argumentToString(arg, options) {\n  const { contextName, contextVariables, getEntity, addVariable, onUnrecognizedArgumentLookup } = options;\n  if (typeof arg === 'undefined') {\n    return 'undefined';\n  }\n  if (arg === null) {\n    return 'null';\n  }\n  const i = contextVariables.indexOf(arg);\n  if (i > -1) {\n    return `${contextName}Variable${i}`;\n  }\n  switch (arg.constructor.name) {\n    case 'String':\n      const hasLines = /\\n/.test(arg);\n      const hasSingleQuotes = /'/.test(arg);\n      const hasDoubleQuotes = /\"/.test(arg);\n      if (hasLines) {\n        return '`' + arg + '`';\n      } else if (hasSingleQuotes && !hasDoubleQuotes) {\n        return '\"' + arg + '\"';\n      } else if (!hasSingleQuotes && hasDoubleQuotes) {\n        return \"'\" + arg + \"'\";\n      } else {\n        return '\\'' + arg + '\\'';\n      }\n    case 'Number': return getEntity(arg);\n    case 'Boolean': return getEntity(arg);\n    case 'Array':\n      return addVariable(arg, `new ${arg.constructor.name}([${Array.from(arg).join(',')}])`);\n    case 'Float32Array':\n    case 'Uint8Array':\n    case 'Uint16Array':\n    case 'Int32Array':\n      return addVariable(arg, `new ${arg.constructor.name}(${JSON.stringify(Array.from(arg))})`);\n    default:\n      if (onUnrecognizedArgumentLookup) {\n        const instantiationString = onUnrecognizedArgumentLookup(arg);\n        if (instantiationString) {\n          return instantiationString;\n        }\n      }\n      throw new Error(`unrecognized argument type ${arg.constructor.name}`);\n  }\n}\n\nfunction trackablePrimitive(value) {\n  return new value.constructor(value);\n}\n\nif (typeof module !== 'undefined') {\n  module.exports = { glWiretap, glExtensionWiretap };\n}\n\nif (typeof window !== 'undefined') {\n  glWiretap.glExtensionWiretap = glExtensionWiretap;\n  window.glWiretap = glWiretap;\n}\n\n},{}],4:[function(require,module,exports){\nfunction setupArguments(args) {\n  const newArguments = new Array(args.length);\n  for (let i = 0; i < args.length; i++) {\n    const arg = args[i];\n    if (arg.toArray) {\n      newArguments[i] = arg.toArray();\n    } else {\n      newArguments[i] = arg;\n    }\n  }\n  return newArguments;\n}\n\nfunction mock1D() {\n  const args = setupArguments(arguments);\n  const row = new Float32Array(this.output.x);\n  for (let x = 0; x < this.output.x; x++) {\n    this.thread.x = x;\n    this.thread.y = 0;\n    this.thread.z = 0;\n    row[x] = this._fn.apply(this, args);\n  }\n  return row;\n}\n\nfunction mock2D() {\n  const args = setupArguments(arguments);\n  const matrix = new Array(this.output.y);\n  for (let y = 0; y < this.output.y; y++) {\n    const row = new Float32Array(this.output.x);\n    for (let x = 0; x < this.output.x; x++) {\n      this.thread.x = x;\n      this.thread.y = y;\n      this.thread.z = 0;\n      row[x] = this._fn.apply(this, args);\n    }\n    matrix[y] = row;\n  }\n  return matrix;\n}\n\nfunction mock2DGraphical() {\n  const args = setupArguments(arguments);\n  for (let y = 0; y < this.output.y; y++) {\n    for (let x = 0; x < this.output.x; x++) {\n      this.thread.x = x;\n      this.thread.y = y;\n      this.thread.z = 0;\n      this._fn.apply(this, args);\n    }\n  }\n}\n\nfunction mock3D() {\n  const args = setupArguments(arguments);\n  const cube = new Array(this.output.z);\n  for (let z = 0; z < this.output.z; z++) {\n    const matrix = new Array(this.output.y);\n    for (let y = 0; y < this.output.y; y++) {\n      const row = new Float32Array(this.output.x);\n      for (let x = 0; x < this.output.x; x++) {\n        this.thread.x = x;\n        this.thread.y = y;\n        this.thread.z = z;\n        row[x] = this._fn.apply(this, args);\n      }\n      matrix[y] = row;\n    }\n    cube[z] = matrix;\n  }\n  return cube;\n}\n\nfunction apiDecorate(kernel) {\n  kernel.setOutput = (output) => {\n    kernel.output = setupOutput(output);\n    if (kernel.graphical) {\n      setupGraphical(kernel);\n    }\n  };\n  kernel.toJSON = () => {\n    throw new Error('Not usable with gpuMock');\n  };\n  kernel.setConstants = (flag) => {\n    kernel.constants = flag;\n    return kernel;\n  };\n  kernel.setGraphical = (flag) => {\n    kernel.graphical = flag;\n    return kernel;\n  };\n  kernel.setCanvas = (flag) => {\n    kernel.canvas = flag;\n    return kernel;\n  };\n  kernel.setContext = (flag) => {\n    kernel.context = flag;\n    return kernel;\n  };\n  kernel.destroy = () => {};\n  kernel.validateSettings = () => {};\n  if (kernel.graphical && kernel.output) {\n    setupGraphical(kernel);\n  }\n  kernel.exec = function() {\n    return new Promise((resolve, reject) => {\n      try {\n        resolve(kernel.apply(kernel, arguments));\n      } catch(e) {\n        reject(e);\n      }\n    });\n  };\n  kernel.getPixels = (flip) => {\n    const {x, y} = kernel.output;\n    return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0);\n  };\n  kernel.color = function(r, g, b, a) {\n    if (typeof a === 'undefined') {\n      a = 1;\n    }\n\n    r = Math.floor(r * 255);\n    g = Math.floor(g * 255);\n    b = Math.floor(b * 255);\n    a = Math.floor(a * 255);\n\n    const width = kernel.output.x;\n    const height = kernel.output.y;\n\n    const x = kernel.thread.x;\n    const y = height - kernel.thread.y - 1;\n\n    const index = x + y * width;\n\n    kernel._colorData[index * 4 + 0] = r;\n    kernel._colorData[index * 4 + 1] = g;\n    kernel._colorData[index * 4 + 2] = b;\n    kernel._colorData[index * 4 + 3] = a;\n  };\n\n  const mockMethod = () => kernel;\n  const methods = [\n    'setWarnVarUsage',\n    'setArgumentTypes',\n    'setTactic',\n    'setOptimizeFloatMemory',\n    'setDebug',\n    'setLoopMaxIterations',\n    'setConstantTypes',\n    'setFunctions',\n    'setNativeFunctions',\n    'setInjectedNative',\n    'setPipeline',\n    'setPrecision',\n    'setOutputToTexture',\n    'setImmutable',\n    'setStrictIntegers',\n    'setDynamicOutput',\n    'setHardcodeConstants',\n    'setDynamicArguments',\n    'setUseLegacyEncoder',\n    'setWarnVarUsage',\n    'addSubKernel',\n  ];\n  for (let i = 0; i < methods.length; i++) {\n    kernel[methods[i]] = mockMethod;\n  }\n  return kernel;\n}\n\nfunction setupGraphical(kernel) {\n  const {x, y} = kernel.output;\n  if (kernel.context && kernel.context.createImageData) {\n    const data = new Uint8ClampedArray(x * y * 4);\n    kernel._imageData = kernel.context.createImageData(x, y);\n    kernel._colorData = data;\n  } else {\n    const data = new Uint8ClampedArray(x * y * 4);\n    kernel._imageData = { data };\n    kernel._colorData = data;\n  }\n}\n\nfunction setupOutput(output) {\n  let result = null;\n  if (output.length) {\n    if (output.length === 3) {\n      const [x,y,z] = output;\n      result = { x, y, z };\n    } else if (output.length === 2) {\n      const [x,y] = output;\n      result = { x, y };\n    } else {\n      const [x] = output;\n      result = { x };\n    }\n  } else {\n    result = output;\n  }\n  return result;\n}\n\nfunction gpuMock(fn, settings = {}) {\n  const output = settings.output ? setupOutput(settings.output) : null;\n  function kernel() {\n    if (kernel.output.z) {\n      return mock3D.apply(kernel, arguments);\n    } else if (kernel.output.y) {\n      if (kernel.graphical) {\n        return mock2DGraphical.apply(kernel, arguments);\n      }\n      return mock2D.apply(kernel, arguments);\n    } else {\n      return mock1D.apply(kernel, arguments);\n    }\n  }\n  kernel._fn = fn;\n  kernel.constants = settings.constants || null;\n  kernel.context = settings.context || null;\n  kernel.canvas = settings.canvas || null;\n  kernel.graphical = settings.graphical || false;\n  kernel._imageData = null;\n  kernel._colorData = null;\n  kernel.output = output;\n  kernel.thread = {\n    x: 0,\n    y: 0,\n    z: 0\n  };\n  return apiDecorate(kernel);\n}\n\nfunction flipPixels(pixels, width, height) {\n  const halfHeight = height / 2 | 0; \n  const bytesPerRow = width * 4;\n  const temp = new Uint8ClampedArray(width * 4);\n  const result = pixels.slice(0);\n  for (let y = 0; y < halfHeight; ++y) {\n    const topOffset = y * bytesPerRow;\n    const bottomOffset = (height - y - 1) * bytesPerRow;\n\n    temp.set(result.subarray(topOffset, topOffset + bytesPerRow));\n\n    result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);\n\n    result.set(temp, bottomOffset);\n  }\n  return result;\n}\n\nmodule.exports = {\n  gpuMock\n};\n\n},{}],5:[function(require,module,exports){\nconst { utils } = require('./utils');\n\nfunction alias(name, source) {\n  const fnString = source.toString();\n  return new Function(`return function ${ name } (${ utils.getArgumentNamesFromString(fnString).join(', ') }) {\n  ${ utils.getFunctionBodyFromString(fnString) }\n}`)();\n}\n\nmodule.exports = {\n  alias\n};\n},{\"./utils\":114}],6:[function(require,module,exports){\nconst { FunctionNode } = require('../function-node');\n\nclass CPUFunctionNode extends FunctionNode {\n  astFunction(ast, retArr) {\n\n    if (!this.isRootKernel) {\n      retArr.push('function');\n      retArr.push(' ');\n      retArr.push(this.name);\n      retArr.push('(');\n\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        retArr.push('user_');\n        retArr.push(argumentName);\n      }\n\n      retArr.push(') {\\n');\n    }\n\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    if (!this.isRootKernel) {\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astReturnStatement(ast, retArr) {\n    const type = this.returnType || this.getType(ast.argument);\n\n    if (!this.returnType) {\n      this.returnType = type;\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(this.leadingReturnStatement);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';\\n');\n      retArr.push(this.followingReturnStatement);\n      retArr.push('continue;\\n');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = `);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push('return ');\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  astLiteral(ast, retArr) {\n\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    retArr.push(ast.value);\n\n    return retArr;\n  }\n\n  astBinaryExpression(ast, retArr) {\n    retArr.push('(');\n    this.astGeneric(ast.left, retArr);\n    retArr.push(ast.operator);\n    this.astGeneric(ast.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    switch (idtNode.name) {\n      case 'Infinity':\n        retArr.push('Infinity');\n        break;\n      default:\n        if (this.constants && this.constants.hasOwnProperty(idtNode.name)) {\n          retArr.push('constants_' + idtNode.name);\n        } else {\n          retArr.push('user_' + idtNode.name);\n        }\n    }\n\n    return retArr;\n  }\n\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      this.pushState('in-for-loop-init');\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < initArr.length; i++) {\n        if (initArr[i].includes && initArr[i].includes(',')) {\n          isSafe = false;\n        }\n      }\n      this.popState('in-for-loop-init');\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), ';\\n');\n      }\n      retArr.push(`for (let ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        whileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    retArr.push('if (');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') {\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('} else {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        doWhileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n\n  }\n\n  astAssignmentExpression(assNode, retArr) {\n    const declaration = this.getDeclaration(assNode.left);\n    if (declaration && !declaration.assignable) {\n      throw this.astErrorOutput(`Variable ${assNode.left.name} is not assignable here`, assNode);\n    }\n    this.astGeneric(assNode.left, retArr);\n    retArr.push(assNode.operator);\n    this.astGeneric(assNode.right, retArr);\n    return retArr;\n  }\n\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); \n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astVariableDeclaration(varDecNode, retArr) {\n    retArr.push(`${varDecNode.kind} `);\n    const { declarations } = varDecNode;\n    for (let i = 0; i < declarations.length; i++) {\n      if (i > 0) {\n        retArr.push(',');\n      }\n      const declaration = declarations[i];\n      const info = this.getDeclaration(declaration.id);\n      if (!info.valueType) {\n        info.valueType = this.getType(declaration.init);\n      }\n      this.astGeneric(declaration, retArr);\n    }\n    if (!this.isState('in-for-loop-init')) {\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n\n  }\n\n  astSwitchStatement(ast, retArr) {\n    const { discriminant, cases } = ast;\n    retArr.push('switch (');\n    this.astGeneric(discriminant, retArr);\n    retArr.push(') {\\n');\n    for (let i = 0; i < cases.length; i++) {\n      if (cases[i].test === null) {\n        retArr.push('default:\\n');\n        this.astGeneric(cases[i].consequent, retArr);\n        if (cases[i].consequent && cases[i].consequent.length > 0) {\n          retArr.push('break;\\n');\n        }\n        continue;\n      }\n      retArr.push('case ');\n      this.astGeneric(cases[i].test, retArr);\n      retArr.push(':\\n');\n      if (cases[i].consequent && cases[i].consequent.length > 0) {\n        this.astGeneric(cases[i].consequent, retArr);\n        retArr.push('break;\\n');\n      }\n    }\n    retArr.push('\\n}');\n  }\n\n  astThisExpression(tNode, retArr) {\n    retArr.push('_this');\n    return retArr;\n  }\n\n  astMemberExpression(mNode, retArr) {\n    const {\n      signature,\n      type,\n      property,\n      xProperty,\n      yProperty,\n      zProperty,\n      name,\n      origin\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'this.thread.value':\n        retArr.push(`_this.thread.${ name }`);\n        return retArr;\n      case 'this.output.value':\n        switch (name) {\n          case 'x':\n            retArr.push('outputX');\n            break;\n          case 'y':\n            retArr.push('outputY');\n            break;\n          case 'z':\n            retArr.push('outputZ');\n            break;\n          default:\n            throw this.astErrorOutput('Unexpected expression', mNode);\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ name }[0]`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ name }[1]`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ name }[2]`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ name }[3]`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n      case 'this.constants.value[]':\n      case 'this.constants.value[][]':\n      case 'this.constants.value[][][]':\n        break;\n      case 'fn()[]':\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      case 'fn()[][]':\n        this.astGeneric(mNode.object.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.object.property, retArr);\n        retArr.push(']');\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      default:\n        throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (!mNode.computed) {\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${name}`);\n          return retArr;\n      }\n    }\n\n    const markupName = `${origin}_${name}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'HTMLImageArray':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n      case 'HTMLImage':\n      default:\n        let size;\n        let isInput;\n        if (origin === 'constants') {\n          const constant = this.constants[name];\n          isInput = this.constantTypes[name] === 'Input';\n          size = isInput ? constant.size : null;\n        } else {\n          isInput = this.isInput(name);\n          size = isInput ? this.argumentSizes[this.argumentNames.indexOf(name)] : null;\n        }\n        retArr.push(`${ markupName }`);\n        if (zProperty && yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? '(outputY * outputX)' : size[1] * size[0] })+(`);\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (typeof xProperty !== 'undefined') {\n          retArr.push('[');\n          this.astGeneric(xProperty, retArr);\n          retArr.push(']');\n        }\n    }\n    return retArr;\n  }\n\n  astCallExpression(ast, retArr) {\n    if (ast.type !== 'CallExpression') {\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n    let functionName = this.astMemberExpressionUnroll(ast.callee);\n\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    retArr.push(functionName);\n\n    retArr.push('(');\n    const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n    for (let i = 0; i < ast.arguments.length; ++i) {\n      const argument = ast.arguments[i];\n\n      let argumentType = this.getType(argument);\n      if (!targetTypes[i]) {\n        this.triggerImplyArgumentType(functionName, i, argumentType, this);\n      }\n\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      this.astGeneric(argument, retArr);\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n    const arrLen = arrNode.elements.length;\n    const elements = [];\n    for (let i = 0; i < arrLen; ++i) {\n      const element = [];\n      this.astGeneric(arrNode.elements[i], element);\n      elements.push(element.join(''));\n    }\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`[${elements.join(', ')}]`);\n        break;\n      default:\n        retArr.push(`new Float32Array([${elements.join(', ')}])`);\n    }\n    return retArr;\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    retArr.push('debugger;');\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  CPUFunctionNode\n};\n},{\"../function-node\":10}],7:[function(require,module,exports){\nconst { utils } = require('../../utils');\n\nfunction constantsToString(constants, types) {\n  const results = [];\n  for (const name in types) {\n    if (!types.hasOwnProperty(name)) continue;\n    const type = types[name];\n    const constant = constants[name];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n        results.push(`${name}:${constant}`);\n        break;\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        results.push(`${name}:new ${constant.constructor.name}(${JSON.stringify(Array.from(constant))})`);\n        break;\n    }\n  }\n  return `{ ${ results.join() } }`;\n}\n\nfunction cpuKernelString(cpuKernel, name) {\n  const header = [];\n  const thisProperties = [];\n  const beforeReturn = [];\n\n  const useFunctionKeyword = !/^function/.test(cpuKernel.color.toString());\n\n  header.push(\n    '  const { context, canvas, constants: incomingConstants } = settings;',\n    `  const output = new Int32Array(${JSON.stringify(Array.from(cpuKernel.output))});`,\n    `  const _constantTypes = ${JSON.stringify(cpuKernel.constantTypes)};`,\n    `  const _constants = ${constantsToString(cpuKernel.constants, cpuKernel.constantTypes)};`\n  );\n\n  thisProperties.push(\n    '    constants: _constants,',\n    '    context,',\n    '    output,',\n    '    thread: {x: 0, y: 0, z: 0},'\n  );\n\n  if (cpuKernel.graphical) {\n    header.push(`  const _imageData = context.createImageData(${cpuKernel.output[0]}, ${cpuKernel.output[1]});`);\n    header.push(`  const _colorData = new Uint8ClampedArray(${cpuKernel.output[0]} * ${cpuKernel.output[1]} * 4);`);\n\n    const colorFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.color.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: (object, name) => {\n        return null;\n      }\n    });\n\n    const getPixelsFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.getPixels.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: () => {\n        return null;\n      }\n    });\n\n    thisProperties.push(\n      '    _imageData,',\n      '    _colorData,',\n      `    color: ${colorFn},`\n    );\n\n    beforeReturn.push(\n      `  kernel.getPixels = ${getPixelsFn};`\n    );\n  }\n\n  const constantTypes = [];\n  const constantKeys = Object.keys(cpuKernel.constantTypes);\n  for (let i = 0; i < constantKeys.length; i++) {\n    constantTypes.push(cpuKernel.constantTypes[constantKeys]);\n  }\n  if (cpuKernel.argumentTypes.indexOf('HTMLImageArray') !== -1 || constantTypes.indexOf('HTMLImageArray') !== -1) {\n    const flattenedImageTo3DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._imageTo3DArray.toString(), {\n      doNotDefine: ['canvas'],\n      findDependency: (object, name) => {\n        if (object === 'this') {\n          return (useFunctionKeyword ? 'function ' : '') + cpuKernel[name].toString();\n        }\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return;\n          case 'context':\n            return 'context';\n        }\n      }\n    });\n    beforeReturn.push(flattenedImageTo3DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n    thisProperties.push(`    _imageTo3DArray,`);\n  } else if (cpuKernel.argumentTypes.indexOf('HTMLImage') !== -1 || constantTypes.indexOf('HTMLImage') !== -1) {\n    const flattenedImageTo2DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._mediaTo2DArray.toString(), {\n      findDependency: (object, name) => {\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return 'settings.canvas';\n          case 'context':\n            return 'settings.context';\n        }\n        throw new Error('unhandled thisLookup');\n      }\n    });\n    beforeReturn.push(flattenedImageTo2DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n  }\n\n  return `function(settings) {\n${ header.join('\\n') }\n  for (const p in _constantTypes) {\n    if (!_constantTypes.hasOwnProperty(p)) continue;\n    const type = _constantTypes[p];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        if (incomingConstants.hasOwnProperty(p)) {\n          console.warn('constant ' + p + ' of type ' + type + ' cannot be resigned');\n        }\n        continue;\n    }\n    if (!incomingConstants.hasOwnProperty(p)) {\n      throw new Error('constant ' + p + ' not found');\n    }\n    _constants[p] = incomingConstants[p];\n  }\n  const kernel = (function() {\n${cpuKernel._kernelString}\n  })\n    .apply({ ${thisProperties.join('\\n')} });\n  ${ beforeReturn.join('\\n') }\n  return kernel;\n}`;\n}\n\nmodule.exports = {\n  cpuKernelString\n};\n},{\"../../utils\":114}],8:[function(require,module,exports){\nconst { Kernel } = require('../kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { CPUFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst { cpuKernelString } = require('./kernel-string');\n\nclass CPUKernel extends Kernel {\n  static getFeatures() {\n    return this.features;\n  }\n  static get features() {\n    return Object.freeze({\n      kernelMap: true,\n      isIntegerDivisionAccurate: true\n    });\n  }\n  static get isSupported() {\n    return true;\n  }\n  static isContextMatch(context) {\n    return false;\n  }\n  static get mode() {\n    return 'cpu';\n  }\n\n  static nativeFunctionArguments() {\n    return null;\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`Looking up native function return type not supported on ${this.name}`);\n  }\n\n  static combineKernels(combinedKernel) {\n    return combinedKernel;\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return 'cpu' + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.mergeSettings(source.settings || settings);\n\n    this._imageData = null;\n    this._colorData = null;\n    this._kernelString = null;\n    this._prependedString = [];\n    this.thread = {\n      x: 0,\n      y: 0,\n      z: 0\n    };\n    this.translatedSources = null;\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      return document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  initContext() {\n    if (!this.canvas) return null;\n    return this.canvas.getContext('2d');\n  }\n\n  initPlugins(settings) {\n    return [];\n  }\n\n  validateSettings(args) {\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      if (argType === 'Array') {\n        this.output = utils.getDimensions(argType);\n      } else if (argType === 'NumberTexture' || argType === 'ArrayTexture(4)') {\n        this.output = args[0].output;\n      } else {\n        throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n    }\n\n    this.checkOutput();\n  }\n\n  translateSource() {\n    this.leadingReturnStatement = this.output.length > 1 ? 'resultX[x] = ' : 'result[x] = ';\n    if (this.subKernels) {\n      const followingReturnStatement = [];\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const {\n          name\n        } = this.subKernels[i];\n        followingReturnStatement.push(this.output.length > 1 ? `resultX_${ name }[x] = subKernelResult_${ name };\\n` : `result_${ name }[x] = subKernelResult_${ name };\\n`);\n      }\n      this.followingReturnStatement = followingReturnStatement.join('');\n    }\n    const functionBuilder = FunctionBuilder.fromKernel(this, CPUFunctionNode);\n    this.translatedSources = functionBuilder.getPrototypes('kernel');\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n  }\n\n  build() {\n    if (this.built) return;\n    this.setupConstants();\n    this.setupArguments(arguments);\n    this.validateSettings(arguments);\n    this.translateSource();\n\n    if (this.graphical) {\n      const {\n        canvas,\n        output\n      } = this;\n      if (!canvas) {\n        throw new Error('no canvas available for using graphical output');\n      }\n      const width = output[0];\n      const height = output[1] || 1;\n      canvas.width = width;\n      canvas.height = height;\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n\n    const kernelString = this.getKernelString();\n    this.kernelString = kernelString;\n\n    if (this.debug) {\n      console.log('Function output:');\n      console.log(kernelString);\n    }\n\n    try {\n      this.run = new Function([], kernelString).bind(this)();\n    } catch (e) {\n      console.error('An error occurred compiling the javascript: ', e);\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  color(r, g, b, a) {\n    if (typeof a === 'undefined') {\n      a = 1;\n    }\n\n    r = Math.floor(r * 255);\n    g = Math.floor(g * 255);\n    b = Math.floor(b * 255);\n    a = Math.floor(a * 255);\n\n    const width = this.output[0];\n    const height = this.output[1];\n\n    const x = this.thread.x;\n    const y = height - this.thread.y - 1;\n\n    const index = x + y * width;\n\n    this._colorData[index * 4 + 0] = r;\n    this._colorData[index * 4 + 1] = g;\n    this._colorData[index * 4 + 2] = b;\n    this._colorData[index * 4 + 3] = a;\n  }\n\n  getKernelString() {\n    if (this._kernelString !== null) return this._kernelString;\n\n    let kernelThreadString = null;\n    let {\n      translatedSources\n    } = this;\n    if (translatedSources.length > 1) {\n      translatedSources = translatedSources.filter(fn => {\n        if (/^function/.test(fn)) return fn;\n        kernelThreadString = fn;\n        return false;\n      });\n    } else {\n      kernelThreadString = translatedSources.shift();\n    }\n    return this._kernelString = `  const LOOP_MAX = ${ this._getLoopMaxString() };\n  ${ this.injectedNative || '' }\n  const _this = this;\n  ${ this._resultKernelHeader() }\n  ${ this._processConstants() }\n  return (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) => {\n    ${ this._prependedString.join('') }\n    ${ this._earlyThrows() }\n    ${ this._processArguments() }\n    ${ this.graphical ? this._graphicalKernelBody(kernelThreadString) : this._resultKernelBody(kernelThreadString) }\n    ${ translatedSources.length > 0 ? translatedSources.join('\\n') : '' }\n  };`;\n  }\n\n  toString() {\n    return cpuKernelString(this);\n  }\n\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${ parseInt(this.loopMaxIterations) };` :\n      ' 1000;'\n    );\n  }\n\n  _processConstants() {\n    if (!this.constants) return '';\n\n    const result = [];\n    for (let p in this.constants) {\n      const type = this.constantTypes[p];\n      switch (type) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    const constants_${p} = this._mediaTo2DArray(this.constants.${p});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    const constants_${p} = this._imageTo3DArray(this.constants.${p});\\n`);\n          break;\n        case 'Input':\n          result.push(`    const constants_${p} = this.constants.${p}.value;\\n`);\n          break;\n        default:\n          result.push(`    const constants_${p} = this.constants.${p};\\n`);\n      }\n    }\n    return result.join('');\n  }\n\n  _earlyThrows() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    const arrayArguments = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      if (this.argumentTypes[i] === 'Array') {\n        arrayArguments.push(this.argumentNames[i]);\n      }\n    }\n    if (arrayArguments.length === 0) return '';\n    const checks = [];\n    for (let i = 0; i < arrayArguments.length; i++) {\n      const argumentName = arrayArguments[i];\n      const checkSubKernels = this._mapSubKernels(subKernel => `user_${argumentName} === result_${subKernel.name}`).join(' || ');\n      checks.push(`user_${argumentName} === result${checkSubKernels ? ` || ${checkSubKernels}` : ''}`);\n    }\n    return `if (${checks.join(' || ')}) throw new Error('Source and destination arrays are the same.  Use immutable = true');`;\n  }\n\n  _processArguments() {\n    const result = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      const variableName = `user_${this.argumentNames[i]}`;\n      switch (this.argumentTypes[i]) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    ${variableName} = this._mediaTo2DArray(${variableName});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    ${variableName} = this._imageTo3DArray(${variableName});\\n`);\n          break;\n        case 'Input':\n          result.push(`    ${variableName} = ${variableName}.value;\\n`);\n          break;\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n          result.push(`\n    if (${variableName}.toArray) {\n      if (!_this.textureCache) {\n        _this.textureCache = [];\n        _this.arrayCache = [];\n      }\n      const textureIndex = _this.textureCache.indexOf(${variableName});\n      if (textureIndex !== -1) {\n        ${variableName} = _this.arrayCache[textureIndex];\n      } else {\n        _this.textureCache.push(${variableName});\n        ${variableName} = ${variableName}.toArray();\n        _this.arrayCache.push(${variableName});\n      }\n    }`);\n          break;\n      }\n    }\n    return result.join('');\n  }\n\n  _mediaTo2DArray(media) {\n    const canvas = this.canvas;\n    const width = media.width > 0 ? media.width : media.videoWidth;\n    const height = media.height > 0 ? media.height : media.videoHeight;\n    if (canvas.width < width) {\n      canvas.width = width;\n    }\n    if (canvas.height < height) {\n      canvas.height = height;\n    }\n    const ctx = this.context;\n    let pixelsData;\n    if (media.constructor === ImageData) {\n      pixelsData = media.data;\n    } else {\n      ctx.drawImage(media, 0, 0, width, height);\n      pixelsData = ctx.getImageData(0, 0, width, height).data;\n    }\n    const imageArray = new Array(height);\n    let index = 0;\n    for (let y = height - 1; y >= 0; y--) {\n      const row = imageArray[y] = new Array(width);\n      for (let x = 0; x < width; x++) {\n        const pixel = new Float32Array(4);\n        pixel[0] = pixelsData[index++] / 255; \n        pixel[1] = pixelsData[index++] / 255; \n        pixel[2] = pixelsData[index++] / 255; \n        pixel[3] = pixelsData[index++] / 255; \n        row[x] = pixel;\n      }\n    }\n    return imageArray;\n  }\n\n  getPixels(flip) {\n    const [width, height] = this.output;\n    return flip ? utils.flipPixels(this._imageData.data, width, height) : this._imageData.data.slice(0);\n  }\n\n  _imageTo3DArray(images) {\n    const imagesArray = new Array(images.length);\n    for (let i = 0; i < images.length; i++) {\n      imagesArray[i] = this._mediaTo2DArray(images[i]);\n    }\n    return imagesArray;\n  }\n\n  _resultKernelHeader() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    switch (this.output.length) {\n      case 1:\n        return this._mutableKernel1DResults();\n      case 2:\n        return this._mutableKernel2DResults();\n      case 3:\n        return this._mutableKernel3DResults();\n    }\n  }\n\n  _resultKernelBody(kernelString) {\n    switch (this.output.length) {\n      case 1:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel1DLoop(kernelString) : this._resultImmutableKernel1DLoop(kernelString)) + this._kernelOutput();\n      case 2:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel2DLoop(kernelString) : this._resultImmutableKernel2DLoop(kernelString)) + this._kernelOutput();\n      case 3:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel3DLoop(kernelString) : this._resultImmutableKernel3DLoop(kernelString)) + this._kernelOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalKernelBody(kernelThreadString) {\n    switch (this.output.length) {\n      case 2:\n        return this._graphicalKernel2DLoop(kernelThreadString) + this._graphicalOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalOutput() {\n    return `\n    this._imageData.data.set(this._colorData);\n    this.context.putImageData(this._imageData, 0, 0);\n    return;`\n  }\n\n  _getKernelResultTypeConstructorString() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return 'Float32Array';\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        return 'Array';\n      default:\n        if (this.graphical) {\n          return 'Float32Array';\n        }\n        throw new Error(`unhandled returnType ${ this.returnType }`);\n    }\n  }\n\n  _resultImmutableKernel1DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _mutableKernel1DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }`;\n  }\n\n  _resultMutableKernel1DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _resultImmutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _mutableKernel2DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n    }`;\n  }\n\n  _resultMutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y];\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _graphicalKernel2DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _resultImmutableKernel3DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _mutableKernel3DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n      }\n    }`;\n  }\n\n  _resultMutableKernel3DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z];\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y];\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _kernelOutput() {\n    if (!this.subKernels) {\n      return '\\n    return result;';\n    }\n    return `\\n    return {\n      result: result,\n      ${ this.subKernels.map(subKernel => `${ subKernel.property }: result_${ subKernel.name }`).join(',\\n      ') }\n    };`;\n  }\n\n  _mapSubKernels(fn) {\n    return this.subKernels === null ? [''] :\n      this.subKernels.map(fn);\n  }\n\n  destroy(removeCanvasReference) {\n    if (removeCanvasReference) {\n      delete this.canvas;\n    }\n  }\n\n  static destroyContext(context) {}\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, CPUFunctionNode).toJSON();\n    return json;\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    const [width, height] = this.output;\n    if (this.graphical) {\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n  }\n\n  prependString(value) {\n    if (this._kernelString) throw new Error('Kernel already built');\n    this._prependedString.push(value);\n  }\n\n  hasPrependString(value) {\n    return this._prependedString.indexOf(value) > -1;\n  }\n}\n\nmodule.exports = {\n  CPUKernel\n};\n},{\"../../utils\":114,\"../function-builder\":9,\"../kernel\":36,\"./function-node\":6,\"./kernel-string\":7}],9:[function(require,module,exports){\nclass FunctionBuilder {\n  static fromKernel(kernel, FunctionNode, extraNodeOptions) {\n    const {\n      kernelArguments,\n      kernelConstants,\n      argumentNames,\n      argumentSizes,\n      argumentBitRatios,\n      constants,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      nativeFunctions,\n      output,\n      optimizeFloatMemory,\n      precision,\n      plugins,\n      source,\n      subKernels,\n      functions,\n      leadingReturnStatement,\n      followingReturnStatement,\n      dynamicArguments,\n      dynamicOutput,\n    } = kernel;\n\n    const argumentTypes = new Array(kernelArguments.length);\n    const constantTypes = {};\n\n    for (let i = 0; i < kernelArguments.length; i++) {\n      argumentTypes[i] = kernelArguments[i].type;\n    }\n\n    for (let i = 0; i < kernelConstants.length; i++) {\n      const kernelConstant = kernelConstants[i];\n      constantTypes[kernelConstant.name] = kernelConstant.type;\n    }\n\n    const needsArgumentType = (functionName, index) => {\n      return functionBuilder.needsArgumentType(functionName, index);\n    };\n\n    const assignArgumentType = (functionName, index, type) => {\n      functionBuilder.assignArgumentType(functionName, index, type);\n    };\n\n    const lookupReturnType = (functionName, ast, requestingNode) => {\n      return functionBuilder.lookupReturnType(functionName, ast, requestingNode);\n    };\n\n    const lookupFunctionArgumentTypes = (functionName) => {\n      return functionBuilder.lookupFunctionArgumentTypes(functionName);\n    };\n\n    const lookupFunctionArgumentName = (functionName, argumentIndex) => {\n      return functionBuilder.lookupFunctionArgumentName(functionName, argumentIndex);\n    };\n\n    const lookupFunctionArgumentBitRatio = (functionName, argumentName) => {\n      return functionBuilder.lookupFunctionArgumentBitRatio(functionName, argumentName);\n    };\n\n    const triggerImplyArgumentType = (functionName, i, argumentType, requestingNode) => {\n      functionBuilder.assignArgumentType(functionName, i, argumentType, requestingNode);\n    };\n\n    const triggerImplyArgumentBitRatio = (functionName, argumentName, calleeFunctionName, argumentIndex) => {\n      functionBuilder.assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex);\n    };\n\n    const onFunctionCall = (functionName, calleeFunctionName, args) => {\n      functionBuilder.trackFunctionCall(functionName, calleeFunctionName, args);\n    };\n\n    const onNestedFunction = (ast, source) => {\n      const argumentNames = [];\n      for (let i = 0; i < ast.params.length; i++) {\n        argumentNames.push(ast.params[i].name);\n      }\n      const nestedFunction = new FunctionNode(source, Object.assign({}, nodeOptions, {\n        returnType: null,\n        ast,\n        name: ast.id.name,\n        argumentNames,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n      }));\n      nestedFunction.traceFunctionAST(ast);\n      functionBuilder.addFunctionNode(nestedFunction);\n    };\n\n    const nodeOptions = Object.assign({\n      isRootKernel: false,\n      onNestedFunction,\n      lookupReturnType,\n      lookupFunctionArgumentTypes,\n      lookupFunctionArgumentName,\n      lookupFunctionArgumentBitRatio,\n      needsArgumentType,\n      assignArgumentType,\n      triggerImplyArgumentType,\n      triggerImplyArgumentBitRatio,\n      onFunctionCall,\n      optimizeFloatMemory,\n      precision,\n      constants,\n      constantTypes,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      output,\n      plugins,\n      dynamicArguments,\n      dynamicOutput,\n    }, extraNodeOptions || {});\n\n    const rootNodeOptions = Object.assign({}, nodeOptions, {\n      isRootKernel: true,\n      name: 'kernel',\n      argumentNames,\n      argumentTypes,\n      argumentSizes,\n      argumentBitRatios,\n      leadingReturnStatement,\n      followingReturnStatement,\n    });\n\n    if (typeof source === 'object' && source.functionNodes) {\n      return new FunctionBuilder().fromJSON(source.functionNodes, FunctionNode);\n    }\n\n    const rootNode = new FunctionNode(source, rootNodeOptions);\n\n    let functionNodes = null;\n    if (functions) {\n      functionNodes = functions.map((fn) => new FunctionNode(fn.source, {\n        returnType: fn.returnType,\n        argumentTypes: fn.argumentTypes,\n        output,\n        plugins,\n        constants,\n        constantTypes,\n        constantBitRatios,\n        optimizeFloatMemory,\n        precision,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n        onNestedFunction,\n      }));\n    }\n\n    let subKernelNodes = null;\n    if (subKernels) {\n      subKernelNodes = subKernels.map((subKernel) => {\n        const { name, source } = subKernel;\n        return new FunctionNode(source, Object.assign({}, nodeOptions, {\n          name,\n          isSubKernel: true,\n          isRootKernel: false,\n        }));\n      });\n    }\n\n    const functionBuilder = new FunctionBuilder({\n      kernel,\n      rootNode,\n      functionNodes,\n      nativeFunctions,\n      subKernelNodes\n    });\n\n    return functionBuilder;\n  }\n\n  constructor(settings) {\n    settings = settings || {};\n    this.kernel = settings.kernel;\n    this.rootNode = settings.rootNode;\n    this.functionNodes = settings.functionNodes || [];\n    this.subKernelNodes = settings.subKernelNodes || [];\n    this.nativeFunctions = settings.nativeFunctions || [];\n    this.functionMap = {};\n    this.nativeFunctionNames = [];\n    this.lookupChain = [];\n    this.functionNodeDependencies = {};\n    this.functionCalls = {};\n\n    if (this.rootNode) {\n      this.functionMap['kernel'] = this.rootNode;\n    }\n\n    if (this.functionNodes) {\n      for (let i = 0; i < this.functionNodes.length; i++) {\n        this.functionMap[this.functionNodes[i].name] = this.functionNodes[i];\n      }\n    }\n\n    if (this.subKernelNodes) {\n      for (let i = 0; i < this.subKernelNodes.length; i++) {\n        this.functionMap[this.subKernelNodes[i].name] = this.subKernelNodes[i];\n      }\n    }\n\n    if (this.nativeFunctions) {\n      for (let i = 0; i < this.nativeFunctions.length; i++) {\n        const nativeFunction = this.nativeFunctions[i];\n        this.nativeFunctionNames.push(nativeFunction.name);\n      }\n    }\n  }\n\n  addFunctionNode(functionNode) {\n    if (!functionNode.name) throw new Error('functionNode.name needs set');\n    this.functionMap[functionNode.name] = functionNode;\n    if (functionNode.isRootKernel) {\n      this.rootNode = functionNode;\n    }\n  }\n\n  traceFunctionCalls(functionName, retList) {\n    functionName = functionName || 'kernel';\n    retList = retList || [];\n\n    if (this.nativeFunctionNames.indexOf(functionName) > -1) {\n      const nativeFunctionIndex = retList.indexOf(functionName);\n      if (nativeFunctionIndex === -1) {\n        retList.push(functionName);\n      } else {\n        const dependantNativeFunctionName = retList.splice(nativeFunctionIndex, 1)[0];\n        retList.push(dependantNativeFunctionName);\n      }\n      return retList;\n    }\n\n    const functionNode = this.functionMap[functionName];\n    if (functionNode) {\n      const functionIndex = retList.indexOf(functionName);\n      if (functionIndex === -1) {\n        retList.push(functionName);\n        functionNode.toString(); \n        for (let i = 0; i < functionNode.calledFunctions.length; ++i) {\n          this.traceFunctionCalls(functionNode.calledFunctions[i], retList);\n        }\n      } else {\n        const dependantFunctionName = retList.splice(functionIndex, 1)[0];\n        retList.push(dependantFunctionName);\n      }\n    }\n\n    return retList;\n  }\n\n  getPrototypeString(functionName) {\n    return this.getPrototypes(functionName).join('\\n');\n  }\n\n  getPrototypes(functionName) {\n    if (this.rootNode) {\n      this.rootNode.toString();\n    }\n    if (functionName) {\n      return this.getPrototypesFromFunctionNames(this.traceFunctionCalls(functionName, []).reverse());\n    }\n    return this.getPrototypesFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  getStringFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const node = this.functionMap[functionList[i]];\n      if (node) {\n        ret.push(this.functionMap[functionList[i]].toString());\n      }\n    }\n    return ret.join('\\n');\n  }\n\n  getPrototypesFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const functionName = functionList[i];\n      const functionIndex = this.nativeFunctionNames.indexOf(functionName);\n      if (functionIndex > -1) {\n        ret.push(this.nativeFunctions[functionIndex].source);\n        continue;\n      }\n      const node = this.functionMap[functionName];\n      if (node) {\n        ret.push(node.toString());\n      }\n    }\n    return ret;\n  }\n\n  toJSON() {\n    return this.traceFunctionCalls(this.rootNode.name).reverse().map(name => {\n      const nativeIndex = this.nativeFunctions.indexOf(name);\n      if (nativeIndex > -1) {\n        return {\n          name,\n          source: this.nativeFunctions[nativeIndex].source\n        };\n      } else if (this.functionMap[name]) {\n        return this.functionMap[name].toJSON();\n      } else {\n        throw new Error(`function ${ name } not found`);\n      }\n    });\n  }\n\n  fromJSON(jsonFunctionNodes, FunctionNode) {\n    this.functionMap = {};\n    for (let i = 0; i < jsonFunctionNodes.length; i++) {\n      const jsonFunctionNode = jsonFunctionNodes[i];\n      this.functionMap[jsonFunctionNode.settings.name] = new FunctionNode(jsonFunctionNode.ast, jsonFunctionNode.settings);\n    }\n    return this;\n  }\n\n  getString(functionName) {\n    if (functionName) {\n      return this.getStringFromFunctionNames(this.traceFunctionCalls(functionName).reverse());\n    }\n    return this.getStringFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  lookupReturnType(functionName, ast, requestingNode) {\n    if (ast.type !== 'CallExpression') {\n      throw new Error(`expected ast type of \"CallExpression\", but is ${ ast.type }`);\n    }\n    if (this._isNativeFunction(functionName)) {\n      return this._lookupNativeFunctionReturnType(functionName);\n    } else if (this._isFunction(functionName)) {\n      const node = this._getFunction(functionName);\n      if (node.returnType) {\n        return node.returnType;\n      } else {\n        for (let i = 0; i < this.lookupChain.length; i++) {\n          if (this.lookupChain[i].ast === ast) {\n            if (node.argumentTypes.length === 0 && ast.arguments.length > 0) {\n              const args = ast.arguments;\n              for (let j = 0; j < args.length; j++) {\n                this.lookupChain.push({\n                  name: requestingNode.name,\n                  ast: args[i],\n                  requestingNode\n                });\n                node.argumentTypes[j] = requestingNode.getType(args[j]);\n                this.lookupChain.pop();\n              }\n              return node.returnType = node.getType(node.getJsAST());\n            }\n\n            throw new Error('circlical logic detected!');\n          }\n        }\n        this.lookupChain.push({\n          name: requestingNode.name,\n          ast,\n          requestingNode\n        });\n        const type = node.getType(node.getJsAST());\n        this.lookupChain.pop();\n        return node.returnType = type;\n      }\n    }\n\n    return null;\n  }\n\n  _getFunction(functionName) {\n    if (!this._isFunction(functionName)) {\n      new Error(`Function ${functionName} not found`);\n    }\n    return this.functionMap[functionName];\n  }\n\n  _isFunction(functionName) {\n    return Boolean(this.functionMap[functionName]);\n  }\n\n  _getNativeFunction(functionName) {\n    for (let i = 0; i < this.nativeFunctions.length; i++) {\n      if (this.nativeFunctions[i].name === functionName) return this.nativeFunctions[i];\n    }\n    return null;\n  }\n\n  _isNativeFunction(functionName) {\n    return Boolean(this._getNativeFunction(functionName));\n  }\n\n  _lookupNativeFunctionReturnType(functionName) {\n    let nativeFunction = this._getNativeFunction(functionName);\n    if (nativeFunction) {\n      return nativeFunction.returnType;\n    }\n    throw new Error(`Native function ${ functionName } not found`);\n  }\n\n  lookupFunctionArgumentTypes(functionName) {\n    if (this._isNativeFunction(functionName)) {\n      return this._getNativeFunction(functionName).argumentTypes;\n    } else if (this._isFunction(functionName)) {\n      return this._getFunction(functionName).argumentTypes;\n    }\n    return null;\n  }\n\n  lookupFunctionArgumentName(functionName, argumentIndex) {\n    return this._getFunction(functionName).argumentNames[argumentIndex];\n  }\n\n  lookupFunctionArgumentBitRatio(functionName, argumentName) {\n    if (!this._isFunction(functionName)) {\n      throw new Error('function not found');\n    }\n    if (this.rootNode.name === functionName) {\n      const i = this.rootNode.argumentNames.indexOf(argumentName);\n      if (i !== -1) {\n        return this.rootNode.argumentBitRatios[i];\n      }\n    }\n    const node = this._getFunction(functionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error('argument not found');\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error('argument bit ratio not found');\n    }\n    return bitRatio;\n  }\n\n  needsArgumentType(functionName, i) {\n    if (!this._isFunction(functionName)) return false;\n    const fnNode = this._getFunction(functionName);\n    return !fnNode.argumentTypes[i];\n  }\n\n  assignArgumentType(functionName, i, argumentType, requestingNode) {\n    if (!this._isFunction(functionName)) return;\n    const fnNode = this._getFunction(functionName);\n    if (!fnNode.argumentTypes[i]) {\n      fnNode.argumentTypes[i] = argumentType;\n    }\n  }\n\n  assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex) {\n    const node = this._getFunction(functionName);\n    if (this._isNativeFunction(calleeFunctionName)) return null;\n    const calleeNode = this._getFunction(calleeFunctionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error(`Argument ${argumentName} not found in arguments from function ${functionName}`);\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error(`Bit ratio for argument ${argumentName} not found in function ${functionName}`);\n    }\n    if (!calleeNode.argumentBitRatios) {\n      calleeNode.argumentBitRatios = new Array(calleeNode.argumentNames.length);\n    }\n    const calleeBitRatio = calleeNode.argumentBitRatios[i];\n    if (typeof calleeBitRatio === 'number') {\n      if (calleeBitRatio !== bitRatio) {\n        throw new Error(`Incompatible bit ratio found at function ${functionName} at argument ${argumentName}`);\n      }\n      return calleeBitRatio;\n    }\n    calleeNode.argumentBitRatios[i] = bitRatio;\n    return bitRatio;\n  }\n\n  trackFunctionCall(functionName, calleeFunctionName, args) {\n    if (!this.functionNodeDependencies[functionName]) {\n      this.functionNodeDependencies[functionName] = new Set();\n      this.functionCalls[functionName] = [];\n    }\n    this.functionNodeDependencies[functionName].add(calleeFunctionName);\n    this.functionCalls[functionName].push(args);\n  }\n\n  getKernelResultType() {\n    return this.rootNode.returnType || this.rootNode.getType(this.rootNode.ast);\n  }\n\n  getSubKernelResultType(index) {\n    const subKernelNode = this.subKernelNodes[index];\n    let called = false;\n    for (let functionCallIndex = 0; functionCallIndex < this.rootNode.functionCalls.length; functionCallIndex++) {\n      const functionCall = this.rootNode.functionCalls[functionCallIndex];\n      if (functionCall.ast.callee.name === subKernelNode.name) {\n        called = true;\n      }\n    }\n    if (!called) {\n      throw new Error(`SubKernel ${ subKernelNode.name } never called by kernel`);\n    }\n    return subKernelNode.returnType || subKernelNode.getType(subKernelNode.getJsAST());\n  }\n\n  getReturnTypes() {\n    const result = {\n      [this.rootNode.name]: this.rootNode.getType(this.rootNode.ast),\n    };\n    const list = this.traceFunctionCalls(this.rootNode.name);\n    for (let i = 0; i < list.length; i++) {\n      const functionName = list[i];\n      const functionNode = this.functionMap[functionName];\n      result[functionName] = functionNode.getType(functionNode.ast);\n    }\n    return result;\n  }\n}\n\nmodule.exports = {\n  FunctionBuilder\n};\n},{}],10:[function(require,module,exports){\nconst acorn = require('acorn');\nconst { utils } = require('../utils');\nconst { FunctionTracer } = require('./function-tracer');\n\nclass FunctionNode {\n  constructor(source, settings) {\n    if (!source && !settings.ast) {\n      throw new Error('source parameter is missing');\n    }\n    settings = settings || {};\n    this.source = source;\n    this.ast = null;\n    this.name = typeof source === 'string' ? settings.isRootKernel ?\n      'kernel' :\n      (settings.name || utils.getFunctionNameFromString(source)) : null;\n    this.calledFunctions = [];\n    this.constants = {};\n    this.constantTypes = {};\n    this.constantBitRatios = {};\n    this.isRootKernel = false;\n    this.isSubKernel = false;\n    this.debug = null;\n    this.functions = null;\n    this.identifiers = null;\n    this.contexts = null;\n    this.functionCalls = null;\n    this.states = [];\n    this.needsArgumentType = null;\n    this.assignArgumentType = null;\n    this.lookupReturnType = null;\n    this.lookupFunctionArgumentTypes = null;\n    this.lookupFunctionArgumentBitRatio = null;\n    this.triggerImplyArgumentType = null;\n    this.triggerImplyArgumentBitRatio = null;\n    this.onNestedFunction = null;\n    this.onFunctionCall = null;\n    this.optimizeFloatMemory = null;\n    this.precision = null;\n    this.loopMaxIterations = null;\n    this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null);\n    this.argumentTypes = [];\n    this.argumentSizes = [];\n    this.argumentBitRatios = null;\n    this.returnType = null;\n    this.output = [];\n    this.plugins = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.dynamicOutput = null;\n    this.dynamicArguments = null;\n    this.strictTypingChecking = false;\n    this.fixIntegerDivisionAccuracy = null;\n\n    if (settings) {\n      for (const p in settings) {\n        if (!settings.hasOwnProperty(p)) continue;\n        if (!this.hasOwnProperty(p)) continue;\n        this[p] = settings[p];\n      }\n    }\n\n    this.literalTypes = {};\n\n    this.validate();\n    this._string = null;\n    this._internalVariableNames = {};\n  }\n\n  validate() {\n    if (typeof this.source !== 'string' && !this.ast) {\n      throw new Error('this.source not a string');\n    }\n\n    if (!this.ast && !utils.isFunctionString(this.source)) {\n      throw new Error('this.source not a function string');\n    }\n\n    if (!this.name) {\n      throw new Error('this.name could not be set');\n    }\n\n    if (this.argumentTypes.length > 0 && this.argumentTypes.length !== this.argumentNames.length) {\n      throw new Error(`argumentTypes count of ${ this.argumentTypes.length } exceeds ${ this.argumentNames.length }`);\n    }\n\n    if (this.output.length < 1) {\n      throw new Error('this.output is not big enough');\n    }\n  }\n\n  isIdentifierConstant(name) {\n    if (!this.constants) return false;\n    return this.constants.hasOwnProperty(name);\n  }\n\n  isInput(argumentName) {\n    return this.argumentTypes[this.argumentNames.indexOf(argumentName)] === 'Input';\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.state !== state) {\n      throw new Error(`Cannot popState ${ state } when in ${ this.state }`);\n    }\n    this.states.pop();\n  }\n\n  isState(state) {\n    return this.state === state;\n  }\n\n  get state() {\n    return this.states[this.states.length - 1];\n  }\n\n  astMemberExpressionUnroll(ast) {\n    if (ast.type === 'Identifier') {\n      return ast.name;\n    } else if (ast.type === 'ThisExpression') {\n      return 'this';\n    }\n\n    if (ast.type === 'MemberExpression') {\n      if (ast.object && ast.property) {\n        if (ast.object.hasOwnProperty('name') && ast.object.name !== 'Math') {\n          return this.astMemberExpressionUnroll(ast.property);\n        }\n\n        return (\n          this.astMemberExpressionUnroll(ast.object) +\n          '.' +\n          this.astMemberExpressionUnroll(ast.property)\n        );\n      }\n    }\n\n    if (ast.hasOwnProperty('expressions')) {\n      const firstExpression = ast.expressions[0];\n      if (firstExpression.type === 'Literal' && firstExpression.value === 0 && ast.expressions.length === 2) {\n        return this.astMemberExpressionUnroll(ast.expressions[1]);\n      }\n    }\n\n    throw this.astErrorOutput('Unknown astMemberExpressionUnroll', ast);\n  }\n\n  getJsAST(inParser) {\n    if (this.ast) {\n      return this.ast;\n    }\n    if (typeof this.source === 'object') {\n      this.traceFunctionAST(this.source);\n      return this.ast = this.source;\n    }\n\n    inParser = inParser || acorn;\n    if (inParser === null) {\n      throw new Error('Missing JS to AST parser');\n    }\n\n    const ast = Object.freeze(inParser.parse(`const parser_${ this.name } = ${ this.source };`, {\n      locations: true\n    }));\n    const functionAST = ast.body[0].declarations[0].init;\n    this.traceFunctionAST(functionAST);\n\n    if (!ast) {\n      throw new Error('Failed to parse JS code');\n    }\n\n    return this.ast = functionAST;\n  }\n\n  traceFunctionAST(ast) {\n    const { contexts, declarations, functions, identifiers, functionCalls } = new FunctionTracer(ast);\n    this.contexts = contexts;\n    this.identifiers = identifiers;\n    this.functionCalls = functionCalls;\n    this.functions = functions;\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const { ast, inForLoopInit, inForLoopTest } = declaration;\n      const { init } = ast;\n      const dependencies = this.getDependencies(init);\n      let valueType = null;\n\n      if (inForLoopInit && inForLoopTest) {\n        valueType = 'Integer';\n      } else {\n        if (init) {\n          const realType = this.getType(init);\n          switch (realType) {\n            case 'Integer':\n            case 'Float':\n            case 'Number':\n              if (init.type === 'MemberExpression') {\n                valueType = realType;\n              } else {\n                valueType = 'Number';\n              }\n              break;\n            case 'LiteralInteger':\n              valueType = 'Number';\n              break;\n            default:\n              valueType = realType;\n          }\n        }\n      }\n      declaration.valueType = valueType;\n      declaration.dependencies = dependencies;\n      declaration.isSafe = this.isSafeDependencies(dependencies);\n    }\n\n    for (let i = 0; i < functions.length; i++) {\n      this.onNestedFunction(functions[i], this.source);\n    }\n  }\n\n  getDeclaration(ast) {\n    for (let i = 0; i < this.identifiers.length; i++) {\n      const identifier = this.identifiers[i];\n      if (ast === identifier.ast) {\n        return identifier.declaration;\n      }\n    }\n    return null;\n  }\n\n  getVariableType(ast) {\n    if (ast.type !== 'Identifier') {\n      throw new Error(`ast of ${ast.type} not \"Identifier\"`);\n    }\n    let type = null;\n    const argumentIndex = this.argumentNames.indexOf(ast.name);\n    if (argumentIndex === -1) {\n      const declaration = this.getDeclaration(ast);\n      if (declaration) {\n        return declaration.valueType;\n      }\n    } else {\n      const argumentType = this.argumentTypes[argumentIndex];\n      if (argumentType) {\n        type = argumentType;\n      }\n    }\n    if (!type && this.strictTypingChecking) {\n      throw new Error(`Declaration of ${name} not found`);\n    }\n    return type;\n  }\n\n  getLookupType(type) {\n    if (!typeLookupMap.hasOwnProperty(type)) {\n      throw new Error(`unknown typeLookupMap ${ type }`);\n    }\n    return typeLookupMap[type];\n  }\n\n  getConstantType(constantName) {\n    if (this.constantTypes[constantName]) {\n      const type = this.constantTypes[constantName];\n      if (type === 'Float') {\n        return 'Number';\n      } else {\n        return type;\n      }\n    }\n    throw new Error(`Type for constant \"${ constantName }\" not declared`);\n  }\n\n  toString() {\n    if (this._string) return this._string;\n    return this._string = this.astGeneric(this.getJsAST(), []).join('').trim();\n  }\n\n  toJSON() {\n    const settings = {\n      source: this.source,\n      name: this.name,\n      constants: this.constants,\n      constantTypes: this.constantTypes,\n      isRootKernel: this.isRootKernel,\n      isSubKernel: this.isSubKernel,\n      debug: this.debug,\n      output: this.output,\n      loopMaxIterations: this.loopMaxIterations,\n      argumentNames: this.argumentNames,\n      argumentTypes: this.argumentTypes,\n      argumentSizes: this.argumentSizes,\n      returnType: this.returnType,\n      leadingReturnStatement: this.leadingReturnStatement,\n      followingReturnStatement: this.followingReturnStatement,\n    };\n\n    return {\n      ast: this.ast,\n      settings\n    };\n  }\n\n  getType(ast) {\n    if (Array.isArray(ast)) {\n      return this.getType(ast[ast.length - 1]);\n    }\n    switch (ast.type) {\n      case 'BlockStatement':\n        return this.getType(ast.body);\n      case 'ArrayExpression':\n        const childType = this.getType(ast.elements[0]);\n        switch (childType) {\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            return `Matrix(${ast.elements.length})`;\n        }\n        return `Array(${ ast.elements.length })`;\n      case 'Literal':\n        const literalKey = this.astKey(ast);\n        if (this.literalTypes[literalKey]) {\n          return this.literalTypes[literalKey];\n        }\n        if (Number.isInteger(ast.value)) {\n          return 'LiteralInteger';\n        } else if (ast.value === true || ast.value === false) {\n          return 'Boolean';\n        } else {\n          return 'Number';\n        }\n        case 'AssignmentExpression':\n          return this.getType(ast.left);\n        case 'CallExpression':\n          if (this.isAstMathFunction(ast)) {\n            return 'Number';\n          }\n          if (!ast.callee || !ast.callee.name) {\n            if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) {\n              const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name;\n              this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n              return this.lookupReturnType(functionName, ast, this);\n            }\n            if (this.getVariableSignature(ast.callee, true) === 'this.color') {\n              return null;\n            }\n            if (ast.callee.type === 'MemberExpression' && ast.callee.object && ast.callee.property && ast.callee.property.name && ast.arguments) {\n              const functionName = ast.callee.property.name;\n              this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n              return this.lookupReturnType(functionName, ast, this);\n            }\n            throw this.astErrorOutput('Unknown call expression', ast);\n          }\n          if (ast.callee && ast.callee.name) {\n            const functionName = ast.callee.name;\n            this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n            return this.lookupReturnType(functionName, ast, this);\n          }\n          throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n        case 'LogicalExpression':\n          return 'Boolean';\n        case 'BinaryExpression':\n          switch (ast.operator) {\n            case '%':\n            case '/':\n              if (this.fixIntegerDivisionAccuracy) {\n                return 'Number';\n              } else {\n                break;\n              }\n              case '>':\n              case '<':\n                return 'Boolean';\n              case '&':\n              case '|':\n              case '^':\n              case '<<':\n              case '>>':\n              case '>>>':\n                return 'Integer';\n          }\n          const type = this.getType(ast.left);\n          if (this.isState('skip-literal-correction')) return type;\n          if (type === 'LiteralInteger') {\n            const rightType = this.getType(ast.right);\n            if (rightType === 'LiteralInteger') {\n              if (ast.left.value % 1 === 0) {\n                return 'Integer';\n              } else {\n                return 'Float';\n              }\n            }\n            return rightType;\n          }\n          return typeLookupMap[type] || type;\n        case 'UpdateExpression':\n          return this.getType(ast.argument);\n        case 'UnaryExpression':\n          if (ast.operator === '~') {\n            return 'Integer';\n          }\n          return this.getType(ast.argument);\n        case 'VariableDeclaration': {\n          const declarations = ast.declarations;\n          let lastType;\n          for (let i = 0; i < declarations.length; i++) {\n            const declaration = declarations[i];\n            lastType = this.getType(declaration);\n          }\n          if (!lastType) {\n            throw this.astErrorOutput(`Unable to find type for declaration`, ast);\n          }\n          return lastType;\n        }\n        case 'VariableDeclarator':\n          const declaration = this.getDeclaration(ast.id);\n          if (!declaration) {\n            throw this.astErrorOutput(`Unable to find declarator`, ast);\n          }\n\n          if (!declaration.valueType) {\n            throw this.astErrorOutput(`Unable to find declarator valueType`, ast);\n          }\n\n          return declaration.valueType;\n        case 'Identifier':\n          if (ast.name === 'Infinity') {\n            return 'Number';\n          }\n          if (this.isAstVariable(ast)) {\n            const signature = this.getVariableSignature(ast);\n            if (signature === 'value') {\n              return this.getCheckVariableType(ast);\n            }\n          }\n          const origin = this.findIdentifierOrigin(ast);\n          if (origin && origin.init) {\n            return this.getType(origin.init);\n          }\n          return null;\n        case 'ReturnStatement':\n          return this.getType(ast.argument);\n        case 'MemberExpression':\n          if (this.isAstMathFunction(ast)) {\n            switch (ast.property.name) {\n              case 'ceil':\n                return 'Integer';\n              case 'floor':\n                return 'Integer';\n              case 'round':\n                return 'Integer';\n            }\n            return 'Number';\n          }\n          if (this.isAstVariable(ast)) {\n            const variableSignature = this.getVariableSignature(ast);\n            switch (variableSignature) {\n              case 'value[]':\n                return this.getLookupType(this.getCheckVariableType(ast.object));\n              case 'value[][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object));\n              case 'value[][][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object.object));\n              case 'value[][][][]':\n                return this.getLookupType(this.getCheckVariableType(ast.object.object.object.object));\n              case 'value.thread.value':\n              case 'this.thread.value':\n                return 'Integer';\n              case 'this.output.value':\n                return this.dynamicOutput ? 'Integer' : 'LiteralInteger';\n              case 'this.constants.value':\n                return this.getConstantType(ast.property.name);\n              case 'this.constants.value[]':\n                return this.getLookupType(this.getConstantType(ast.object.property.name));\n              case 'this.constants.value[][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.property.name));\n              case 'this.constants.value[][][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.object.property.name));\n              case 'this.constants.value[][][][]':\n                return this.getLookupType(this.getConstantType(ast.object.object.object.object.property.name));\n              case 'fn()[]':\n              case 'fn()[][]':\n              case 'fn()[][][]':\n                return this.getLookupType(this.getType(ast.object));\n              case 'value.value':\n                if (this.isAstMathVariable(ast)) {\n                  return 'Number';\n                }\n                switch (ast.property.name) {\n                  case 'r':\n                  case 'g':\n                  case 'b':\n                  case 'a':\n                    return this.getLookupType(this.getCheckVariableType(ast.object));\n                }\n                case '[][]':\n                  return 'Number';\n            }\n            throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n          }\n          throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n        case 'ConditionalExpression':\n          return this.getType(ast.consequent);\n        case 'FunctionDeclaration':\n        case 'FunctionExpression':\n          const lastReturn = this.findLastReturn(ast.body);\n          if (lastReturn) {\n            return this.getType(lastReturn);\n          }\n          return null;\n        case 'IfStatement':\n          return this.getType(ast.consequent);\n        case 'SequenceExpression':\n          return this.getType(ast.expressions[ast.expressions.length - 1]);\n        default:\n          throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n    }\n  }\n\n  getCheckVariableType(ast) {\n    const type = this.getVariableType(ast);\n    if (!type) {\n      throw this.astErrorOutput(`${ast.type} is not defined`, ast);\n    }\n    return type;\n  }\n\n  inferArgumentTypesIfNeeded(functionName, args) {\n    for (let i = 0; i < args.length; i++) {\n      if (!this.needsArgumentType(functionName, i)) continue;\n      const type = this.getType(args[i]);\n      if (!type) {\n        throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]);\n      }\n      this.assignArgumentType(functionName, i, type);\n    }\n  }\n\n  isAstMathVariable(ast) {\n    const mathProperties = [\n      'E',\n      'PI',\n      'SQRT2',\n      'SQRT1_2',\n      'LN2',\n      'LN10',\n      'LOG2E',\n      'LOG10E',\n    ];\n    return ast.type === 'MemberExpression' &&\n      ast.object && ast.object.type === 'Identifier' &&\n      ast.object.name === 'Math' &&\n      ast.property &&\n      ast.property.type === 'Identifier' &&\n      mathProperties.indexOf(ast.property.name) > -1;\n  }\n\n  isAstMathFunction(ast) {\n    const mathFunctions = [\n      'abs',\n      'acos',\n      'acosh',\n      'asin',\n      'asinh',\n      'atan',\n      'atan2',\n      'atanh',\n      'cbrt',\n      'ceil',\n      'clz32',\n      'cos',\n      'cosh',\n      'expm1',\n      'exp',\n      'floor',\n      'fround',\n      'imul',\n      'log',\n      'log2',\n      'log10',\n      'log1p',\n      'max',\n      'min',\n      'pow',\n      'random',\n      'round',\n      'sign',\n      'sin',\n      'sinh',\n      'sqrt',\n      'tan',\n      'tanh',\n      'trunc',\n    ];\n    return ast.type === 'CallExpression' &&\n      ast.callee &&\n      ast.callee.type === 'MemberExpression' &&\n      ast.callee.object &&\n      ast.callee.object.type === 'Identifier' &&\n      ast.callee.object.name === 'Math' &&\n      ast.callee.property &&\n      ast.callee.property.type === 'Identifier' &&\n      mathFunctions.indexOf(ast.callee.property.name) > -1;\n  }\n\n  isAstVariable(ast) {\n    return ast.type === 'Identifier' || ast.type === 'MemberExpression';\n  }\n\n  isSafe(ast) {\n    return this.isSafeDependencies(this.getDependencies(ast));\n  }\n\n  isSafeDependencies(dependencies) {\n    return dependencies && dependencies.every ? dependencies.every(dependency => dependency.isSafe) : true;\n  }\n\n  getDependencies(ast, dependencies, isNotSafe) {\n    if (!dependencies) {\n      dependencies = [];\n    }\n    if (!ast) return null;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.getDependencies(ast[i], dependencies, isNotSafe);\n      }\n      return dependencies;\n    }\n    switch (ast.type) {\n      case 'AssignmentExpression':\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'ConditionalExpression':\n        this.getDependencies(ast.test, dependencies, isNotSafe);\n        this.getDependencies(ast.alternate, dependencies, isNotSafe);\n        this.getDependencies(ast.consequent, dependencies, isNotSafe);\n        return dependencies;\n      case 'Literal':\n        dependencies.push({\n          origin: 'literal',\n          value: ast.value,\n          isSafe: isNotSafe === true ? false : ast.value > -Infinity && ast.value < Infinity && !isNaN(ast.value)\n        });\n        break;\n      case 'VariableDeclarator':\n        return this.getDependencies(ast.init, dependencies, isNotSafe);\n      case 'Identifier':\n        const declaration = this.getDeclaration(ast);\n        if (declaration) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'declaration',\n            isSafe: isNotSafe ? false : this.isSafeDependencies(declaration.dependencies),\n          });\n        } else if (this.argumentNames.indexOf(ast.name) > -1) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'argument',\n            isSafe: false,\n          });\n        } else if (this.strictTypingChecking) {\n          throw new Error(`Cannot find identifier origin \"${ast.name}\"`);\n        }\n        break;\n      case 'FunctionDeclaration':\n        return this.getDependencies(ast.body.body[ast.body.body.length - 1], dependencies, isNotSafe);\n      case 'ReturnStatement':\n        return this.getDependencies(ast.argument, dependencies);\n      case 'BinaryExpression':\n      case 'LogicalExpression':\n        isNotSafe = (ast.operator === '/' || ast.operator === '*');\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'UnaryExpression':\n      case 'UpdateExpression':\n        return this.getDependencies(ast.argument, dependencies, isNotSafe);\n      case 'VariableDeclaration':\n        return this.getDependencies(ast.declarations, dependencies, isNotSafe);\n      case 'ArrayExpression':\n        dependencies.push({\n          origin: 'declaration',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'CallExpression':\n        dependencies.push({\n          origin: 'function',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'MemberExpression':\n        const details = this.getMemberExpressionDetails(ast);\n        switch (details.signature) {\n          case 'value[]':\n            this.getDependencies(ast.object, dependencies, isNotSafe);\n            break;\n          case 'value[][]':\n            this.getDependencies(ast.object.object, dependencies, isNotSafe);\n            break;\n          case 'value[][][]':\n            this.getDependencies(ast.object.object.object, dependencies, isNotSafe);\n            break;\n          case 'this.output.value':\n            if (this.dynamicOutput) {\n              dependencies.push({\n                name: details.name,\n                origin: 'output',\n                isSafe: false,\n              });\n            }\n            break;\n        }\n        if (details) {\n          if (details.property) {\n            this.getDependencies(details.property, dependencies, isNotSafe);\n          }\n          if (details.xProperty) {\n            this.getDependencies(details.xProperty, dependencies, isNotSafe);\n          }\n          if (details.yProperty) {\n            this.getDependencies(details.yProperty, dependencies, isNotSafe);\n          }\n          if (details.zProperty) {\n            this.getDependencies(details.zProperty, dependencies, isNotSafe);\n          }\n          return dependencies;\n        }\n        case 'SequenceExpression':\n          return this.getDependencies(ast.expressions, dependencies, isNotSafe);\n        default:\n          throw this.astErrorOutput(`Unhandled type ${ ast.type } in getDependencies`, ast);\n    }\n    return dependencies;\n  }\n\n  getVariableSignature(ast, returnRawValue) {\n    if (!this.isAstVariable(ast)) {\n      throw new Error(`ast of type \"${ ast.type }\" is not a variable signature`);\n    }\n    if (ast.type === 'Identifier') {\n      return 'value';\n    }\n    const signature = [];\n    while (true) {\n      if (!ast) break;\n      if (ast.computed) {\n        signature.push('[]');\n      } else if (ast.type === 'ThisExpression') {\n        signature.unshift('this');\n      } else if (ast.property && ast.property.name) {\n        if (\n          ast.property.name === 'x' ||\n          ast.property.name === 'y' ||\n          ast.property.name === 'z'\n        ) {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        } else if (\n          ast.property.name === 'constants' ||\n          ast.property.name === 'thread' ||\n          ast.property.name === 'output'\n        ) {\n          signature.unshift('.' + ast.property.name);\n        } else {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        }\n      } else if (ast.name) {\n        signature.unshift(returnRawValue ? ast.name : 'value');\n      } else if (ast.callee && ast.callee.name) {\n        signature.unshift(returnRawValue ? ast.callee.name + '()' : 'fn()');\n      } else if (ast.elements) {\n        signature.unshift('[]');\n      } else {\n        signature.unshift('unknown');\n      }\n      ast = ast.object;\n    }\n\n    const signatureString = signature.join('');\n    if (returnRawValue) {\n      return signatureString;\n    }\n\n    const allowedExpressions = [\n      'value',\n      'value[]',\n      'value[][]',\n      'value[][][]',\n      'value[][][][]',\n      'value.value',\n      'value.thread.value',\n      'this.thread.value',\n      'this.output.value',\n      'this.constants.value',\n      'this.constants.value[]',\n      'this.constants.value[][]',\n      'this.constants.value[][][]',\n      'this.constants.value[][][][]',\n      'fn()[]',\n      'fn()[][]',\n      'fn()[][][]',\n      '[][]',\n    ];\n    if (allowedExpressions.indexOf(signatureString) > -1) {\n      return signatureString;\n    }\n    return null;\n  }\n\n  build() {\n    return this.toString().length > 0;\n  }\n\n  astGeneric(ast, retArr) {\n    if (ast === null) {\n      throw this.astErrorOutput('NULL ast', ast);\n    } else {\n      if (Array.isArray(ast)) {\n        for (let i = 0; i < ast.length; i++) {\n          this.astGeneric(ast[i], retArr);\n        }\n        return retArr;\n      }\n\n      switch (ast.type) {\n        case 'FunctionDeclaration':\n          return this.astFunctionDeclaration(ast, retArr);\n        case 'FunctionExpression':\n          return this.astFunctionExpression(ast, retArr);\n        case 'ReturnStatement':\n          return this.astReturnStatement(ast, retArr);\n        case 'Literal':\n          return this.astLiteral(ast, retArr);\n        case 'BinaryExpression':\n          return this.astBinaryExpression(ast, retArr);\n        case 'Identifier':\n          return this.astIdentifierExpression(ast, retArr);\n        case 'AssignmentExpression':\n          return this.astAssignmentExpression(ast, retArr);\n        case 'ExpressionStatement':\n          return this.astExpressionStatement(ast, retArr);\n        case 'EmptyStatement':\n          return this.astEmptyStatement(ast, retArr);\n        case 'BlockStatement':\n          return this.astBlockStatement(ast, retArr);\n        case 'IfStatement':\n          return this.astIfStatement(ast, retArr);\n        case 'SwitchStatement':\n          return this.astSwitchStatement(ast, retArr);\n        case 'BreakStatement':\n          return this.astBreakStatement(ast, retArr);\n        case 'ContinueStatement':\n          return this.astContinueStatement(ast, retArr);\n        case 'ForStatement':\n          return this.astForStatement(ast, retArr);\n        case 'WhileStatement':\n          return this.astWhileStatement(ast, retArr);\n        case 'DoWhileStatement':\n          return this.astDoWhileStatement(ast, retArr);\n        case 'VariableDeclaration':\n          return this.astVariableDeclaration(ast, retArr);\n        case 'VariableDeclarator':\n          return this.astVariableDeclarator(ast, retArr);\n        case 'ThisExpression':\n          return this.astThisExpression(ast, retArr);\n        case 'SequenceExpression':\n          return this.astSequenceExpression(ast, retArr);\n        case 'UnaryExpression':\n          return this.astUnaryExpression(ast, retArr);\n        case 'UpdateExpression':\n          return this.astUpdateExpression(ast, retArr);\n        case 'LogicalExpression':\n          return this.astLogicalExpression(ast, retArr);\n        case 'MemberExpression':\n          return this.astMemberExpression(ast, retArr);\n        case 'CallExpression':\n          return this.astCallExpression(ast, retArr);\n        case 'ArrayExpression':\n          return this.astArrayExpression(ast, retArr);\n        case 'DebuggerStatement':\n          return this.astDebuggerStatement(ast, retArr);\n        case 'ConditionalExpression':\n          return this.astConditionalExpression(ast, retArr);\n      }\n\n      throw this.astErrorOutput('Unknown ast type : ' + ast.type, ast);\n    }\n  }\n  astErrorOutput(error, ast) {\n    if (typeof this.source !== 'string') {\n      return new Error(error);\n    }\n\n    const debugString = utils.getAstString(this.source, ast);\n    const leadingSource = this.source.substr(ast.start);\n    const splitLines = leadingSource.split(/\\n/);\n    const lineBefore = splitLines.length > 0 ? splitLines[splitLines.length - 1] : 0;\n    return new Error(`${error} on line ${ splitLines.length }, position ${ lineBefore.length }:\\n ${ debugString }`);\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    return retArr;\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astFunction(ast, retArr) {\n    throw new Error(`\"astFunction\" not defined on ${ this.constructor.name }`);\n  }\n\n  astFunctionDeclaration(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  astFunctionExpression(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  isChildFunction(ast) {\n    for (let i = 0; i < this.functions.length; i++) {\n      if (this.functions[i] === ast) {\n        return true;\n      }\n    }\n    return false;\n  }\n  astReturnStatement(ast, retArr) {\n    return retArr;\n  }\n  astLiteral(ast, retArr) {\n    this.literalTypes[this.astKey(ast)] = 'Number';\n    return retArr;\n  }\n  astBinaryExpression(ast, retArr) {\n    return retArr;\n  }\n  astIdentifierExpression(ast, retArr) {\n    return retArr;\n  }\n  astAssignmentExpression(ast, retArr) {\n    return retArr;\n  }\n  astExpressionStatement(esNode, retArr) {\n    this.astGeneric(esNode.expression, retArr);\n    retArr.push(';');\n    return retArr;\n  }\n  astEmptyStatement(eNode, retArr) {\n    return retArr;\n  }\n  astBlockStatement(ast, retArr) {\n    return retArr;\n  }\n  astIfStatement(ast, retArr) {\n    return retArr;\n  }\n  astSwitchStatement(ast, retArr) {\n    return retArr;\n  }\n  astBreakStatement(brNode, retArr) {\n    retArr.push('break;');\n    return retArr;\n  }\n  astContinueStatement(crNode, retArr) {\n    retArr.push('continue;\\n');\n    return retArr;\n  }\n  astForStatement(ast, retArr) {\n    return retArr;\n  }\n  astWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  astDoWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  astVariableDeclarator(iVarDecNode, retArr) {\n    this.astGeneric(iVarDecNode.id, retArr);\n    if (iVarDecNode.init !== null) {\n      retArr.push('=');\n      this.astGeneric(iVarDecNode.init, retArr);\n    }\n    return retArr;\n  }\n  astThisExpression(ast, retArr) {\n    return retArr;\n  }\n  astSequenceExpression(sNode, retArr) {\n    const { expressions } = sNode;\n    const sequenceResult = [];\n    for (let i = 0; i < expressions.length; i++) {\n      const expression = expressions[i];\n      const expressionResult = [];\n      this.astGeneric(expression, expressionResult);\n      sequenceResult.push(expressionResult.join(''));\n    }\n    if (sequenceResult.length > 1) {\n      retArr.push('(', sequenceResult.join(','), ')');\n    } else {\n      retArr.push(sequenceResult[0]);\n    }\n    return retArr;\n  }\n  astUnaryExpression(uNode, retArr) {\n    const unaryResult = this.checkAndUpconvertBitwiseUnary(uNode, retArr);\n    if (unaryResult) {\n      return retArr;\n    }\n\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(uNode, retArr) {}\n\n  astUpdateExpression(uNode, retArr) {\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n  astLogicalExpression(logNode, retArr) {\n    retArr.push('(');\n    this.astGeneric(logNode.left, retArr);\n    retArr.push(logNode.operator);\n    this.astGeneric(logNode.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n  astMemberExpression(ast, retArr) {\n    return retArr;\n  }\n  astCallExpression(ast, retArr) {\n    return retArr;\n  }\n  astArrayExpression(ast, retArr) {\n    return retArr;\n  }\n\n  getMemberExpressionDetails(ast) {\n    if (ast.type !== 'MemberExpression') {\n      throw this.astErrorOutput(`Expression ${ ast.type } not a MemberExpression`, ast);\n    }\n    let name = null;\n    let type = null;\n    const variableSignature = this.getVariableSignature(ast);\n    switch (variableSignature) {\n      case 'value':\n        return null;\n      case 'value.thread.value':\n      case 'this.thread.value':\n      case 'this.output.value':\n        return {\n          signature: variableSignature,\n            type: 'Integer',\n            name: ast.property.name\n        };\n      case 'value[]':\n        if (typeof ast.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object),\n            xProperty: ast.property\n        };\n      case 'value[][]':\n        if (typeof ast.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object),\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][]':\n        if (typeof ast.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][][]':\n        if (typeof ast.object.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value.value':\n        if (typeof ast.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        if (this.isAstMathVariable(ast)) {\n          name = ast.property.name;\n          return {\n            name,\n            origin: 'Math',\n            type: 'Number',\n            signature: variableSignature,\n          };\n        }\n        switch (ast.property.name) {\n          case 'r':\n          case 'g':\n          case 'b':\n          case 'a':\n            name = ast.object.name;\n            return {\n              name,\n              property: ast.property.name,\n                origin: 'user',\n                signature: variableSignature,\n                type: 'Number'\n            };\n          default:\n            throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        case 'this.constants.value':\n          if (typeof ast.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n              signature: variableSignature,\n          };\n        case 'this.constants.value[]':\n          if (typeof ast.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n              signature: variableSignature,\n              xProperty: ast.property,\n          };\n        case 'this.constants.value[][]': {\n          if (typeof ast.object.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n            signature: variableSignature,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n          };\n        }\n        case 'this.constants.value[][][]': {\n          if (typeof ast.object.object.object.property.name !== 'string') {\n            throw this.astErrorOutput('Unexpected expression', ast);\n          }\n          name = ast.object.object.object.property.name;\n          type = this.getConstantType(name);\n          if (!type) {\n            throw this.astErrorOutput('Constant has no type', ast);\n          }\n          return {\n            name,\n            type,\n            origin: 'constants',\n            signature: variableSignature,\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n          };\n        }\n        case 'fn()[]':\n        case 'fn()[][]':\n        case '[][]':\n          return {\n            signature: variableSignature,\n              property: ast.property,\n          };\n        default:\n          throw this.astErrorOutput('Unexpected expression', ast);\n    }\n  }\n\n  findIdentifierOrigin(astToFind) {\n    const stack = [this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack[0];\n      if (atNode.type === 'VariableDeclarator' && atNode.id && atNode.id.name && atNode.id.name === astToFind.name) {\n        return atNode;\n      }\n      stack.shift();\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      }\n    }\n    return null;\n  }\n\n  findLastReturn(ast) {\n    const stack = [ast || this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack.pop();\n      if (atNode.type === 'ReturnStatement') {\n        return atNode;\n      }\n      if (atNode.type === 'FunctionDeclaration') {\n        continue;\n      }\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      } else if (atNode.consequent) {\n        stack.push(atNode.consequent);\n      } else if (atNode.cases) {\n        stack.push(atNode.cases);\n      }\n    }\n    return null;\n  }\n\n  getInternalVariableName(name) {\n    if (!this._internalVariableNames.hasOwnProperty(name)) {\n      this._internalVariableNames[name] = 0;\n    }\n    this._internalVariableNames[name]++;\n    if (this._internalVariableNames[name] === 1) {\n      return name;\n    }\n    return name + this._internalVariableNames[name];\n  }\n\n  astKey(ast, separator = ',') {\n    if (!ast.start || !ast.end) throw new Error('AST start and end needed');\n    return `${ast.start}${separator}${ast.end}`;\n  }\n}\n\nconst typeLookupMap = {\n  'Number': 'Number',\n  'Float': 'Float',\n  'Integer': 'Integer',\n  'Array': 'Number',\n  'Array(2)': 'Number',\n  'Array(3)': 'Number',\n  'Array(4)': 'Number',\n  'Matrix(2)': 'Number',\n  'Matrix(3)': 'Number',\n  'Matrix(4)': 'Number',\n  'Array2D': 'Number',\n  'Array3D': 'Number',\n  'Input': 'Number',\n  'HTMLCanvas': 'Array(4)',\n  'OffscreenCanvas': 'Array(4)',\n  'HTMLImage': 'Array(4)',\n  'ImageBitmap': 'Array(4)',\n  'ImageData': 'Array(4)',\n  'HTMLVideo': 'Array(4)',\n  'HTMLImageArray': 'Array(4)',\n  'NumberTexture': 'Number',\n  'MemoryOptimizedNumberTexture': 'Number',\n  'Array1D(2)': 'Array(2)',\n  'Array1D(3)': 'Array(3)',\n  'Array1D(4)': 'Array(4)',\n  'Array2D(2)': 'Array(2)',\n  'Array2D(3)': 'Array(3)',\n  'Array2D(4)': 'Array(4)',\n  'Array3D(2)': 'Array(2)',\n  'Array3D(3)': 'Array(3)',\n  'Array3D(4)': 'Array(4)',\n  'ArrayTexture(1)': 'Number',\n  'ArrayTexture(2)': 'Array(2)',\n  'ArrayTexture(3)': 'Array(3)',\n  'ArrayTexture(4)': 'Array(4)',\n};\n\nmodule.exports = {\n  FunctionNode\n};\n},{\"../utils\":114,\"./function-tracer\":11,\"acorn\":1}],11:[function(require,module,exports){\nconst { utils } = require('../utils');\n\nfunction last(array) {\n  return array.length > 0 ? array[array.length - 1] : null;\n}\n\nconst states = {\n  trackIdentifiers: 'trackIdentifiers',\n  memberExpression: 'memberExpression',\n  inForLoopInit: 'inForLoopInit'\n};\n\nclass FunctionTracer {\n  constructor(ast) {\n    this.runningContexts = [];\n    this.functionContexts = [];\n    this.contexts = [];\n    this.functionCalls = [];\n    this.declarations = [];\n    this.identifiers = [];\n    this.functions = [];\n    this.returnStatements = [];\n    this.trackedIdentifiers = null;\n    this.states = [];\n    this.newFunctionContext();\n    this.scan(ast);\n  }\n\n  isState(state) {\n    return this.states[this.states.length - 1] === state;\n  }\n\n  hasState(state) {\n    return this.states.indexOf(state) > -1;\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.isState(state)) {\n      this.states.pop();\n    } else {\n      throw new Error(`Cannot pop the non-active state \"${state}\"`);\n    }\n  }\n\n  get currentFunctionContext() {\n    return last(this.functionContexts);\n  }\n\n  get currentContext() {\n    return last(this.runningContexts);\n  }\n\n  newFunctionContext() {\n    const newContext = { '@contextType': 'function' };\n    this.contexts.push(newContext);\n    this.functionContexts.push(newContext);\n  }\n\n  newContext(run) {\n    const newContext = Object.assign({ '@contextType': 'const/let' }, this.currentContext);\n    this.contexts.push(newContext);\n    this.runningContexts.push(newContext);\n    run();\n    const { currentFunctionContext } = this;\n    for (const p in currentFunctionContext) {\n      if (!currentFunctionContext.hasOwnProperty(p) || newContext.hasOwnProperty(p)) continue;\n      newContext[p] = currentFunctionContext[p];\n    }\n    this.runningContexts.pop();\n    return newContext;\n  }\n\n  useFunctionContext(run) {\n    const functionContext = last(this.functionContexts);\n    this.runningContexts.push(functionContext);\n    run();\n    this.runningContexts.pop();\n  }\n\n  getIdentifiers(run) {\n    const trackedIdentifiers = this.trackedIdentifiers = [];\n    this.pushState(states.trackIdentifiers);\n    run();\n    this.trackedIdentifiers = null;\n    this.popState(states.trackIdentifiers);\n    return trackedIdentifiers;\n  }\n\n  getDeclaration(name) {\n    const { currentContext, currentFunctionContext, runningContexts } = this;\n    const declaration = currentContext[name] || currentFunctionContext[name] || null;\n\n    if (\n      !declaration &&\n      currentContext === currentFunctionContext &&\n      runningContexts.length > 0\n    ) {\n      const previousRunningContext = runningContexts[runningContexts.length - 2];\n      if (previousRunningContext[name]) {\n        return previousRunningContext[name];\n      }\n    }\n\n    return declaration;\n  }\n\n  scan(ast) {\n    if (!ast) return;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.scan(ast[i]);\n      }\n      return;\n    }\n    switch (ast.type) {\n      case 'Program':\n        this.useFunctionContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'BlockStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'AssignmentExpression':\n      case 'LogicalExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'BinaryExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'UpdateExpression':\n        if (ast.operator === '++') {\n          const declaration = this.getDeclaration(ast.argument.name);\n          if (declaration) {\n            declaration.suggestedType = 'Integer';\n          }\n        }\n        this.scan(ast.argument);\n        break;\n      case 'UnaryExpression':\n        this.scan(ast.argument);\n        break;\n      case 'VariableDeclaration':\n        if (ast.kind === 'var') {\n          this.useFunctionContext(() => {\n            ast.declarations = utils.normalizeDeclarations(ast);\n            this.scan(ast.declarations);\n          });\n        } else {\n          ast.declarations = utils.normalizeDeclarations(ast);\n          this.scan(ast.declarations);\n        }\n        break;\n      case 'VariableDeclarator': {\n        const { currentContext } = this;\n        const inForLoopInit = this.hasState(states.inForLoopInit);\n        const declaration = {\n          ast: ast,\n          context: currentContext,\n          name: ast.id.name,\n          origin: 'declaration',\n          inForLoopInit,\n          inForLoopTest: null,\n          assignable: currentContext === this.currentFunctionContext || (!inForLoopInit && !currentContext.hasOwnProperty(ast.id.name)),\n          suggestedType: null,\n          valueType: null,\n          dependencies: null,\n          isSafe: null,\n        };\n        if (!currentContext[ast.id.name]) {\n          currentContext[ast.id.name] = declaration;\n        }\n        this.declarations.push(declaration);\n        this.scan(ast.id);\n        this.scan(ast.init);\n        break;\n      }\n      case 'FunctionExpression':\n      case 'FunctionDeclaration':\n        if (this.runningContexts.length === 0) {\n          this.scan(ast.body);\n        } else {\n          this.functions.push(ast);\n        }\n        break;\n      case 'IfStatement':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        if (ast.alternate) this.scan(ast.alternate);\n        break;\n      case 'ForStatement': {\n        let testIdentifiers;\n        const context = this.newContext(() => {\n          this.pushState(states.inForLoopInit);\n          this.scan(ast.init);\n          this.popState(states.inForLoopInit);\n\n          testIdentifiers = this.getIdentifiers(() => {\n            this.scan(ast.test);\n          });\n\n          this.scan(ast.update);\n          this.newContext(() => {\n            this.scan(ast.body);\n          });\n        });\n\n        if (testIdentifiers) {\n          for (const p in context) {\n            if (p === '@contextType') continue;\n            if (testIdentifiers.indexOf(p) > -1) {\n              context[p].inForLoopTest = true;\n            }\n          }\n        }\n        break;\n      }\n      case 'DoWhileStatement':\n      case 'WhileStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n          this.scan(ast.test);\n        });\n        break;\n      case 'Identifier': {\n        if (this.isState(states.trackIdentifiers)) {\n          this.trackedIdentifiers.push(ast.name);\n        }\n        this.identifiers.push({\n          context: this.currentContext,\n          declaration: this.getDeclaration(ast.name),\n          ast,\n        });\n        break;\n      }\n      case 'ReturnStatement':\n        this.returnStatements.push(ast);\n        this.scan(ast.argument);\n        break;\n      case 'MemberExpression':\n        this.pushState(states.memberExpression);\n        this.scan(ast.object);\n        this.scan(ast.property);\n        this.popState(states.memberExpression);\n        break;\n      case 'ExpressionStatement':\n        this.scan(ast.expression);\n        break;\n      case 'SequenceExpression':\n        this.scan(ast.expressions);\n        break;\n      case 'CallExpression':\n        this.functionCalls.push({\n          context: this.currentContext,\n          ast,\n        });\n        this.scan(ast.arguments);\n        break;\n      case 'ArrayExpression':\n        this.scan(ast.elements);\n        break;\n      case 'ConditionalExpression':\n        this.scan(ast.test);\n        this.scan(ast.alternate);\n        this.scan(ast.consequent);\n        break;\n      case 'SwitchStatement':\n        this.scan(ast.discriminant);\n        this.scan(ast.cases);\n        break;\n      case 'SwitchCase':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        break;\n\n      case 'ThisExpression':\n      case 'Literal':\n      case 'DebuggerStatement':\n      case 'EmptyStatement':\n      case 'BreakStatement':\n      case 'ContinueStatement':\n        break;\n      default:\n        throw new Error(`unhandled type \"${ast.type}\"`);\n    }\n  }\n}\n\nmodule.exports = {\n  FunctionTracer,\n};\n},{\"../utils\":114}],12:[function(require,module,exports){\nconst { glWiretap } = require('gl-wiretap');\nconst { utils } = require('../../utils');\n\nfunction toStringWithoutUtils(fn) {\n  return fn.toString()\n    .replace('=>', '')\n    .replace(/^function /, '')\n    .replace(/utils[.]/g, '/*utils.*/');\n}\n\nfunction glKernelString(Kernel, args, originKernel, setupContextString, destroyContextString) {\n  if (!originKernel.built) {\n    originKernel.build.apply(originKernel, args);\n  }\n  args = args ? Array.from(args).map(arg => {\n    switch (typeof arg) {\n      case 'boolean':\n        return new Boolean(arg);\n      case 'number':\n        return new Number(arg);\n      default:\n        return arg;\n    }\n  }) : null;\n  const uploadedValues = [];\n  const postResult = [];\n  const context = glWiretap(originKernel.context, {\n    useTrackablePrimitives: true,\n    onReadPixels: (targetName) => {\n      if (kernel.subKernels) {\n        if (!subKernelsResultVariableSetup) {\n          postResult.push(`    const result = { result: ${getRenderString(targetName, kernel)} };`);\n          subKernelsResultVariableSetup = true;\n        } else {\n          const property = kernel.subKernels[subKernelsResultIndex++].property;\n          postResult.push(`    result${isNaN(property) ? '.' + property : `[${property}]`} = ${getRenderString(targetName, kernel)};`);\n        }\n        if (subKernelsResultIndex === kernel.subKernels.length) {\n          postResult.push('    return result;');\n        }\n        return;\n      }\n      if (targetName) {\n        postResult.push(`    return ${getRenderString(targetName, kernel)};`);\n      } else {\n        postResult.push(`    return null;`);\n      }\n    },\n    onUnrecognizedArgumentLookup: (argument) => {\n      const argumentName = findKernelValue(argument, kernel.kernelArguments, [], context, uploadedValues);\n      if (argumentName) {\n        return argumentName;\n      }\n      const constantName = findKernelValue(argument, kernel.kernelConstants, constants ? Object.keys(constants).map(key => constants[key]) : [], context, uploadedValues);\n      if (constantName) {\n        return constantName;\n      }\n      return null;\n    }\n  });\n  let subKernelsResultVariableSetup = false;\n  let subKernelsResultIndex = 0;\n  const {\n    source,\n    canvas,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    kernelArguments,\n    kernelConstants,\n    tactic,\n  } = originKernel;\n  const kernel = new Kernel(source, {\n    canvas,\n    context,\n    checkContext: false,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    tactic,\n  });\n  let result = [];\n  context.setIndent(2);\n  kernel.build.apply(kernel, args);\n  result.push(context.toString());\n  context.reset();\n\n  kernel.kernelArguments.forEach((kernelArgument, i) => {\n    switch (kernelArgument.type) {\n      case 'Integer':\n      case 'Boolean':\n      case 'Number':\n      case 'Float':\n      case 'Array':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'HTMLCanvas':\n      case 'HTMLImage':\n      case 'HTMLVideo':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'HTMLImageArray':\n        for (let imageIndex = 0; imageIndex < args[i].length; imageIndex++) {\n          const arg = args[i];\n          context.insertVariable(`uploadValue_${kernelArgument.name}[${imageIndex}]`, arg[imageIndex]);\n        }\n        break;\n      case 'Input':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'MemoryOptimizedNumberTexture':\n      case 'NumberTexture':\n      case 'Array1D(2)':\n      case 'Array1D(3)':\n      case 'Array1D(4)':\n      case 'Array2D(2)':\n      case 'Array2D(3)':\n      case 'Array2D(4)':\n      case 'Array3D(2)':\n      case 'Array3D(3)':\n      case 'Array3D(4)':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, args[i].texture);\n        break;\n      default:\n        throw new Error(`unhandled kernelArgumentType insertion for glWiretap of type ${kernelArgument.type}`);\n    }\n  });\n  result.push('/** start of injected functions **/');\n  result.push(`function ${toStringWithoutUtils(utils.flattenTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten2dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten3dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten4dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.isArray)}`);\n  if (kernel.renderOutput !== kernel.renderTexture && kernel.formatValues) {\n    result.push(\n      `  const renderOutput = function ${toStringWithoutUtils(kernel.formatValues)};`\n    );\n  }\n  result.push('/** end of injected functions **/');\n  result.push(`  const innerKernel = function (${kernel.kernelArguments.map(kernelArgument => kernelArgument.varName).join(', ')}) {`);\n  context.setIndent(4);\n  kernel.run.apply(kernel, args);\n  if (kernel.renderKernels) {\n    kernel.renderKernels();\n  } else if (kernel.renderOutput) {\n    kernel.renderOutput();\n  }\n  result.push('    /** start setup uploads for kernel values **/');\n  kernel.kernelArguments.forEach(kernelArgument => {\n    result.push('    ' + kernelArgument.getStringValueHandler().split('\\n').join('\\n    '));\n  });\n  result.push('    /** end setup uploads for kernel values **/');\n  result.push(context.toString());\n  if (kernel.renderOutput === kernel.renderTexture) {\n    context.reset();\n    const framebufferName = context.getContextVariableName(kernel.framebuffer);\n    if (kernel.renderKernels) {\n      const results = kernel.renderKernels();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n      result: {\n        texture: ${ textureName },\n        type: '${ results.result.type }',\n        toArray: ${ getToArrayString(results.result, textureName, framebufferName) }\n      },`);\n      const { subKernels, mappedTextures } = kernel;\n      for (let i = 0; i < subKernels.length; i++) {\n        const texture = mappedTextures[i];\n        const subKernel = subKernels[i];\n        const subKernelResult = results[subKernel.property];\n        const subKernelTextureName = context.getContextVariableName(texture.texture);\n        result.push(`\n      ${subKernel.property}: {\n        texture: ${ subKernelTextureName },\n        type: '${ subKernelResult.type }',\n        toArray: ${ getToArrayString(subKernelResult, subKernelTextureName, framebufferName) }\n      },`);\n      }\n      result.push(`    };`);\n    } else {\n      const rendered = kernel.renderOutput();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n        texture: ${ textureName },\n        type: '${ rendered.type }',\n        toArray: ${ getToArrayString(rendered, textureName, framebufferName) }\n      };`);\n    }\n  }\n  result.push(`    ${destroyContextString ? '\\n' + destroyContextString + '    ': ''}`);\n  result.push(postResult.join('\\n'));\n  result.push('  };');\n  if (kernel.graphical) {\n    result.push(getGetPixelsString(kernel));\n    result.push(`  innerKernel.getPixels = getPixels;`);\n  }\n  result.push('  return innerKernel;');\n\n  let constantsUpload = [];\n  kernelConstants.forEach((kernelConstant) => {\n    constantsUpload.push(`${kernelConstant.getStringValueHandler()}`);\n  });\n  return `function kernel(settings) {\n  const { context, constants } = settings;\n  ${constantsUpload.join('')}\n  ${setupContextString ? setupContextString : ''}\n${result.join('\\n')}\n}`;\n}\n\nfunction getRenderString(targetName, kernel) {\n  const readBackValue = kernel.precision === 'single' ? targetName : `new Float32Array(${targetName}.buffer)`;\n  if (kernel.output[2]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]}, ${kernel.output[2]})`;\n  }\n  if (kernel.output[1]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]})`;\n  }\n\n  return `renderOutput(${readBackValue}, ${kernel.output[0]})`;\n}\n\nfunction getGetPixelsString(kernel) {\n  const getPixels = kernel.getPixels.toString();\n  const useFunctionKeyword = !/^function/.test(getPixels);\n  return utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ getPixels }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      }\n      return null;\n    },\n    thisLookup: (property) => {\n      if (property === 'context') {\n        return null;\n      }\n      if (kernel.hasOwnProperty(property)) {\n        return JSON.stringify(kernel[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n}\n\nfunction getToArrayString(kernelResult, textureName, framebufferName) {\n  const toArray = kernelResult.toArray.toString();\n  const useFunctionKeyword = !/^function/.test(toArray);\n  const flattenedFunctions = utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ toArray }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      } else if (object === 'this') {\n        if (name === 'framebuffer') {\n          return '';\n        }\n        return `${useFunctionKeyword ? 'function ' : ''}${kernelResult[name].toString()}`;\n      } else {\n        throw new Error('unhandled fromObject');\n      }\n    },\n    thisLookup: (property, isDeclaration) => {\n      if (property === 'texture') {\n        return textureName;\n      }\n      if (property === 'context') {\n        if (isDeclaration) return null;\n        return 'gl';\n      }\n      if (kernelResult.hasOwnProperty(property)) {\n        return JSON.stringify(kernelResult[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n  return `() => {\n  function framebuffer() { return ${framebufferName}; };\n  ${flattenedFunctions}\n  return toArray();\n  }`;\n}\n\nfunction findKernelValue(argument, kernelValues, values, context, uploadedValues) {\n  if (argument === null) return null;\n  if (kernelValues === null) return null;\n  switch (typeof argument) {\n    case 'boolean':\n    case 'number':\n      return null;\n  }\n  if (\n    typeof HTMLImageElement !== 'undefined' &&\n    argument instanceof HTMLImageElement\n  ) {\n    for (let i = 0; i < kernelValues.length; i++) {\n      const kernelValue = kernelValues[i];\n      if (kernelValue.type !== 'HTMLImageArray' && kernelValue) continue;\n      if (kernelValue.uploadValue !== argument) continue;\n      const variableIndex = values[i].indexOf(argument);\n      if (variableIndex === -1) continue;\n      const variableName = `uploadValue_${kernelValue.name}[${variableIndex}]`;\n      context.insertVariable(variableName, argument);\n      return variableName;\n    }\n  }\n\n  for (let i = 0; i < kernelValues.length; i++) {\n    const kernelValue = kernelValues[i];\n    if (argument !== kernelValue.uploadValue) continue;\n    const variable = `uploadValue_${kernelValue.name}`;\n    context.insertVariable(variable, kernelValue);\n    return variable;\n  }\n  return null;\n}\n\nmodule.exports = {\n  glKernelString\n};\n},{\"../../utils\":114,\"gl-wiretap\":3}],13:[function(require,module,exports){\nconst { Kernel } = require('../kernel');\nconst { utils } = require('../../utils');\nconst { GLTextureArray2Float } = require('./texture/array-2-float');\nconst { GLTextureArray2Float2D } = require('./texture/array-2-float-2d');\nconst { GLTextureArray2Float3D } = require('./texture/array-2-float-3d');\nconst { GLTextureArray3Float } = require('./texture/array-3-float');\nconst { GLTextureArray3Float2D } = require('./texture/array-3-float-2d');\nconst { GLTextureArray3Float3D } = require('./texture/array-3-float-3d');\nconst { GLTextureArray4Float } = require('./texture/array-4-float');\nconst { GLTextureArray4Float2D } = require('./texture/array-4-float-2d');\nconst { GLTextureArray4Float3D } = require('./texture/array-4-float-3d');\nconst { GLTextureFloat } = require('./texture/float');\nconst { GLTextureFloat2D } = require('./texture/float-2d');\nconst { GLTextureFloat3D } = require('./texture/float-3d');\nconst { GLTextureMemoryOptimized } = require('./texture/memory-optimized');\nconst { GLTextureMemoryOptimized2D } = require('./texture/memory-optimized-2d');\nconst { GLTextureMemoryOptimized3D } = require('./texture/memory-optimized-3d');\nconst { GLTextureUnsigned } = require('./texture/unsigned');\nconst { GLTextureUnsigned2D } = require('./texture/unsigned-2d');\nconst { GLTextureUnsigned3D } = require('./texture/unsigned-3d');\nconst { GLTextureGraphical } = require('./texture/graphical');\n\nclass GLKernel extends Kernel {\n  static get mode() {\n    return 'gpu';\n  }\n\n  static getIsFloatRead() {\n    const kernelString = `function kernelFunction() {\n      return 1;\n    }`;\n    const kernel = new this(kernelString, {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [1],\n      precision: 'single',\n      returnType: 'Number',\n      tactic: 'speed',\n    });\n    kernel.build();\n    kernel.run();\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return result[0] === 1;\n  }\n\n  static getIsIntegerDivisionAccurate() {\n    function kernelFunction(v1, v2) {\n      return v1[this.thread.x] / v2[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [2],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [6, 6030401],\n      [3, 3991]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return result[0] === 2 && result[1] === 1511;\n  }\n\n  static getIsSpeedTacticSupported() {\n    function kernelFunction(value) {\n      return value[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [4],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [0, 1, 2, 3]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return Math.round(result[0]) === 0 && Math.round(result[1]) === 1 && Math.round(result[2]) === 2 && Math.round(result[3]) === 3;\n  }\n\n  static get testCanvas() {\n    throw new Error(`\"testCanvas\" not defined on ${ this.name }`);\n  }\n\n  static get testContext() {\n    throw new Error(`\"testContext\" not defined on ${ this.name }`);\n  }\n\n  static getFeatures() {\n    const gl = this.testContext;\n    const isDrawBuffers = this.getIsDrawBuffers();\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      isTextureFloat: this.getIsTextureFloat(),\n      isDrawBuffers,\n      kernelMap: isDrawBuffers,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  static setupFeatureChecks() {\n    throw new Error(`\"setupFeatureChecks\" not defined on ${ this.name }`);\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return kernel.getVariablePrecisionString() + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  setFixIntegerDivisionAccuracy(fix) {\n    this.fixIntegerDivisionAccuracy = fix;\n    return this;\n  }\n\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  setFloatTextures(flag) {\n    utils.warnDeprecated('method', 'setFloatTextures', 'setOptimizeFloatMemory');\n    this.floatTextures = flag;\n    return this;\n  }\n\n  static nativeFunctionArguments(source) {\n    const argumentTypes = [];\n    const argumentNames = [];\n    const states = [];\n    const isStartingVariableName = /^[a-zA-Z_]/;\n    const isVariableChar = /[a-zA-Z_0-9]/;\n    let i = 0;\n    let argumentName = null;\n    let argumentType = null;\n    while (i < source.length) {\n      const char = source[i];\n      const nextChar = source[i + 1];\n      const state = states.length > 0 ? states[states.length - 1] : null;\n\n      if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '*') {\n        states.push('MULTI_LINE_COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'MULTI_LINE_COMMENT' && char === '*' && nextChar === '/') {\n        states.pop();\n        i += 2;\n        continue;\n      }\n\n      else if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '/') {\n        states.push('COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'COMMENT' && char === '\\n') {\n        states.pop();\n        i++;\n        continue;\n      }\n\n      else if (state === null && char === '(') {\n        states.push('FUNCTION_ARGUMENTS');\n        i++;\n        continue;\n      } else if (state === 'FUNCTION_ARGUMENTS') {\n        if (char === ')') {\n          states.pop();\n          break;\n        }\n        if (char === 'f' && nextChar === 'l' && source[i + 2] === 'o' && source[i + 3] === 'a' && source[i + 4] === 't' && source[i + 5] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'float';\n          argumentName = '';\n          i += 6;\n          continue;\n        } else if (char === 'i' && nextChar === 'n' && source[i + 2] === 't' && source[i + 3] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'int';\n          argumentName = '';\n          i += 4;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '2' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec2';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '3' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec3';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '4' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec4';\n          argumentName = '';\n          i += 5;\n          continue;\n        }\n      }\n\n      else if (state === 'DECLARE_VARIABLE') {\n        if (argumentName === '') {\n          if (char === ' ') {\n            i++;\n            continue;\n          }\n          if (!isStartingVariableName.test(char)) {\n            throw new Error('variable name is not expected string');\n          }\n        }\n        argumentName += char;\n        if (!isVariableChar.test(nextChar)) {\n          states.pop();\n          argumentNames.push(argumentName);\n          argumentTypes.push(typeMap[argumentType]);\n        }\n      }\n\n      i++;\n    }\n    if (states.length > 0) {\n      throw new Error('GLSL function was not parsable');\n    }\n    return {\n      argumentNames,\n      argumentTypes,\n    };\n  }\n\n  static nativeFunctionReturnType(source) {\n    return typeMap[source.match(/int|float|vec[2-4]/)[0]];\n  }\n\n  static combineKernels(combinedKernel, lastKernel) {\n    combinedKernel.apply(null, arguments);\n    const {\n      texSize,\n      context,\n      threadDim\n    } = lastKernel.texSize;\n    let result;\n    if (lastKernel.precision === 'single') {\n      const w = texSize[0];\n      const h = Math.ceil(texSize[1] / 4);\n      result = new Float32Array(w * h * 4 * 4);\n      context.readPixels(0, 0, w, h * 4, context.RGBA, context.FLOAT, result);\n    } else {\n      const bytes = new Uint8Array(texSize[0] * texSize[1] * 4);\n      context.readPixels(0, 0, texSize[0], texSize[1], context.RGBA, context.UNSIGNED_BYTE, bytes);\n      result = new Float32Array(bytes.buffer);\n    }\n\n    result = result.subarray(0, threadDim[0] * threadDim[1] * threadDim[2]);\n\n    if (lastKernel.output.length === 1) {\n      return result;\n    } else if (lastKernel.output.length === 2) {\n      return utils.splitArray(result, lastKernel.output[0]);\n    } else if (lastKernel.output.length === 3) {\n      const cube = utils.splitArray(result, lastKernel.output[0] * lastKernel.output[1]);\n      return cube.map(function(x) {\n        return utils.splitArray(x, lastKernel.output[0]);\n      });\n    }\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.transferValues = null;\n    this.formatValues = null;\n    this.TextureConstructor = null;\n    this.renderOutput = null;\n    this.renderRawOutput = null;\n    this.texSize = null;\n    this.translatedSource = null;\n    this.compiledFragmentShader = null;\n    this.compiledVertexShader = null;\n    this.switchingKernels = null;\n    this._textureSwitched = null;\n    this._mappedTextureSwitched = null;\n  }\n\n  checkTextureSize() {\n    const { features } = this.constructor;\n    if (this.texSize[0] > features.maxTextureSize || this.texSize[1] > features.maxTextureSize) {\n      throw new Error(`Texture size [${this.texSize[0]},${this.texSize[1]}] generated by kernel is larger than supported size [${features.maxTextureSize},${features.maxTextureSize}]`);\n    }\n  }\n\n  translateSource() {\n    throw new Error(`\"translateSource\" not defined on ${this.constructor.name}`);\n  }\n\n  pickRenderStrategy(args) {\n    if (this.graphical) {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = (pixels) => pixels;\n      this.TextureConstructor = GLTextureGraphical;\n      return null;\n    }\n    if (this.precision === 'unsigned') {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = this.readPackedPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              return null;\n            }\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              return this.requestFallback(args);\n        }\n      } else {\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToArrays;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            this.renderOutput = this.renderValues;\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              this.formatValues = utils.erect3DPackedFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              this.formatValues = utils.erect2DPackedFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              this.formatValues = utils.erectPackedFloat;\n              return null;\n            }\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              return this.requestFallback(args);\n        }\n      }\n    } else if (this.precision === 'single') {\n      this.renderRawOutput = this.readFloatPixelsToFloat32Array;\n      this.transferValues = this.readFloatPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.optimizeFloatMemory) {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureMemoryOptimized;\n                return null;\n              }\n            } else {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureFloat3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureFloat2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureFloat;\n                return null;\n              }\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              return null;\n            }\n          }\n        }\n      }\n      this.renderOutput = this.renderValues;\n      if (this.subKernels !== null) {\n        this.renderKernels = this.renderKernelsToArrays;\n      }\n      if (this.optimizeFloatMemory) {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized3D;\n              this.formatValues = utils.erectMemoryOptimized3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized2D;\n              this.formatValues = utils.erectMemoryOptimized2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureMemoryOptimized;\n              this.formatValues = utils.erectMemoryOptimizedFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      } else {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureFloat3D;\n              this.formatValues = utils.erect3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureFloat2D;\n              this.formatValues = utils.erect2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureFloat;\n              this.formatValues = utils.erectFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      }\n    } else {\n      throw new Error(`unhandled precision of \"${this.precision}\"`);\n    }\n\n    throw new Error(`unhandled return type \"${this.returnType}\"`);\n  }\n\n  getKernelString() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultTexture() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Integer':\n      case 'Number':\n        return this.getMainResultNumberTexture();\n      case 'Array(2)':\n        return this.getMainResultArray2Texture();\n      case 'Array(3)':\n        return this.getMainResultArray3Texture();\n      case 'Array(4)':\n        return this.getMainResultArray4Texture();\n      default:\n        throw new Error(`unhandled returnType type ${ this.returnType }`);\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultSubKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultGraphical() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultMemoryOptimizedFloats() {\n    throw new Error(`abstract method call`);\n  }\n  getMainResultPackedPixels() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultString() {\n    if (this.graphical) {\n      return this.getMainResultGraphical();\n    } else if (this.precision === 'single') {\n      if (this.optimizeFloatMemory) {\n        return this.getMainResultMemoryOptimizedFloats();\n      }\n      return this.getMainResultTexture();\n    } else {\n      return this.getMainResultPackedPixels();\n    }\n  }\n\n  getMainResultNumberTexture() {\n    return utils.linesToString(this.getMainResultKernelNumberTexture()) +\n      utils.linesToString(this.getMainResultSubKernelNumberTexture());\n  }\n\n  getMainResultArray2Texture() {\n    return utils.linesToString(this.getMainResultKernelArray2Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray2Texture());\n  }\n\n  getMainResultArray3Texture() {\n    return utils.linesToString(this.getMainResultKernelArray3Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray3Texture());\n  }\n\n  getMainResultArray4Texture() {\n    return utils.linesToString(this.getMainResultKernelArray4Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray4Texture());\n  }\n\n  getFloatTacticDeclaration() {\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    return `precision ${variablePrecision} float;\\n`;\n  }\n\n  getIntTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic, true)} int;\\n`;\n  }\n\n  getSampler2DTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2D;\\n`;\n  }\n\n  getSampler2DArrayTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2DArray;\\n`;\n  }\n\n  renderTexture() {\n    return this.immutable ? this.texture.clone() : this.texture;\n  }\n  readPackedPixelsToUint8Array() {\n    if (this.precision !== 'unsigned') throw new Error('Requires this.precision to be \"unsigned\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const result = new Uint8Array(texSize[0] * texSize[1] * 4);\n    gl.readPixels(0, 0, texSize[0], texSize[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n\n  readPackedPixelsToFloat32Array() {\n    return new Float32Array(this.readPackedPixelsToUint8Array().buffer);\n  }\n\n  readFloatPixelsToFloat32Array() {\n    if (this.precision !== 'single') throw new Error('Requires this.precision to be \"single\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const w = texSize[0];\n    const h = texSize[1];\n    const result = new Float32Array(w * h * 4);\n    gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n\n  getPixels(flip) {\n    const {\n      context: gl,\n      output\n    } = this;\n    const [width, height] = output;\n    const pixels = new Uint8Array(width * height * 4);\n    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n    return new Uint8ClampedArray((flip ? pixels : utils.flipPixels(pixels, width, height)).buffer);\n  }\n\n  renderKernelsToArrays() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    for (let i = 0; i < this.subKernels.length; i++) {\n      result[this.subKernels[i].property] = this.mappedTextures[i].toArray();\n    }\n    return result;\n  }\n\n  renderKernelsToTextures() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    if (this.immutable) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i].clone();\n      }\n    } else {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i];\n      }\n    }\n    return result;\n  }\n\n  resetSwitchingKernels() {\n    const existingValue = this.switchingKernels;\n    this.switchingKernels = null;\n    return existingValue;\n  }\n\n  setOutput(output) {\n    const newOutput = this.toKernelOutput(output);\n    if (this.program) {\n      if (!this.dynamicOutput) {\n        throw new Error('Resizing a kernel with dynamicOutput: false is not possible');\n      }\n      const newThreadDim = [newOutput[0], newOutput[1] || 1, newOutput[2] || 1];\n      const newTexSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, newThreadDim);\n      const oldTexSize = this.texSize;\n      if (oldTexSize) {\n        const oldPrecision = this.getVariablePrecisionString(oldTexSize, this.tactic);\n        const newPrecision = this.getVariablePrecisionString(newTexSize, this.tactic);\n        if (oldPrecision !== newPrecision) {\n          if (this.debug) {\n            console.warn('Precision requirement changed, asking GPU instance to recompile');\n          }\n          this.switchKernels({\n            type: 'outputPrecisionMismatch',\n            precision: newPrecision,\n            needed: output\n          });\n          return;\n        }\n      }\n      this.output = newOutput;\n      this.threadDim = newThreadDim;\n      this.texSize = newTexSize;\n      const { context: gl } = this;\n      gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n      this.updateMaxTexSize();\n      this.framebuffer.width = this.texSize[0];\n      this.framebuffer.height = this.texSize[1];\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      this.canvas.width = this.maxTexSize[0];\n      this.canvas.height = this.maxTexSize[1];\n      if (this.texture) {\n        this.texture.delete();\n      }\n      this.texture = null;\n      this._setupOutputTexture();\n      if (this.mappedTextures && this.mappedTextures.length > 0) {\n        for (let i = 0; i < this.mappedTextures.length; i++) {\n          this.mappedTextures[i].delete();\n        }\n        this.mappedTextures = null;\n        this._setupSubOutputTextures();\n      }\n    } else {\n      this.output = newOutput;\n    }\n    return this;\n  }\n  renderValues() {\n    return this.formatValues(\n      this.transferValues(),\n      this.output[0],\n      this.output[1],\n      this.output[2]\n    );\n  }\n  switchKernels(reason) {\n    if (this.switchingKernels) {\n      this.switchingKernels.push(reason);\n    } else {\n      this.switchingKernels = [reason];\n    }\n  }\n  getVariablePrecisionString(textureSize = this.texSize, tactic = this.tactic, isInt = false) {\n    if (!tactic) {\n      if (!this.constructor.features.isSpeedTacticSupported) return 'highp';\n      const low = this.constructor.features[isInt ? 'lowIntPrecision' : 'lowFloatPrecision'];\n      const medium = this.constructor.features[isInt ? 'mediumIntPrecision' : 'mediumFloatPrecision'];\n      const high = this.constructor.features[isInt ? 'highIntPrecision' : 'highFloatPrecision'];\n      const requiredSize = Math.log2(textureSize[0] * textureSize[1]);\n      if (requiredSize <= low.rangeMax) {\n        return 'lowp';\n      } else if (requiredSize <= medium.rangeMax) {\n        return 'mediump';\n      } else if (requiredSize <= high.rangeMax) {\n        return 'highp';\n      } else {\n        throw new Error(`The required size exceeds that of the ability of your system`);\n      }\n    }\n    switch (tactic) {\n      case 'speed':\n        return 'lowp';\n      case 'balanced':\n        return 'mediump';\n      case 'precision':\n        return 'highp';\n      default:\n        throw new Error(`Unknown tactic \"${tactic}\" use \"speed\", \"balanced\", \"precision\", or empty for auto`);\n    }\n  }\n\n  updateTextureArgumentRefs(kernelValue, arg) {\n    if (!this.immutable) return;\n    if (this.texture.texture === arg.texture) {\n      const { prevArg } = kernelValue;\n      if (prevArg) {\n        if (prevArg.texture._refs === 1) {\n          this.texture.delete();\n          this.texture = prevArg.clone();\n          this._textureSwitched = true;\n        }\n        prevArg.delete();\n      }\n      kernelValue.prevArg = arg.clone();\n    } else if (this.mappedTextures && this.mappedTextures.length > 0) {\n      const { mappedTextures } = this;\n      for (let i = 0; i < mappedTextures.length; i++) {\n        const mappedTexture = mappedTextures[i];\n        if (mappedTexture.texture === arg.texture) {\n          const { prevArg } = kernelValue;\n          if (prevArg) {\n            if (prevArg.texture._refs === 1) {\n              mappedTexture.delete();\n              mappedTextures[i] = prevArg.clone();\n              this._mappedTextureSwitched[i] = true;\n            }\n            prevArg.delete();\n          }\n          kernelValue.prevArg = arg.clone();\n          return;\n        }\n      }\n    }\n  }\n\n  onActivate(previousKernel) {\n    this._textureSwitched = true;\n    this.texture = previousKernel.texture;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        this._mappedTextureSwitched[i] = true;\n      }\n      this.mappedTextures = previousKernel.mappedTextures;\n    }\n  }\n\n  initCanvas() {}\n}\n\nconst typeMap = {\n  int: 'Integer',\n  float: 'Number',\n  vec2: 'Array(2)',\n  vec3: 'Array(3)',\n  vec4: 'Array(4)',\n};\n\nmodule.exports = {\n  GLKernel\n};\n},{\"../../utils\":114,\"../kernel\":36,\"./texture/array-2-float\":16,\"./texture/array-2-float-2d\":14,\"./texture/array-2-float-3d\":15,\"./texture/array-3-float\":19,\"./texture/array-3-float-2d\":17,\"./texture/array-3-float-3d\":18,\"./texture/array-4-float\":22,\"./texture/array-4-float-2d\":20,\"./texture/array-4-float-3d\":21,\"./texture/float\":25,\"./texture/float-2d\":23,\"./texture/float-3d\":24,\"./texture/graphical\":26,\"./texture/memory-optimized\":30,\"./texture/memory-optimized-2d\":28,\"./texture/memory-optimized-3d\":29,\"./texture/unsigned\":33,\"./texture/unsigned-2d\":31,\"./texture/unsigned-3d\":32}],14:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect2DArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float2D\n};\n},{\"../../../utils\":114,\"./float\":25}],15:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect3DArray2(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float3D\n};\n},{\"../../../utils\":114,\"./float\":25}],16:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erectArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float\n};\n},{\"../../../utils\":114,\"./float\":25}],17:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect2DArray3(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float2D\n};\n},{\"../../../utils\":114,\"./float\":25}],18:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect3DArray3(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float3D\n};\n},{\"../../../utils\":114,\"./float\":25}],19:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erectArray3(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float\n};\n},{\"../../../utils\":114,\"./float\":25}],20:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect2DArray4(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float2D\n};\n},{\"../../../utils\":114,\"./float\":25}],21:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect3DArray4(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float3D\n};\n},{\"../../../utils\":114,\"./float\":25}],22:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erectArray4(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float\n};\n},{\"../../../utils\":114,\"./float\":25}],23:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat2D\n};\n},{\"../../../utils\":114,\"./float\":25}],24:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat3D\n};\n},{\"../../../utils\":114,\"./float\":25}],25:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureFloat extends GLTexture {\n  get textureType() {\n    return this.context.FLOAT;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  renderRawOutput() {\n    const gl = this.context;\n    const size = this.size;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Float32Array(size[0] * size[1] * 4);\n    gl.readPixels(0, 0, size[0], size[1], gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return this.renderRawOutput();\n  }\n  toArray() {\n    return utils.erectFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat\n};\n},{\"../../../utils\":114,\"./index\":27}],26:[function(require,module,exports){\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureGraphical extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return this.renderValues();\n  }\n}\n\nmodule.exports = {\n  GLTextureGraphical\n};\n},{\"./unsigned\":33}],27:[function(require,module,exports){\nconst { Texture } = require('../../../texture');\n\nclass GLTexture extends Texture {\n  get textureType() {\n    throw new Error(`\"textureType\" not implemented on ${ this.name }`);\n  }\n\n  clone() {\n    return new this.constructor(this);\n  }\n\n  beforeMutate() {\n    if (this.texture._refs > 1) {\n      this.newTexture();\n      return true;\n    }\n    return false;\n  }\n\n  cloneTexture() {\n    this.texture._refs--;\n    const { context: gl, size, texture, kernel } = this;\n    if (kernel.debug) {\n      console.warn('cloning internal texture');\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, size[0], size[1]);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  newTexture() {\n    this.texture._refs--;\n    const gl = this.context;\n    const size = this.size;\n    const kernel = this.kernel;\n    if (kernel.debug) {\n      console.warn('new internal texture');\n    }\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  clear() {\n    if (this.texture._refs) {\n      this.texture._refs--;\n      const gl = this.context;\n      const target = this.texture = gl.createTexture();\n      selectTexture(gl, target);\n      const size = this.size;\n      target._refs = 1;\n      gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    }\n    const { context: gl, texture } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    gl.clearColor(0, 0, 0, 0);\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n  }\n\n  delete() {\n    if (this._deleted) return;\n    this._deleted = true;\n    if (this.texture._refs) {\n      this.texture._refs--;\n      if (this.texture._refs) return;\n    }\n    this.context.deleteTexture(this.texture);\n  }\n\n  framebuffer() {\n    if (!this._framebuffer) {\n      this._framebuffer = this.kernel.getRawValueFramebuffer(this.size[0], this.size[1]);\n    }\n    return this._framebuffer;\n  }\n}\n\nfunction selectTexture(gl, texture) {\n  gl.activeTexture(gl.TEXTURE15);\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n}\n\nmodule.exports = { GLTexture };\n},{\"../../../texture\":113}],28:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized2D\n};\n},{\"../../../utils\":114,\"./float\":25}],29:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized3D\n};\n},{\"../../../utils\":114,\"./float\":25}],30:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimizedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized\n};\n},{\"../../../utils\":114,\"./float\":25}],31:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned2D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect2DPackedFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned2D\n};\n},{\"../../../utils\":114,\"./unsigned\":33}],32:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned3D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect3DPackedFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned3D\n};\n},{\"../../../utils\":114,\"./unsigned\":33}],33:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureUnsigned extends GLTexture {\n  get textureType() {\n    return this.context.UNSIGNED_BYTE;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  renderRawOutput() {\n    const { context: gl } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Uint8Array(this.size[0] * this.size[1] * 4);\n    gl.readPixels(0, 0, this.size[0], this.size[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return new Float32Array(this.renderRawOutput().buffer);\n  }\n  toArray() {\n    return utils.erectPackedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned\n};\n},{\"../../../utils\":114,\"./index\":27}],34:[function(require,module,exports){\nconst getContext = require('gl');\nconst { WebGLKernel } = require('../web-gl/kernel');\nconst { glKernelString } = require('../gl/kernel-string');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nclass HeadlessGLKernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) return isSupported;\n    this.setupFeatureChecks();\n    isSupported = testContext !== null;\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    testCanvas = null;\n    testExtensions = null;\n    if (typeof getContext !== 'function') return;\n    try { \n      testContext = getContext(2, 2, {\n        preserveDrawingBuffer: true\n      });\n      if (!testContext || !testContext.getExtension) return;\n      testExtensions = {\n        STACKGL_resize_drawingbuffer: testContext.getExtension('STACKGL_resize_drawingbuffer'),\n        STACKGL_destroy_context: testContext.getExtension('STACKGL_destroy_context'),\n        OES_texture_float: testContext.getExtension('OES_texture_float'),\n        OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n        OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n        WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n        WEBGL_color_buffer_float: testContext.getExtension('WEBGL_color_buffer_float'),\n      };\n      features = this.getFeatures();\n    } catch (e) {\n      console.warn(e);\n    }\n  }\n\n  static isContextMatch(context) {\n    try {\n      return context.getParameter(context.RENDERER) === 'ANGLE';\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  initCanvas() {\n    return {};\n  }\n\n  initContext() {\n    return getContext(2, 2, {\n      preserveDrawingBuffer: true\n    });\n  }\n\n  initExtensions() {\n    this.extensions = {\n      STACKGL_resize_drawingbuffer: this.context.getExtension('STACKGL_resize_drawingbuffer'),\n      STACKGL_destroy_context: this.context.getExtension('STACKGL_destroy_context'),\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n    };\n  }\n\n  build() {\n    super.build.apply(this, arguments);\n    if (!this.fallbackRequested) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n  }\n\n  destroyExtensions() {\n    this.extensions.STACKGL_resize_drawingbuffer = null;\n    this.extensions.STACKGL_destroy_context = null;\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('STACKGL_destroy_context');\n    if (extension && extension.destroy) {\n      extension.destroy();\n    }\n  }\n\n  toString() {\n    const setupContextString = `const gl = context || require('gl')(1, 1);\\n`;\n    const destroyContextString = `    if (!context) { gl.getExtension('STACKGL_destroy_context').destroy(); }\\n`;\n    return glKernelString(this.constructor, arguments, this, setupContextString, destroyContextString);\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    if (this.graphical && this.extensions.STACKGL_resize_drawingbuffer) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n    return this;\n  }\n}\n\nmodule.exports = {\n  HeadlessGLKernel\n};\n},{\"../gl/kernel-string\":12,\"../web-gl/kernel\":70,\"gl\":2}],35:[function(require,module,exports){\nclass KernelValue {\n  constructor(value, settings) {\n    const {\n      name,\n      kernel,\n      context,\n      checkContext,\n      onRequestContextHandle,\n      onUpdateValueMismatch,\n      origin,\n      strictIntegers,\n      type,\n      tactic,\n    } = settings;\n    if (!name) {\n      throw new Error('name not set');\n    }\n    if (!type) {\n      throw new Error('type not set');\n    }\n    if (!origin) {\n      throw new Error('origin not set');\n    }\n    if (origin !== 'user' && origin !== 'constants') {\n      throw new Error(`origin must be \"user\" or \"constants\" value is \"${ origin }\"`);\n    }\n    if (!onRequestContextHandle) {\n      throw new Error('onRequestContextHandle is not set');\n    }\n    this.name = name;\n    this.origin = origin;\n    this.tactic = tactic;\n    this.varName = origin === 'constants' ? `constants.${name}` : name;\n    this.kernel = kernel;\n    this.strictIntegers = strictIntegers;\n    this.type = value.type || type;\n    this.size = value.size || null;\n    this.index = null;\n    this.context = context;\n    this.checkContext = checkContext !== null && checkContext !== undefined ? checkContext : true;\n    this.contextHandle = null;\n    this.onRequestContextHandle = onRequestContextHandle;\n    this.onUpdateValueMismatch = onUpdateValueMismatch;\n    this.forceUploadEachRun = null;\n  }\n\n  get id() {\n    return `${this.origin}_${name}`;\n  }\n\n  getSource() {\n    throw new Error(`\"getSource\" not defined on ${ this.constructor.name }`);\n  }\n\n  updateValue(value) {\n    throw new Error(`\"updateValue\" not defined on ${ this.constructor.name }`);\n  }\n}\n\nmodule.exports = {\n  KernelValue\n};\n},{}],36:[function(require,module,exports){\nconst { utils } = require('../utils');\nconst { Input } = require('../input');\n\nclass Kernel {\n  static get isSupported() {\n    throw new Error(`\"isSupported\" not implemented on ${ this.name }`);\n  }\n\n  static isContextMatch(context) {\n    throw new Error(`\"isContextMatch\" not implemented on ${ this.name }`);\n  }\n\n  static getFeatures() {\n    throw new Error(`\"getFeatures\" not implemented on ${ this.name }`);\n  }\n\n  static destroyContext(context) {\n    throw new Error(`\"destroyContext\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionArguments() {\n    throw new Error(`\"nativeFunctionArguments\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`\"nativeFunctionReturnType\" called on ${ this.name }`);\n  }\n\n  static combineKernels() {\n    throw new Error(`\"combineKernels\" called on ${ this.name }`);\n  }\n\n  constructor(source, settings) {\n    if (typeof source !== 'object') {\n      if (typeof source !== 'string') {\n        throw new Error('source not a string');\n      }\n      if (!utils.isFunctionString(source)) {\n        throw new Error('source not a function string');\n      }\n    }\n    this.useLegacyEncoder = false;\n    this.fallbackRequested = false;\n    this.onRequestFallback = null;\n\n    this.argumentNames = typeof source === 'string' ? utils.getArgumentNamesFromString(source) : null;\n    this.argumentTypes = null;\n    this.argumentSizes = null;\n    this.argumentBitRatios = null;\n    this.kernelArguments = null;\n    this.kernelConstants = null;\n    this.forceUploadKernelConstants = null;\n\n\n    this.source = source;\n\n    this.output = null;\n\n    this.debug = false;\n\n    this.graphical = false;\n\n    this.loopMaxIterations = 0;\n\n    this.constants = null;\n\n    this.constantTypes = null;\n\n    this.constantBitRatios = null;\n\n    this.dynamicArguments = false;\n\n    this.dynamicOutput = false;\n\n    this.canvas = null;\n\n    this.context = null;\n\n    this.checkContext = null;\n\n    this.gpu = null;\n\n    this.functions = null;\n\n    this.nativeFunctions = null;\n\n    this.injectedNative = null;\n\n    this.subKernels = null;\n\n    this.validate = true;\n\n    this.immutable = false;\n\n    this.pipeline = false;\n\n    this.precision = null;\n\n    this.tactic = null;\n\n    this.plugins = null;\n\n    this.returnType = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.optimizeFloatMemory = null;\n    this.strictIntegers = false;\n    this.fixIntegerDivisionAccuracy = null;\n    this.built = false;\n    this.signature = null;\n  }\n\n  mergeSettings(settings) {\n    for (let p in settings) {\n      if (!settings.hasOwnProperty(p) || !this.hasOwnProperty(p)) continue;\n      switch (p) {\n        case 'output':\n          if (!Array.isArray(settings.output)) {\n            this.setOutput(settings.output); \n            continue;\n          }\n          break;\n        case 'functions':\n          this.functions = [];\n          for (let i = 0; i < settings.functions.length; i++) {\n            this.addFunction(settings.functions[i]);\n          }\n          continue;\n        case 'graphical':\n          if (settings[p] && !settings.hasOwnProperty('precision')) {\n            this.precision = 'unsigned';\n          }\n          this[p] = settings[p];\n          continue;\n        case 'nativeFunctions':\n          if (!settings.nativeFunctions) continue;\n          this.nativeFunctions = [];\n          for (let i = 0; i < settings.nativeFunctions.length; i++) {\n            const s = settings.nativeFunctions[i];\n            const { name, source } = s;\n            this.addNativeFunction(name, source, s);\n          }\n          continue;\n      }\n      this[p] = settings[p];\n    }\n\n    if (!this.canvas) this.canvas = this.initCanvas();\n    if (!this.context) this.context = this.initContext();\n    if (!this.plugins) this.plugins = this.initPlugins(settings);\n  }\n  build() {\n    throw new Error(`\"build\" not defined on ${ this.constructor.name }`);\n  }\n\n  run() {\n    throw new Error(`\"run\" not defined on ${ this.constructor.name }`)\n  }\n\n  initCanvas() {\n    throw new Error(`\"initCanvas\" not defined on ${ this.constructor.name }`);\n  }\n\n  initContext() {\n    throw new Error(`\"initContext\" not defined on ${ this.constructor.name }`);\n  }\n\n  initPlugins(settings) {\n    throw new Error(`\"initPlugins\" not defined on ${ this.constructor.name }`);\n  }\n\n  addFunction(source, settings = {}) {\n    if (source.name && source.source && source.argumentTypes && 'returnType' in source) {\n      this.functions.push(source);\n    } else if ('settings' in source && 'source' in source) {\n      this.functions.push(this.functionToIGPUFunction(source.source, source.settings));\n    } else if (typeof source === 'string' || typeof source === 'function') {\n      this.functions.push(this.functionToIGPUFunction(source, settings));\n    } else {\n      throw new Error(`function not properly defined`);\n    }\n    return this;\n  }\n\n  addNativeFunction(name, source, settings = {}) {\n    const { argumentTypes, argumentNames } = settings.argumentTypes ?\n      splitArgumentTypes(settings.argumentTypes) :\n      this.constructor.nativeFunctionArguments(source) || {};\n    this.nativeFunctions.push({\n      name,\n      source,\n      settings,\n      argumentTypes,\n      argumentNames,\n      returnType: settings.returnType || this.constructor.nativeFunctionReturnType(source)\n    });\n    return this;\n  }\n\n  setupArguments(args) {\n    this.kernelArguments = [];\n    if (!this.argumentTypes) {\n      if (!this.argumentTypes) {\n        this.argumentTypes = [];\n        for (let i = 0; i < args.length; i++) {\n          const argType = utils.getVariableType(args[i], this.strictIntegers);\n          const type = argType === 'Integer' ? 'Number' : argType;\n          this.argumentTypes.push(type);\n          this.kernelArguments.push({\n            type\n          });\n        }\n      }\n    } else {\n      for (let i = 0; i < this.argumentTypes.length; i++) {\n        this.kernelArguments.push({\n          type: this.argumentTypes[i]\n        });\n      }\n    }\n\n    this.argumentSizes = new Array(args.length);\n    this.argumentBitRatios = new Int32Array(args.length);\n\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      this.argumentSizes[i] = arg.constructor === Input ? arg.size : null;\n      this.argumentBitRatios[i] = this.getBitRatio(arg);\n    }\n\n    if (this.argumentNames.length !== args.length) {\n      throw new Error(`arguments are miss-aligned`);\n    }\n  }\n\n  setupConstants() {\n    this.kernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    if (this.constants) {\n      for (let name in this.constants) {\n        if (needsConstantTypes) {\n          const type = utils.getVariableType(this.constants[name], this.strictIntegers);\n          this.constantTypes[name] = type;\n          this.kernelConstants.push({\n            name,\n            type\n          });\n        } else {\n          this.kernelConstants.push({\n            name,\n            type: this.constantTypes[name]\n          });\n        }\n        this.constantBitRatios[name] = this.getBitRatio(this.constants[name]);\n      }\n    }\n  }\n\n  setOptimizeFloatMemory(flag) {\n    this.optimizeFloatMemory = flag;\n    return this;\n  }\n\n  toKernelOutput(output) {\n    if (output.hasOwnProperty('x')) {\n      if (output.hasOwnProperty('y')) {\n        if (output.hasOwnProperty('z')) {\n          return [output.x, output.y, output.z];\n        } else {\n          return [output.x, output.y];\n        }\n      } else {\n        return [output.x];\n      }\n    } else {\n      return output;\n    }\n  }\n\n  setOutput(output) {\n    this.output = this.toKernelOutput(output);\n    return this;\n  }\n\n  setDebug(flag) {\n    this.debug = flag;\n    return this;\n  }\n\n  setGraphical(flag) {\n    this.graphical = flag;\n    this.precision = 'unsigned';\n    return this;\n  }\n\n  setLoopMaxIterations(max) {\n    this.loopMaxIterations = max;\n    return this;\n  }\n\n  setConstants(constants) {\n    this.constants = constants;\n    return this;\n  }\n\n  setConstantTypes(constantTypes) {\n    this.constantTypes = constantTypes;\n    return this;\n  }\n\n  setFunctions(functions) {\n    for (let i = 0; i < functions.length; i++) {\n      this.addFunction(functions[i]);\n    }\n    return this;\n  }\n\n  setNativeFunctions(nativeFunctions) {\n    for (let i = 0; i < nativeFunctions.length; i++) {\n      const settings = nativeFunctions[i];\n      const { name, source } = settings;\n      this.addNativeFunction(name, source, settings);\n    }\n    return this;\n  }\n\n  setInjectedNative(injectedNative) {\n    this.injectedNative = injectedNative;\n    return this;\n  }\n\n  setPipeline(flag) {\n    this.pipeline = flag;\n    return this;\n  }\n\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  setDimensions(flag) {\n    utils.warnDeprecated('method', 'setDimensions', 'setOutput');\n    this.output = flag;\n    return this;\n  }\n\n  setOutputToTexture(flag) {\n    utils.warnDeprecated('method', 'setOutputToTexture', 'setPipeline');\n    this.pipeline = flag;\n    return this;\n  }\n\n  setImmutable(flag) {\n    this.immutable = flag;\n    return this;\n  }\n\n  setCanvas(canvas) {\n    this.canvas = canvas;\n    return this;\n  }\n\n  setStrictIntegers(flag) {\n    this.strictIntegers = flag;\n    return this;\n  }\n\n  setDynamicOutput(flag) {\n    this.dynamicOutput = flag;\n    return this;\n  }\n\n  setHardcodeConstants(flag) {\n    utils.warnDeprecated('method', 'setHardcodeConstants');\n    this.setDynamicOutput(flag);\n    this.setDynamicArguments(flag);\n    return this;\n  }\n\n  setDynamicArguments(flag) {\n    this.dynamicArguments = flag;\n    return this;\n  }\n\n  setUseLegacyEncoder(flag) {\n    this.useLegacyEncoder = flag;\n    return this;\n  }\n\n  setWarnVarUsage(flag) {\n    utils.warnDeprecated('method', 'setWarnVarUsage');\n    return this;\n  }\n\n  getCanvas() {\n    utils.warnDeprecated('method', 'getCanvas');\n    return this.canvas;\n  }\n\n  getWebGl() {\n    utils.warnDeprecated('method', 'getWebGl');\n    return this.context;\n  }\n\n  setContext(context) {\n    this.context = context;\n    return this;\n  }\n\n  setArgumentTypes(argumentTypes) {\n    if (Array.isArray(argumentTypes)) {\n      this.argumentTypes = argumentTypes;\n    } else {\n      this.argumentTypes = [];\n      for (const p in argumentTypes) {\n        if (!argumentTypes.hasOwnProperty(p)) continue;\n        const argumentIndex = this.argumentNames.indexOf(p);\n        if (argumentIndex === -1) throw new Error(`unable to find argument ${ p }`);\n        this.argumentTypes[argumentIndex] = argumentTypes[p];\n      }\n    }\n    return this;\n  }\n\n  setTactic(tactic) {\n    this.tactic = tactic;\n    return this;\n  }\n\n  requestFallback(args) {\n    if (!this.onRequestFallback) {\n      throw new Error(`\"onRequestFallback\" not defined on ${ this.constructor.name }`);\n    }\n    this.fallbackRequested = true;\n    return this.onRequestFallback(args);\n  }\n\n  validateSettings() {\n    throw new Error(`\"validateSettings\" not defined on ${ this.constructor.name }`);\n  }\n\n  addSubKernel(subKernel) {\n    if (this.subKernels === null) {\n      this.subKernels = [];\n    }\n    if (!subKernel.source) throw new Error('subKernel missing \"source\" property');\n    if (!subKernel.property && isNaN(subKernel.property)) throw new Error('subKernel missing \"property\" property');\n    if (!subKernel.name) throw new Error('subKernel missing \"name\" property');\n    this.subKernels.push(subKernel);\n    return this;\n  }\n\n  destroy(removeCanvasReferences) {\n    throw new Error(`\"destroy\" called on ${ this.constructor.name }`);\n  }\n\n  getBitRatio(value) {\n    if (this.precision === 'single') {\n      return 4;\n    } else if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  getPixels(flip) {\n    throw new Error(`\"getPixels\" called on ${ this.constructor.name }`);\n  }\n\n  checkOutput() {\n    if (!this.output || !utils.isArray(this.output)) throw new Error('kernel.output not an array');\n    if (this.output.length < 1) throw new Error('kernel.output is empty, needs at least 1 value');\n    for (let i = 0; i < this.output.length; i++) {\n      if (isNaN(this.output[i]) || this.output[i] < 1) {\n        throw new Error(`${ this.constructor.name }.output[${ i }] incorrectly defined as \\`${ this.output[i] }\\`, needs to be numeric, and greater than 0`);\n      }\n    }\n  }\n\n  prependString(value) {\n    throw new Error(`\"prependString\" called on ${ this.constructor.name }`);\n  }\n\n  hasPrependString(value) {\n    throw new Error(`\"hasPrependString\" called on ${ this.constructor.name }`);\n  }\n\n  toJSON() {\n    return {\n      settings: {\n        output: this.output,\n        pipeline: this.pipeline,\n        argumentNames: this.argumentNames,\n        argumentsTypes: this.argumentTypes,\n        constants: this.constants,\n        pluginNames: this.plugins ? this.plugins.map(plugin => plugin.name) : null,\n        returnType: this.returnType,\n      }\n    };\n  }\n\n  buildSignature(args) {\n    const Constructor = this.constructor;\n    this.signature = Constructor.getSignature(this, Constructor.getArgumentTypes(this, args));\n  }\n\n  static getArgumentTypes(kernel, args) {\n    const argumentTypes = new Array(args.length);\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      const type = kernel.argumentTypes[i];\n      if (arg.type) {\n        argumentTypes[i] = arg.type;\n      } else {\n        switch (type) {\n          case 'Number':\n          case 'Integer':\n          case 'Float':\n          case 'ArrayTexture(1)':\n            argumentTypes[i] = utils.getVariableType(arg);\n            break;\n          default:\n            argumentTypes[i] = type;\n        }\n      }\n    }\n    return argumentTypes;\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    throw new Error(`\"getSignature\" not implemented on ${ this.name }`);\n  }\n\n  functionToIGPUFunction(source, settings = {}) {\n    if (typeof source !== 'string' && typeof source !== 'function') throw new Error('source not a string or function');\n    const sourceString = typeof source === 'string' ? source : source.toString();\n    let argumentTypes = [];\n\n    if (Array.isArray(settings.argumentTypes)) {\n      argumentTypes = settings.argumentTypes;\n    } else if (typeof settings.argumentTypes === 'object') {\n      argumentTypes = utils.getArgumentNamesFromString(sourceString)\n        .map(name => settings.argumentTypes[name]) || [];\n    } else {\n      argumentTypes = settings.argumentTypes || [];\n    }\n\n    return {\n      name: utils.getFunctionNameFromString(sourceString) || null,\n      source: sourceString,\n      argumentTypes,\n      returnType: settings.returnType || null,\n    };\n  }\n\n  onActivate(previousKernel) {}\n}\n\nfunction splitArgumentTypes(argumentTypesObject) {\n  const argumentNames = Object.keys(argumentTypesObject);\n  const argumentTypes = [];\n  for (let i = 0; i < argumentNames.length; i++) {\n    const argumentName = argumentNames[i];\n    argumentTypes.push(argumentTypesObject[argumentName]);\n  }\n  return { argumentTypes, argumentNames };\n}\n\nmodule.exports = {\n  Kernel\n};\n},{\"../input\":110,\"../utils\":114}],37:[function(require,module,exports){\nconst fragmentShader = `__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nvarying vec2 vTexCoord;\n\nfloat acosh(float x) {\n  return log(x + sqrt(x * x - 1.0));\n}\n\nfloat sinh(float x) {\n  return (pow(${Math.E}, x) - pow(${Math.E}, -x)) / 2.0;\n}\n\nfloat asinh(float x) {\n  return log(x + sqrt(x * x + 1.0));\n}\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat atanh(float x) {\n  x = (x + 1.0) / (x - 1.0);\n  if (x < 0.0) {\n    return 0.5 * log(-x);\n  }\n  return 0.5 * log(x);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat cosh(float x) {\n  return (pow(${Math.E}, x) + pow(${Math.E}, -x)) / 2.0; \n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat tanh(float x) {\n  float e = exp(2.0 * x);\n  return (e - 1.0) / (e + 1.0);\n}\n\nfloat trunc(float x) {\n  if (x >= 0.0) {\n    return floor(x); \n  } else {\n    return ceil(x);\n  }\n}\n\nvec4 _round(vec4 x) {\n  return floor(x + 0.5);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x / y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(_round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(_round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  if (channel == 0) return texel.r * 255.0 + texel.g * 65280.0;\n  if (channel == 1) return texel.b * 255.0 + texel.a * 65280.0;\n  return 0.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  if (channel == 0) return texel.r * 255.0;\n  if (channel == 1) return texel.g * 255.0;\n  if (channel == 2) return texel.b * 255.0;\n  if (channel == 3) return texel.a * 255.0;\n  return 0.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return texel.r;\n  if (channel == 1) return texel.g;\n  if (channel == 2) return texel.b;\n  if (channel == 3) return texel.a;\n  return 0.0;\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture2D(tex, st / vec2(texSize));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture2D(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n  \n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture2D(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nvoid color(sampler2D image) {\n  actualColor = texture2D(image, vTexCoord);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};\n},{}],38:[function(require,module,exports){\nconst { utils } = require('../../utils');\nconst { FunctionNode } = require('../function-node');\n\nclass WebGLFunctionNode extends FunctionNode {\n  constructor(source, settings) {\n    super(source, settings);\n    if (settings && settings.hasOwnProperty('fixIntegerDivisionAccuracy')) {\n      this.fixIntegerDivisionAccuracy = settings.fixIntegerDivisionAccuracy;\n    }\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    const consequentType = this.getType(ast.consequent);\n    const alternateType = this.getType(ast.alternate);\n    if (consequentType === null && alternateType === null) {\n      retArr.push('if (');\n      this.astGeneric(ast.test, retArr);\n      retArr.push(') {');\n      this.astGeneric(ast.consequent, retArr);\n      retArr.push(';');\n      retArr.push('} else {');\n      this.astGeneric(ast.alternate, retArr);\n      retArr.push(';');\n      retArr.push('}');\n      return retArr;\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  astFunction(ast, retArr) {\n    if (this.isRootKernel) {\n      retArr.push('void');\n    } else {\n      if (!this.returnType) {\n        const lastReturn = this.findLastReturn();\n        if (lastReturn) {\n          this.returnType = this.getType(ast.body);\n          if (this.returnType === 'LiteralInteger') {\n            this.returnType = 'Number';\n          }\n        }\n      }\n\n      const { returnType } = this;\n      if (!returnType) {\n        retArr.push('void');\n      } else {\n        const type = typeMap[returnType];\n        if (!type) {\n          throw new Error(`unknown type ${returnType}`);\n        }\n        retArr.push(type);\n      }\n    }\n    retArr.push(' ');\n    retArr.push(this.name);\n    retArr.push('(');\n\n    if (!this.isRootKernel) {\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        let argumentType = this.argumentTypes[this.argumentNames.indexOf(argumentName)];\n        if (!argumentType) {\n          throw this.astErrorOutput(`Unknown argument ${argumentName} type`, ast);\n        }\n        if (argumentType === 'LiteralInteger') {\n          this.argumentTypes[i] = argumentType = 'Number';\n        }\n        const type = typeMap[argumentType];\n        if (!type) {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        const name = utils.sanitizeName(argumentName);\n        if (type === 'sampler2D' || type === 'sampler2DArray') {\n          retArr.push(`${type} user_${name},ivec2 user_${name}Size,ivec3 user_${name}Dim`);\n        } else {\n          retArr.push(`${type} user_${name}`);\n        }\n      }\n    }\n\n    retArr.push(') {\\n');\n\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    retArr.push('}\\n');\n    return retArr;\n  }\n\n  astReturnStatement(ast, retArr) {\n    if (!ast.argument) throw this.astErrorOutput('Unexpected return statement', ast);\n    this.pushState('skip-literal-correction');\n    const type = this.getType(ast.argument);\n    this.popState('skip-literal-correction');\n\n    const result = [];\n\n    if (!this.returnType) {\n      if (type === 'LiteralInteger' || type === 'Integer') {\n        this.returnType = 'Number';\n      } else {\n        this.returnType = type;\n      }\n    }\n\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Float':\n        switch (type) {\n          case 'Integer':\n            result.push('float(');\n            this.astGeneric(ast.argument, result);\n            result.push(')');\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToFloat(ast.argument, result);\n\n            if (this.getType(ast) === 'Integer') {\n              result.unshift('float(');\n              result.push(')');\n            }\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Integer':\n        switch (type) {\n          case 'Float':\n          case 'Number':\n            this.castValueToInteger(ast.argument, result);\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToInteger(ast.argument, result);\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Array(4)':\n      case 'Array(3)':\n      case 'Array(2)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'Input':\n        this.astGeneric(ast.argument, result);\n        break;\n      default:\n        throw this.astErrorOutput(`unhandled return type ${this.returnType}`, ast);\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(`kernelResult = ${ result.join('') };`);\n      retArr.push('return;');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = ${ result.join('') };`);\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push(`return ${ result.join('') };`);\n    }\n    return retArr;\n  }\n\n  astLiteral(ast, retArr) {\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    const key = this.astKey(ast);\n    if (Number.isInteger(ast.value)) {\n      if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n        this.literalTypes[key] = 'Integer';\n        retArr.push(`${ast.value}`);\n      } else if (this.isState('casting-to-float') || this.isState('building-float')) {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      } else {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      }\n    } else if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n      this.literalTypes[key] = 'Integer';\n      retArr.push(Math.round(ast.value));\n    } else {\n      this.literalTypes[key] = 'Number';\n      retArr.push(`${ast.value}`);\n    }\n    return retArr;\n  }\n\n  astBinaryExpression(ast, retArr) {\n    if (this.checkAndUpconvertOperator(ast, retArr)) {\n      return retArr;\n    }\n\n    if (this.fixIntegerDivisionAccuracy && ast.operator === '/') {\n      retArr.push('divWithIntCheck(');\n      this.pushState('building-float');\n      switch (this.getType(ast.left)) {\n        case 'Integer':\n          this.castValueToFloat(ast.left, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.left, retArr);\n          break;\n        default:\n          this.astGeneric(ast.left, retArr);\n      }\n      retArr.push(', ');\n      switch (this.getType(ast.right)) {\n        case 'Integer':\n          this.castValueToFloat(ast.right, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.right, retArr);\n          break;\n        default:\n          this.astGeneric(ast.right, retArr);\n      }\n      this.popState('building-float');\n      retArr.push(')');\n      return retArr;\n    }\n\n    retArr.push('(');\n    const leftType = this.getType(ast.left) || 'Number';\n    const rightType = this.getType(ast.right) || 'Number';\n    if (!leftType || !rightType) {\n      throw this.astErrorOutput(`Unhandled binary expression`, ast);\n    }\n    const key = leftType + ' & ' + rightType;\n    switch (key) {\n      case 'Integer & Integer':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n      case 'Number & Float':\n      case 'Float & Number':\n      case 'Float & Float':\n      case 'Number & Number':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & LiteralInteger':\n        if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n          this.pushState('building-integer');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.astGeneric(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.castLiteralToFloat(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castLiteralToFloat(ast.right, retArr);\n          this.popState('building-float');\n        }\n        break;\n\n      case 'Integer & Float':\n      case 'Integer & Number':\n        if (ast.operator === '>' || ast.operator === '<' && ast.right.type === 'Literal') {\n          if (!Number.isInteger(ast.right.value)) {\n            this.pushState('building-float');\n            this.castValueToFloat(ast.left, retArr);\n            retArr.push(operatorMap[ast.operator] || ast.operator);\n            this.astGeneric(ast.right, retArr);\n            this.popState('building-float');\n            break;\n          }\n        }\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.pushState('casting-to-integer');\n        if (ast.right.type === 'Literal') {\n          const literalResult = [];\n          this.astGeneric(ast.right, literalResult);\n          const literalType = this.getType(ast.right);\n          if (literalType === 'Integer') {\n            retArr.push(literalResult.join(''));\n          } else {\n            throw this.astErrorOutput(`Unhandled binary expression with literal`, ast);\n          }\n        } else {\n          retArr.push('int(');\n          this.astGeneric(ast.right, retArr);\n          retArr.push(')');\n        }\n        this.popState('casting-to-integer');\n        this.popState('building-integer');\n        break;\n      case 'Integer & LiteralInteger':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToInteger(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Number & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'Float & LiteralInteger':\n      case 'Number & LiteralInteger':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & Float':\n      case 'LiteralInteger & Number':\n        if (this.isState('casting-to-integer')) {\n          this.pushState('building-integer');\n          this.castLiteralToInteger(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castValueToInteger(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.pushState('casting-to-float');\n          this.astGeneric(ast.right, retArr);\n          this.popState('casting-to-float');\n          this.popState('building-float');\n        }\n        break;\n      case 'LiteralInteger & Integer':\n        this.pushState('building-integer');\n        this.castLiteralToInteger(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Boolean & Boolean':\n        this.pushState('building-boolean');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-boolean');\n        break;\n\n      case 'Float & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n\n      default:\n        throw this.astErrorOutput(`Unhandled binary expression between ${key}`, ast);\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  checkAndUpconvertOperator(ast, retArr) {\n    const bitwiseResult = this.checkAndUpconvertBitwiseOperators(ast, retArr);\n    if (bitwiseResult) {\n      return bitwiseResult;\n    }\n    const upconvertableOperators = {\n      '%': this.fixIntegerDivisionAccuracy ? 'integerCorrectionModulo' : 'modulo',\n      '**': 'pow',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.left)) {\n      case 'Integer':\n        this.castValueToFloat(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    switch (this.getType(ast.right)) {\n      case 'Integer':\n        this.castValueToFloat(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseOperators(ast, retArr) {\n    const upconvertableOperators = {\n      '&': 'bitwiseAnd',\n      '|': 'bitwiseOr',\n      '^': 'bitwiseXOR',\n      '<<': 'bitwiseZeroFillLeftShift',\n      '>>': 'bitwiseSignedRightShift',\n      '>>>': 'bitwiseZeroFillRightShift',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    const leftType = this.getType(ast.left);\n    switch (leftType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    const rightType = this.getType(ast.right);\n    switch (rightType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(ast, retArr) {\n    const upconvertableOperators = {\n      '~': 'bitwiseNot',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.argument)) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.argument, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.argument, retArr);\n        break;\n      default:\n        this.astGeneric(ast.argument, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  castLiteralToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  castLiteralToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  castValueToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    retArr.push('int(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  castValueToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    retArr.push('float(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput('IdentifierExpression - not an Identifier', idtNode);\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      retArr.push('3.402823466e+38');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      const { declarations } = forNode.init;\n      if (declarations.length > 1) {\n        isSafe = false;\n      }\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < declarations.length; i++) {\n        if (declarations[i].init && declarations[i].init.type !== 'Literal') {\n          isSafe = false;\n        }\n      }\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      const initString = initArr.join('');\n      const initNeedsSemiColon = initString[initString.length - 1] !== ';';\n      retArr.push(`for (${initString}${initNeedsSemiColon ? ';' : ''}${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), '\\n');\n      }\n      retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', whileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    retArr.push('if (!');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') break;\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', doWhileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') break;\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n\n  astAssignmentExpression(assNode, retArr) {\n    if (assNode.operator === '%=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('mod(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else if (assNode.operator === '**=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('pow(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else {\n      const leftType = this.getType(assNode.left);\n      const rightType = this.getType(assNode.right);\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(assNode.operator);\n      if (leftType !== 'Integer' && rightType === 'Integer') {\n        retArr.push('float(');\n        this.astGeneric(assNode.right, retArr);\n        retArr.push(')');\n      } else {\n        this.astGeneric(assNode.right, retArr);\n      }\n      return retArr;\n    }\n  }\n\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); \n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  astVariableDeclaration(varDecNode, retArr) {\n    const declarations = varDecNode.declarations;\n    if (!declarations || !declarations[0] || !declarations[0].init) {\n      throw this.astErrorOutput('Unexpected expression', varDecNode);\n    }\n    const result = [];\n    let lastType = null;\n    const declarationSets = [];\n    let declarationSet = [];\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const init = declaration.init;\n      const info = this.getDeclaration(declaration.id);\n      const actualType = this.getType(declaration.init);\n      let type = actualType;\n      if (type === 'LiteralInteger') {\n        if (info.suggestedType === 'Integer') {\n          type = 'Integer';\n        } else {\n          type = 'Number';\n        }\n      }\n      const markupType = typeMap[type];\n      if (!markupType) {\n        throw this.astErrorOutput(`Markup type ${ type } not handled`, varDecNode);\n      }\n      const declarationResult = [];\n      if (actualType === 'Integer' && type === 'Integer') {\n        info.valueType = 'Number';\n        if (i === 0 || lastType === null) {\n          declarationResult.push('float ');\n        } else if (type !== lastType) {\n          throw new Error('Unhandled declaration');\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        declarationResult.push('float(');\n        this.astGeneric(init, declarationResult);\n        declarationResult.push(')');\n      } else {\n        info.valueType = type;\n        if (i === 0 || lastType === null) {\n          declarationResult.push(`${markupType} `);\n        } else if (type !== lastType) {\n          declarationSets.push(declarationSet.join(','));\n          declarationSet = [];\n          declarationResult.push(`${markupType} `);\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        if (actualType === 'Number' && type === 'Integer') {\n          if (init.left && init.left.type === 'Literal') {\n            this.astGeneric(init, declarationResult);\n          } else {\n            declarationResult.push('int(');\n            this.astGeneric(init, declarationResult);\n            declarationResult.push(')');\n          }\n        } else if (actualType === 'LiteralInteger' && type === 'Integer') {\n          this.castLiteralToInteger(init, declarationResult);\n        } else {\n          this.astGeneric(init, declarationResult);\n        }\n      }\n      declarationSet.push(declarationResult.join(''));\n    }\n\n    if (declarationSet.length > 0) {\n      declarationSets.push(declarationSet.join(','));\n    }\n\n    result.push(declarationSets.join(';'));\n\n    retArr.push(result.join(''));\n    retArr.push(';');\n    return retArr;\n  }\n\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n  }\n\n  astSwitchStatement(ast, retArr) {\n    if (ast.type !== 'SwitchStatement') {\n      throw this.astErrorOutput('Invalid switch statement', ast);\n    }\n    const { discriminant, cases } = ast;\n    const type = this.getType(discriminant);\n    const varName = `switchDiscriminant${this.astKey(ast, '_')}`;\n    switch (type) {\n      case 'Float':\n      case 'Number':\n        retArr.push(`float ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n      case 'Integer':\n        retArr.push(`int ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n    }\n    if (cases.length === 1 && !cases[0].test) {\n      this.astGeneric(cases[0].consequent, retArr);\n      return retArr;\n    }\n\n    let fallingThrough = false;\n    let defaultResult = [];\n    let movingDefaultToEnd = false;\n    let pastFirstIf = false;\n    for (let i = 0; i < cases.length; i++) {\n      if (!cases[i].test) {\n        if (cases.length > i + 1) {\n          movingDefaultToEnd = true;\n          this.astGeneric(cases[i].consequent, defaultResult);\n          continue;\n        } else {\n          retArr.push(' else {\\n');\n        }\n      } else {\n        if (i === 0 || !pastFirstIf) {\n          pastFirstIf = true;\n          retArr.push(`if (${varName} == `);\n        } else {\n          if (fallingThrough) {\n            retArr.push(`${varName} == `);\n            fallingThrough = false;\n          } else {\n            retArr.push(` else if (${varName} == `);\n          }\n        }\n        if (type === 'Integer') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'Number':\n            case 'Float':\n              this.castValueToInteger(cases[i].test, retArr);\n              break;\n            case 'LiteralInteger':\n              this.castLiteralToInteger(cases[i].test, retArr);\n              break;\n          }\n        } else if (type === 'Float') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'LiteralInteger':\n              this.castLiteralToFloat(cases[i].test, retArr);\n              break;\n            case 'Integer':\n              this.castValueToFloat(cases[i].test, retArr);\n              break;\n          }\n        } else {\n          throw new Error('unhanlded');\n        }\n        if (!cases[i].consequent || cases[i].consequent.length === 0) {\n          fallingThrough = true;\n          retArr.push(' || ');\n          continue;\n        }\n        retArr.push(`) {\\n`);\n      }\n      this.astGeneric(cases[i].consequent, retArr);\n      retArr.push('\\n}');\n    }\n    if (movingDefaultToEnd) {\n      retArr.push(' else {');\n      retArr.push(defaultResult.join(''));\n      retArr.push('}');\n    }\n    return retArr;\n  }\n\n  astThisExpression(tNode, retArr) {\n    retArr.push('this');\n    return retArr;\n  }\n\n  astMemberExpression(mNode, retArr) {\n    const {\n      property,\n      name,\n      signature,\n      origin,\n      type,\n      xProperty,\n      yProperty,\n      zProperty\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'value.thread.value':\n      case 'this.thread.value':\n        if (name !== 'x' && name !== 'y' && name !== 'z') {\n          throw this.astErrorOutput('Unexpected expression, expected `this.thread.x`, `this.thread.y`, or `this.thread.z`', mNode);\n        }\n        retArr.push(`threadId.${name}`);\n        return retArr;\n      case 'this.output.value':\n        if (this.dynamicOutput) {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.x)');\n              } else {\n                retArr.push('uOutputDim.x');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.y)');\n              } else {\n                retArr.push('uOutputDim.y');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.z)');\n              } else {\n                retArr.push('uOutputDim.z');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        } else {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[0]);\n              } else {\n                retArr.push(this.output[0], '.0');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[1]);\n              } else {\n                retArr.push(this.output[1], '.0');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[2]);\n              } else {\n                retArr.push(this.output[2], '.0');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value[][][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        const cleanName = utils.sanitizeName(name);\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ cleanName }.r`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ cleanName }.g`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ cleanName }.b`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ cleanName }.a`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n        if (typeof xProperty === 'undefined') {\n          switch (type) {\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              retArr.push(`constants_${ utils.sanitizeName(name) }`);\n              return retArr;\n          }\n        }\n        case 'this.constants.value[]':\n        case 'this.constants.value[][]':\n        case 'this.constants.value[][][]':\n        case 'this.constants.value[][][][]':\n          break;\n        case 'fn()[]':\n          this.astCallExpression(mNode.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(property));\n          retArr.push(']');\n          return retArr;\n        case 'fn()[][]':\n          this.astCallExpression(mNode.object.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(mNode.object.property));\n          retArr.push(']');\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(mNode.property));\n          retArr.push(']');\n          return retArr;\n        case '[][]':\n          this.astArrayExpression(mNode.object, retArr);\n          retArr.push('[');\n          retArr.push(this.memberExpressionPropertyMarkup(property));\n          retArr.push(']');\n          return retArr;\n        default:\n          throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (mNode.computed === false) {\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${utils.sanitizeName(name)}`);\n          return retArr;\n      }\n    }\n\n    const markupName = `${origin}_${utils.sanitizeName(name)}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(xProperty));\n        retArr.push(']');\n        break;\n      case 'HTMLImageArray':\n        retArr.push(`getImage3D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(1)':\n        retArr.push(`getFloatFromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(2)':\n      case 'Array2D(2)':\n      case 'Array3D(2)':\n        retArr.push(`getMemoryOptimizedVec2(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(2)':\n        retArr.push(`getVec2FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(3)':\n      case 'Array2D(3)':\n      case 'Array3D(3)':\n        retArr.push(`getMemoryOptimizedVec3(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(3)':\n        retArr.push(`getVec3FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(4)':\n      case 'Array2D(4)':\n      case 'Array3D(4)':\n        retArr.push(`getMemoryOptimizedVec4(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(4)':\n      case 'HTMLCanvas':\n      case 'OffscreenCanvas':\n      case 'HTMLImage':\n      case 'ImageBitmap':\n      case 'ImageData':\n      case 'HTMLVideo':\n        retArr.push(`getVec4FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'NumberTexture':\n      case 'Array':\n      case 'Array2D':\n      case 'Array3D':\n      case 'Array4D':\n      case 'Input':\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        if (this.precision === 'single') {\n          retArr.push(`getMemoryOptimized32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        } else {\n          const bitRatio = (origin === 'user' ?\n            this.lookupFunctionArgumentBitRatio(this.name, name) :\n            this.constantBitRatios[name]\n          );\n          switch (bitRatio) {\n            case 1:\n              retArr.push(`get8(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 2:\n              retArr.push(`get16(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 4:\n            case 0:\n              retArr.push(`get32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            default:\n              throw new Error(`unhandled bit ratio of ${bitRatio}`);\n          }\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        }\n        break;\n      case 'MemoryOptimizedNumberTexture':\n        retArr.push(`getMemoryOptimized32(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`${markupName}[${this.memberExpressionPropertyMarkup(yProperty)}]`);\n        if (yProperty) {\n          retArr.push(`[${this.memberExpressionPropertyMarkup(xProperty)}]`);\n        }\n        break;\n      default:\n        throw new Error(`unhandled member expression \"${ type }\"`);\n    }\n    return retArr;\n  }\n\n  astCallExpression(ast, retArr) {\n    if (!ast.callee) {\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n\n    let functionName = null;\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    if (isMathFunction || (ast.callee.object && ast.callee.object.type === 'ThisExpression')) {\n      functionName = ast.callee.property.name;\n    }\n    else if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[0].type === 'Literal' && !isNaN(ast.callee.expressions[0].raw)) {\n      functionName = ast.callee.expressions[1].property.name;\n    } else {\n      functionName = ast.callee.name;\n    }\n\n    if (!functionName) {\n      throw this.astErrorOutput(`Unhandled function, couldn't find name`, ast);\n    }\n\n    switch (functionName) {\n      case 'pow':\n        functionName = '_pow';\n        break;\n      case 'round':\n        functionName = '_round';\n        break;\n    }\n\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    if (functionName === 'random' && this.plugins && this.plugins.length > 0) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.functionMatch === 'Math.random()' && plugin.functionReplace) {\n          retArr.push(plugin.functionReplace);\n          return retArr;\n        }\n      }\n    }\n\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    retArr.push(functionName);\n\n    retArr.push('(');\n\n    if (isMathFunction) {\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        const argumentType = this.getType(argument);\n        if (i > 0) {\n          retArr.push(', ');\n        }\n\n        switch (argumentType) {\n          case 'Integer':\n            this.castValueToFloat(argument, retArr);\n            break;\n          default:\n            this.astGeneric(argument, retArr);\n            break;\n        }\n      }\n    } else {\n      const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        let targetType = targetTypes[i];\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        const argumentType = this.getType(argument);\n        if (!targetType) {\n          this.triggerImplyArgumentType(functionName, i, argumentType, this);\n          targetType = argumentType;\n        }\n        switch (argumentType) {\n          case 'Boolean':\n            this.astGeneric(argument, retArr);\n            continue;\n          case 'Number':\n          case 'Float':\n            if (targetType === 'Integer') {\n              retArr.push('int(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.astGeneric(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Integer':\n            if (targetType === 'Number' || targetType === 'Float') {\n              retArr.push('float(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Integer') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'LiteralInteger':\n            if (targetType === 'Integer') {\n              this.castLiteralToInteger(argument, retArr);\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            if (targetType === argumentType) {\n              if (argument.type === 'Identifier') {\n                retArr.push(`user_${utils.sanitizeName(argument.name)}`);\n              } else if (argument.type === 'ArrayExpression' || argument.type === 'MemberExpression' || argument.type === 'CallExpression') {\n                this.astGeneric(argument, retArr);\n              } else {\n                throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              }\n              continue;\n            }\n            break;\n          case 'HTMLCanvas':\n          case 'OffscreenCanvas':\n          case 'HTMLImage':\n          case 'ImageBitmap':\n          case 'ImageData':\n          case 'HTMLImageArray':\n          case 'HTMLVideo':\n          case 'ArrayTexture(1)':\n          case 'ArrayTexture(2)':\n          case 'ArrayTexture(3)':\n          case 'ArrayTexture(4)':\n          case 'Array':\n          case 'Input':\n            if (targetType === argumentType) {\n              if (argument.type !== 'Identifier') throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              this.triggerImplyArgumentBitRatio(this.name, argument.name, functionName, i);\n              const name = utils.sanitizeName(argument.name);\n              retArr.push(`user_${name},user_${name}Size,user_${name}Dim`);\n              continue;\n            }\n            break;\n        }\n        throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named \"${ argument.name }\"`, ast);\n      }\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n\n    const arrLen = arrNode.elements.length;\n\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`mat${arrLen}(`);\n        break;\n      default:\n        retArr.push(`vec${arrLen}(`);\n    }\n    for (let i = 0; i < arrLen; ++i) {\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      const subNode = arrNode.elements[i];\n      this.astGeneric(subNode, retArr)\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  memberExpressionXYZ(x, y, z, retArr) {\n    if (z) {\n      retArr.push(this.memberExpressionPropertyMarkup(z), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    if (y) {\n      retArr.push(this.memberExpressionPropertyMarkup(y), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    retArr.push(this.memberExpressionPropertyMarkup(x));\n    return retArr;\n  }\n\n  memberExpressionPropertyMarkup(property) {\n    if (!property) {\n      throw new Error('Property not set');\n    }\n    const type = this.getType(property);\n    const result = [];\n    switch (type) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(property, result);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(property, result);\n        break;\n      default:\n        this.astGeneric(property, result);\n    }\n    return result.join('');\n  }\n}\n\nconst typeMap = {\n  'Array': 'sampler2D',\n  'Array(2)': 'vec2',\n  'Array(3)': 'vec3',\n  'Array(4)': 'vec4',\n  'Matrix(2)': 'mat2',\n  'Matrix(3)': 'mat3',\n  'Matrix(4)': 'mat4',\n  'Array2D': 'sampler2D',\n  'Array3D': 'sampler2D',\n  'Boolean': 'bool',\n  'Float': 'float',\n  'Input': 'sampler2D',\n  'Integer': 'int',\n  'Number': 'float',\n  'LiteralInteger': 'float',\n  'NumberTexture': 'sampler2D',\n  'MemoryOptimizedNumberTexture': 'sampler2D',\n  'ArrayTexture(1)': 'sampler2D',\n  'ArrayTexture(2)': 'sampler2D',\n  'ArrayTexture(3)': 'sampler2D',\n  'ArrayTexture(4)': 'sampler2D',\n  'HTMLVideo': 'sampler2D',\n  'HTMLCanvas': 'sampler2D',\n  'OffscreenCanvas': 'sampler2D',\n  'HTMLImage': 'sampler2D',\n  'ImageBitmap': 'sampler2D',\n  'ImageData': 'sampler2D',\n  'HTMLImageArray': 'sampler2DArray',\n};\n\nconst operatorMap = {\n  '===': '==',\n  '!==': '!='\n};\n\nmodule.exports = {\n  WebGLFunctionNode\n};\n},{\"../../utils\":114,\"../function-node\":10}],39:[function(require,module,exports){\nconst { WebGLKernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGLKernelValueFloat } = require('./kernel-value/float');\nconst { WebGLKernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGLKernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGLKernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGLKernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGLKernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGLKernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGLKernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGLKernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGLKernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGLKernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGLKernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGLKernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGLKernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGLKernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGLKernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGLKernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGLKernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGLKernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGLKernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGLKernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGLKernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGLKernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueUnsignedInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueDynamicSingleArray3DI,\n      'Input': WebGLKernelValueDynamicSingleInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueSingleArray3DI,\n      'Input': WebGLKernelValueSingleInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  lookupKernelValueType,\n  kernelValueMaps,\n};\n},{\"./kernel-value/array2\":41,\"./kernel-value/array3\":42,\"./kernel-value/array4\":43,\"./kernel-value/boolean\":44,\"./kernel-value/dynamic-html-image\":45,\"./kernel-value/dynamic-html-video\":46,\"./kernel-value/dynamic-memory-optimized-number-texture\":47,\"./kernel-value/dynamic-number-texture\":48,\"./kernel-value/dynamic-single-array\":49,\"./kernel-value/dynamic-single-array1d-i\":50,\"./kernel-value/dynamic-single-array2d-i\":51,\"./kernel-value/dynamic-single-array3d-i\":52,\"./kernel-value/dynamic-single-input\":53,\"./kernel-value/dynamic-unsigned-array\":54,\"./kernel-value/dynamic-unsigned-input\":55,\"./kernel-value/float\":56,\"./kernel-value/html-image\":57,\"./kernel-value/html-video\":58,\"./kernel-value/integer\":60,\"./kernel-value/memory-optimized-number-texture\":61,\"./kernel-value/number-texture\":62,\"./kernel-value/single-array\":63,\"./kernel-value/single-array1d-i\":64,\"./kernel-value/single-array2d-i\":65,\"./kernel-value/single-array3d-i\":66,\"./kernel-value/single-input\":67,\"./kernel-value/unsigned-array\":68,\"./kernel-value/unsigned-input\":69}],40:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\nconst { Input } = require('../../../input');\n\nclass WebGLKernelArray extends WebGLKernelValue {\n  checkSize(width, height) {\n    if (!this.kernel.validate) return;\n    const { maxTextureSize } = this.kernel.constructor.features;\n    if (width > maxTextureSize || height > maxTextureSize) {\n      if (width > height) {\n        throw new Error(`Argument texture width of ${width} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else if (width < height) {\n        throw new Error(`Argument texture height of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else {\n        throw new Error(`Argument texture height and width of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      }\n    }\n  }\n\n  setup() {\n    this.requestTexture();\n    this.setupTexture();\n    this.defineTexture();\n  }\n\n  requestTexture() {\n    this.texture = this.onRequestTexture();\n  }\n\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n  }\n\n  setupTexture() {\n    this.contextHandle = this.onRequestContextHandle();\n    this.index = this.onRequestIndex();\n    this.dimensionsId = this.id + 'Dim';\n    this.sizeId = this.id + 'Size';\n  }\n\n  getBitRatio(value) {\n    if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  destroy() {\n    if (this.prevArg) {\n      this.prevArg.delete();\n    }\n    this.context.deleteTexture(this.texture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelArray\n};\n},{\"../../../input\":110,\"./index\":59}],41:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray2 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec2 ${this.id} = vec2(${value[0]},${value[1]});\\n`;\n    }\n    return `uniform vec2 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform2fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray2\n};\n},{\"./index\":59}],42:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray3 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec3 ${this.id} = vec3(${value[0]},${value[1]},${value[2]});\\n`;\n    }\n    return `uniform vec3 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform3fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray3\n};\n},{\"./index\":59}],43:[function(require,module,exports){\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray4 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec4 ${this.id} = vec4(${value[0]},${value[1]},${value[2]},${value[3]});\\n`;\n    }\n    return `uniform vec4 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform4fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray4\n};\n},{\"./index\":59}],44:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueBoolean extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const bool ${this.id} = ${value};\\n`;\n    }\n    return `uniform bool ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueBoolean\n};\n},{\"../../../utils\":114,\"./index\":59}],45:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueDynamicHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLImage\n};\n},{\"../../../utils\":114,\"./html-image\":57}],46:[function(require,module,exports){\nconst { WebGLKernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGLKernelValueDynamicHTMLVideo extends WebGLKernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLVideo\n};\n},{\"./dynamic-html-image\":45}],47:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    this.dimensions = inputTexture.dimensions;\n    this.checkSize(inputTexture.size[0], inputTexture.size[1]);\n    this.textureSize = inputTexture.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(inputTexture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":114,\"./memory-optimized-number-texture\":61}],48:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('./number-texture');\n\nclass WebGLKernelValueDynamicNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = value.dimensions;\n    this.checkSize(value.size[0], value.size[1]);\n    this.textureSize = value.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicNumberTexture\n};\n},{\"../../../utils\":114,\"./number-texture\":62}],49:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('./single-array');\n\nclass WebGLKernelValueDynamicSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray\n};\n},{\"../../../utils\":114,\"./single-array\":63}],50:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('./single-array1d-i');\n\nclass WebGLKernelValueDynamicSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray1DI\n};\n},{\"../../../utils\":114,\"./single-array1d-i\":64}],51:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('./single-array2d-i');\n\nclass WebGLKernelValueDynamicSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray2DI\n};\n},{\"../../../utils\":114,\"./single-array2d-i\":65}],52:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('./single-array3d-i');\n\nclass WebGLKernelValueDynamicSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray3DI\n};\n},{\"../../../utils\":114,\"./single-array3d-i\":66}],53:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('./single-input');\n\nclass WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleInput\n};\n},{\"../../../utils\":114,\"./single-input\":67}],54:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('./unsigned-array');\n\nclass WebGLKernelValueDynamicUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedArray\n};\n},{\"../../../utils\":114,\"./unsigned-array\":68}],55:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('./unsigned-input');\n\nclass WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value.value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedInput\n};\n},{\"../../../utils\":114,\"./unsigned-input\":69}],56:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueFloat extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      if (Number.isInteger(value)) {\n        return `const float ${this.id} = ${value}.0;\\n`;\n      }\n      return `const float ${this.id} = ${value};\\n`;\n    }\n    return `uniform float ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1f(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueFloat\n};\n},{\"../../../utils\":114,\"./index\":59}],57:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueHTMLImage extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.uploadValue = value;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputImage) {\n    if (inputImage.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputImage.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue = inputImage);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueHTMLImage\n};\n},{\"../../../utils\":114,\"./array\":40}],58:[function(require,module,exports){\nconst { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueHTMLVideo extends WebGLKernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueHTMLVideo\n};\n},{\"./html-image\":57}],59:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { KernelValue } = require('../../kernel-value');\n\nclass WebGLKernelValue extends KernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.dimensionsId = null;\n    this.sizeId = null;\n    this.initialValueConstructor = value.constructor;\n    this.onRequestTexture = settings.onRequestTexture;\n    this.onRequestIndex = settings.onRequestIndex;\n    this.uploadValue = null;\n    this.textureSize = null;\n    this.bitRatio = null;\n    this.prevArg = null;\n  }\n\n  get id() {\n    return `${this.origin}_${utils.sanitizeName(this.name)}`;\n  }\n\n  setup() {}\n\n  getTransferArrayType(value) {\n    if (Array.isArray(value[0])) {\n      return this.getTransferArrayType(value[0]);\n    }\n    switch (value.constructor) {\n      case Array:\n      case Int32Array:\n      case Int16Array:\n      case Int8Array:\n        return Float32Array;\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Uint16Array:\n      case Uint32Array:\n      case Float32Array:\n      case Float64Array:\n        return value.constructor;\n    }\n    console.warn('Unfamiliar constructor type.  Will go ahead and use, but likley this may result in a transfer of zeros');\n    return value.constructor;\n  }\n\n  getStringValueHandler() {\n    throw new Error(`\"getStringValueHandler\" not implemented on ${this.constructor.name}`);\n  }\n\n  getVariablePrecisionString() {\n    return this.kernel.getVariablePrecisionString(this.textureSize || undefined, this.tactic || undefined);\n  }\n\n  destroy() {}\n}\n\nmodule.exports = {\n  WebGLKernelValue\n};\n},{\"../../../utils\":114,\"../../kernel-value\":35}],60:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueInteger extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueInteger\n};\n},{\"../../../utils\":114,\"./index\":59}],61:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nconst sameError = `Source and destination textures are the same.  Use immutable = true and manually cleanup kernel output texture memory with texture.delete()`;\n\nclass WebGLKernelValueMemoryOptimizedNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    this.dimensions = value.dimensions;\n    this.textureSize = value.size;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueMemoryOptimizedNumberTexture,\n  sameError\n};\n},{\"../../../utils\":114,\"./array\":40}],62:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\nconst { sameError } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    const { size: textureSize, dimensions } = value;\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = dimensions;\n    this.textureSize = textureSize;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueNumberTexture\n};\n},{\"../../../utils\":114,\"./array\":40,\"./memory-optimized-number-texture\":61}],63:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray\n};\n},{\"../../../utils\":114,\"./array\":40}],64:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray1DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], 1, 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten2dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray1DI\n};\n},{\"../../../utils\":114,\"./array\":40}],65:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray2DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten3dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray2DI\n};\n},{\"../../../utils\":114,\"./array\":40}],66:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray3DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], valueDimensions[3]]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten4dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray3DI\n};\n},{\"../../../utils\":114,\"./array\":40}],67:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}.value, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(input.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleInput\n};\n},{\"../../../utils\":114,\"./array\":40}],68:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedArray\n};\n},{\"../../../utils\":114,\"./array\":40}],69:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    const [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value.value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}.value, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedInput\n};\n},{\"../../../utils\":114,\"./array\":40}],70:[function(require,module,exports){\nconst { GLKernel } = require('../gl/kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { WebGLFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst mrud = require('../../plugins/math-random-uniformly-distributed');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { glKernelString } = require('../gl/kernel-string');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nconst plugins = [mrud];\nconst canvases = [];\nconst maxTexSizes = {};\n\n\nclass WebGLKernel extends GLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      OES_texture_float: testContext.getExtension('OES_texture_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    if (typeof WebGLRenderingContext !== 'undefined') {\n      return context instanceof WebGLRenderingContext;\n    }\n    return false;\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.program = null;\n    this.pipeline = settings.pipeline;\n    this.endianness = utils.systemEndianness();\n    this.extensions = {};\n    this.argumentTextureCount = 0;\n    this.constantTextureCount = 0;\n    this.fragShader = null;\n    this.vertShader = null;\n    this.drawBuffersMap = null;\n\n    this.maxTexSize = null;\n    this.onRequestSwitchKernel = null;\n\n    this.texture = null;\n    this.mappedTextures = null;\n    this.mergeSettings(source.settings || settings);\n\n    this.threadDim = null;\n    this.framebuffer = null;\n    this.buffer = null;\n\n    this.textureCache = [];\n    this.programUniformLocationCache = {};\n    this.uniform1fCache = {};\n    this.uniform1iCache = {};\n    this.uniform2fCache = {};\n    this.uniform2fvCache = {};\n    this.uniform2ivCache = {};\n    this.uniform3fvCache = {};\n    this.uniform3ivCache = {};\n    this.uniform4fvCache = {};\n    this.uniform4ivCache = {};\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      const canvas = document.createElement('canvas');\n      canvas.width = 2;\n      canvas.height = 2;\n      return canvas;\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl', settings) || this.canvas.getContext('experimental-webgl', settings);\n  }\n\n  initPlugins(settings) {\n    const pluginsToUse = [];\n    const { source } = this;\n    if (typeof source === 'string') {\n      for (let i = 0; i < plugins.length; i++) {\n        const plugin = plugins[i];\n        if (source.match(plugin.functionMatch)) {\n          pluginsToUse.push(plugin);\n        }\n      }\n    } else if (typeof source === 'object') {\n      if (settings.pluginNames) { \n        for (let i = 0; i < plugins.length; i++) {\n          const plugin = plugins[i];\n          const usePlugin = settings.pluginNames.some(pluginName => pluginName === plugin.name);\n          if (usePlugin) {\n            pluginsToUse.push(plugin);\n          }\n        }\n      }\n    }\n    return pluginsToUse;\n  }\n\n  initExtensions() {\n    this.extensions = {\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n      WEBGL_color_buffer_float: this.context.getExtension('WEBGL_color_buffer_float'),\n    };\n  }\n\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n\n    if (this.optimizeFloatMemory === true && !features.isTextureFloat) {\n      throw new Error('Float textures are not supported');\n    } else if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Single precision not supported');\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.subKernels && this.subKernels.length > 0 && !this.extensions.WEBGL_draw_buffers) {\n      throw new Error('could not instantiate draw buffers extension');\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'precision') {\n        this.precision = 'unsigned';\n        console.warn('Cannot use graphical mode and single precision at the same time');\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  updateMaxTexSize() {\n    const { texSize, canvas } = this;\n    if (this.maxTexSize === null) {\n      let canvasIndex = canvases.indexOf(canvas);\n      if (canvasIndex === -1) {\n        canvasIndex = canvases.length;\n        canvases.push(canvas);\n        maxTexSizes[canvasIndex] = [texSize[0], texSize[1]];\n      }\n      this.maxTexSize = maxTexSizes[canvasIndex];\n    }\n    if (this.maxTexSize[0] < texSize[0]) {\n      this.maxTexSize[0] = texSize[0];\n    }\n    if (this.maxTexSize[1] < texSize[1]) {\n      this.maxTexSize[1] = texSize[1];\n    }\n  }\n\n  setupArguments(args) {\n    this.kernelArguments = [];\n    this.argumentTextureCount = 0;\n    const needsArgumentTypes = this.argumentTypes === null;\n    if (needsArgumentTypes) {\n      this.argumentTypes = [];\n    }\n    this.argumentSizes = [];\n    this.argumentBitRatios = [];\n\n    if (args.length < this.argumentNames.length) {\n      throw new Error('not enough arguments for kernel');\n    } else if (args.length > this.argumentNames.length) {\n      throw new Error('too many arguments for kernel');\n    }\n\n    const { context: gl } = this;\n    let textureIndexes = 0;\n\n    const onRequestTexture = () => {\n      return this.createTexture();\n    };\n    const onRequestIndex = () => {\n      return this.constantTextureCount + textureIndexes++;\n    };\n    const onUpdateValueMismatch = (constructor) => {\n      this.switchKernels({\n        type: 'argumentMismatch',\n        needed: constructor\n      });\n    };\n    const onRequestContextHandle = () => {\n      return gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount++;\n    };\n\n    for (let index = 0; index < args.length; index++) {\n      const value = args[index];\n      const name = this.argumentNames[index];\n      let type;\n      if (needsArgumentTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.argumentTypes.push(type);\n      } else {\n        type = this.argumentTypes[index];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, this.dynamicArguments ? 'dynamic' : 'static', this.precision, args[index]);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelArgument = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'user',\n        context: gl,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture,\n        onRequestIndex,\n        onUpdateValueMismatch,\n        onRequestContextHandle,\n      });\n      this.kernelArguments.push(kernelArgument);\n      kernelArgument.setup();\n      this.argumentSizes.push(kernelArgument.textureSize);\n      this.argumentBitRatios[index] = kernelArgument.bitRatio;\n    }\n  }\n\n  createTexture() {\n    const texture = this.context.createTexture();\n    this.textureCache.push(texture);\n    return texture;\n  }\n\n  setupConstants(args) {\n    const { context: gl } = this;\n    this.kernelConstants = [];\n    this.forceUploadKernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    let textureIndexes = 0;\n    for (const name in this.constants) {\n      const value = this.constants[name];\n      let type;\n      if (needsConstantTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.constantTypes[name] = type;\n      } else {\n        type = this.constantTypes[name];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, 'static', this.precision, value);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelValue = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'constants',\n        context: this.context,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture: () => {\n          return this.createTexture();\n        },\n        onRequestIndex: () => {\n          return textureIndexes++;\n        },\n        onRequestContextHandle: () => {\n          return gl.TEXTURE0 + this.constantTextureCount++;\n        }\n      });\n      this.constantBitRatios[name] = kernelValue.bitRatio;\n      this.kernelConstants.push(kernelValue);\n      kernelValue.setup();\n      if (kernelValue.forceUploadEachRun) {\n        this.forceUploadKernelConstants.push(kernelValue);\n      }\n    }\n  }\n\n  build() {\n    if (this.built) return;\n    this.initExtensions();\n    this.validateSettings(arguments);\n    this.setupConstants(arguments);\n    if (this.fallbackRequested) return;\n    this.setupArguments(arguments);\n    if (this.fallbackRequested) return;\n    this.updateMaxTexSize();\n    this.translateSource();\n    const failureResult = this.pickRenderStrategy(arguments);\n    if (failureResult) {\n      return failureResult;\n    }\n    const { texSize, context: gl, canvas } = this;\n    gl.enable(gl.SCISSOR_TEST);\n    if (this.pipeline && this.precision === 'single') {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    } else {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    }\n    const threadDim = this.threadDim = Array.from(this.output);\n    while (threadDim.length < 3) {\n      threadDim.push(1);\n    }\n\n    const compiledVertexShader = this.getVertexShader(arguments);\n    const vertShader = gl.createShader(gl.VERTEX_SHADER);\n    gl.shaderSource(vertShader, compiledVertexShader);\n    gl.compileShader(vertShader);\n    this.vertShader = vertShader;\n\n    const compiledFragmentShader = this.getFragmentShader(arguments);\n    const fragShader = gl.createShader(gl.FRAGMENT_SHADER);\n    gl.shaderSource(fragShader, compiledFragmentShader);\n    gl.compileShader(fragShader);\n    this.fragShader = fragShader;\n\n    if (this.debug) {\n      console.log('GLSL Shader Output:');\n      console.log(compiledFragmentShader);\n    }\n\n    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader));\n    }\n    if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader));\n    }\n\n    const program = this.program = gl.createProgram();\n    gl.attachShader(program, vertShader);\n    gl.attachShader(program, fragShader);\n    gl.linkProgram(program);\n    this.framebuffer = gl.createFramebuffer();\n    this.framebuffer.width = texSize[0];\n    this.framebuffer.height = texSize[1];\n    this.rawValueFramebuffers = {};\n\n    const vertices = new Float32Array([-1, -1,\n      1, -1, -1, 1,\n      1, 1\n    ]);\n    const texCoords = new Float32Array([\n      0, 0,\n      1, 0,\n      0, 1,\n      1, 1\n    ]);\n\n    const texCoordOffset = vertices.byteLength;\n\n    let buffer = this.buffer;\n    if (!buffer) {\n      buffer = this.buffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n      gl.bufferData(gl.ARRAY_BUFFER, vertices.byteLength + texCoords.byteLength, gl.STATIC_DRAW);\n    } else {\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n    }\n\n    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);\n    gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);\n\n    const aPosLoc = gl.getAttribLocation(this.program, 'aPos');\n    gl.enableVertexAttribArray(aPosLoc);\n    gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0);\n    const aTexCoordLoc = gl.getAttribLocation(this.program, 'aTexCoord');\n    gl.enableVertexAttribArray(aTexCoordLoc);\n    gl.vertexAttribPointer(aTexCoordLoc, 2, gl.FLOAT, false, 0, texCoordOffset);\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\n    let i = 0;\n    gl.useProgram(this.program);\n    for (let p in this.constants) {\n      this.kernelConstants[i++].updateValue(this.constants[p]);\n    }\n\n    this._setupOutputTexture();\n    if (\n      this.subKernels !== null &&\n      this.subKernels.length > 0\n    ) {\n      this._mappedTextureSwitched = {};\n      this._setupSubOutputTextures();\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGLFunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  setupReturnTypes(functionBuilder) {\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n\n    if (this.subKernels && this.subKernels.length > 0) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const subKernel = this.subKernels[i];\n        if (!subKernel.returnType) {\n          subKernel.returnType = functionBuilder.getSubKernelResultType(i);\n        }\n      }\n    }\n  }\n\n  run() {\n    const { kernelArguments, texSize, forceUploadKernelConstants, context: gl } = this;\n\n    gl.useProgram(this.program);\n    gl.scissor(0, 0, texSize[0], texSize[1]);\n    if (this.dynamicOutput) {\n      this.setUniform3iv('uOutputDim', new Int32Array(this.threadDim));\n      this.setUniform2iv('uTexSize', texSize);\n    }\n\n    this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);\n\n    for (let i = 0; i < forceUploadKernelConstants.length; i++) {\n      const constant = forceUploadKernelConstants[i];\n      constant.updateValue(this.constants[constant.name]);\n      if (this.switchingKernels) return;\n    }\n    for (let i = 0; i < kernelArguments.length; i++) {\n      kernelArguments[i].updateValue(arguments[i]);\n      if (this.switchingKernels) return;\n    }\n\n    if (this.plugins) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.onBeforeRun) {\n          plugin.onBeforeRun(this);\n        }\n      }\n    }\n\n    if (this.graphical) {\n      if (this.pipeline) {\n        gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n        gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n        if (this.immutable) {\n          this._replaceOutputTexture();\n        }\n        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n        return this.immutable ? this.texture.clone() : this.texture;\n      }\n      gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n      gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n      return;\n    }\n\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    if (this.immutable) {\n      this._replaceOutputTexture();\n    }\n\n    if (this.subKernels !== null) {\n      if (this.immutable) {\n        this._replaceSubOutputTextures();\n      }\n      this.drawBuffers();\n    }\n\n    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n  }\n\n  drawBuffers() {\n    this.extensions.WEBGL_draw_buffers.drawBuffersWEBGL(this.drawBuffersMap);\n  }\n\n  getInternalFormat() {\n    return this.context.RGBA;\n  }\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n\n  _replaceOutputTexture() {\n    if (this.texture.beforeMutate() || this._textureSwitched) {\n      const gl = this.context;\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      this._textureSwitched = false;\n    }\n  }\n\n  _setupOutputTexture() {\n    const gl = this.context;\n    const texSize = this.texSize;\n    if (this.texture) {\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    const texture = this.createTexture();\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  _replaceSubOutputTextures() {\n    const gl = this.context;\n    for (let i = 0; i < this.mappedTextures.length; i++) {\n      const mappedTexture = this.mappedTextures[i];\n      if (mappedTexture.beforeMutate() || this._mappedTextureSwitched[i]) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, mappedTexture.texture, 0);\n        this._mappedTextureSwitched[i] = false;\n      }\n    }\n  }\n\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      if (this.precision === 'single') {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  setUniform1f(name, value) {\n    if (this.uniform1fCache.hasOwnProperty(name)) {\n      const cache = this.uniform1fCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1fCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1f(loc, value);\n  }\n\n  setUniform1i(name, value) {\n    if (this.uniform1iCache.hasOwnProperty(name)) {\n      const cache = this.uniform1iCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1iCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1i(loc, value);\n  }\n\n  setUniform2f(name, value1, value2) {\n    if (this.uniform2fCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fCache[name];\n      if (\n        value1 === cache[0] &&\n        value2 === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fCache[name] = [value1, value2];\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2f(loc, value1, value2);\n  }\n\n  setUniform2fv(name, value) {\n    if (this.uniform2fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2fv(loc, value);\n  }\n\n  setUniform2iv(name, value) {\n    if (this.uniform2ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform2ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2iv(loc, value);\n  }\n\n  setUniform3fv(name, value) {\n    if (this.uniform3fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform3fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3fv(loc, value);\n  }\n\n  setUniform3iv(name, value) {\n    if (this.uniform3ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform3ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3iv(loc, value);\n  }\n\n  setUniform4fv(name, value) {\n    if (this.uniform4fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform4fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4fv(loc, value);\n  }\n\n  setUniform4iv(name, value) {\n    if (this.uniform4ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform4ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4iv(loc, value);\n  }\n\n  getUniformLocation(name) {\n    if (this.programUniformLocationCache.hasOwnProperty(name)) {\n      return this.programUniformLocationCache[name];\n    }\n    return this.programUniformLocationCache[name] = this.context.getUniformLocation(this.program, name);\n  }\n\n  _getFragShaderArtifactMap(args) {\n    return {\n      HEADER: this._getHeaderString(),\n      LOOP_MAX: this._getLoopMaxString(),\n      PLUGINS: this._getPluginsString(),\n      CONSTANTS: this._getConstantsString(),\n      DECODE32_ENDIANNESS: this._getDecode32EndiannessString(),\n      ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(),\n      DIVIDE_WITH_INTEGER_CHECK: this._getDivideWithIntegerCheckString(),\n      INJECTED_NATIVE: this._getInjectedNative(),\n      MAIN_CONSTANTS: this._getMainConstantsString(),\n      MAIN_ARGUMENTS: this._getMainArgumentsString(args),\n      KERNEL: this.getKernelString(),\n      MAIN_RESULT: this.getMainResultString(),\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  _getVertShaderArtifactMap(args) {\n    return {\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  _getHeaderString() {\n    return (\n      this.subKernels !== null ?\n      '#extension GL_EXT_draw_buffers : require\\n' :\n      ''\n    );\n  }\n\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${parseInt(this.loopMaxIterations)};\\n` :\n      ' 1000;\\n'\n    );\n  }\n\n  _getPluginsString() {\n    if (!this.plugins) return '\\n';\n    return this.plugins.map(plugin => plugin.source && this.source.match(plugin.functionMatch) ? plugin.source : '').join('\\n');\n  }\n\n  _getConstantsString() {\n    const result = [];\n    const { threadDim, texSize } = this;\n    if (this.dynamicOutput) {\n      result.push(\n        'uniform ivec3 uOutputDim',\n        'uniform ivec2 uTexSize'\n      );\n    } else {\n      result.push(\n        `ivec3 uOutputDim = ivec3(${threadDim[0]}, ${threadDim[1]}, ${threadDim[2]})`,\n        `ivec2 uTexSize = ivec2(${texSize[0]}, ${texSize[1]})`\n      );\n    }\n    return utils.linesToString(result);\n  }\n\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    if (subKernels === null || subKernels.length < 1) {\n      return 'varying vec2 vTexCoord;\\n';\n    } else {\n      return 'out vec2 vTexCoord;\\n';\n    }\n  }\n\n  _getDecode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  _getEncode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  _getDivideWithIntegerCheckString() {\n    return this.fixIntegerDivisionAccuracy ?\n      `float divWithIntCheck(float x, float y) {\n  if (floor(x) == x && floor(y) == y && integerMod(x, y) == 0.0) {\n    return float(int(x) / int(y));\n  }\n  return x / y;\n}\n\nfloat integerCorrectionModulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -(number - (divisor * floor(divWithIntCheck(number, divisor))));\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return number - (divisor * floor(divWithIntCheck(number, divisor)));\n}` :\n      '';\n  }\n\n  _getMainArgumentsString(args) {\n    const results = [];\n    const { argumentNames } = this;\n    for (let i = 0; i < argumentNames.length; i++) {\n      results.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return results.join('');\n  }\n\n  _getInjectedNative() {\n    return this.injectedNative || '';\n  }\n\n  _getMainConstantsString() {\n    const result = [];\n    const { constants } = this;\n    if (constants) {\n      let i = 0;\n      for (const name in constants) {\n        if (!this.constants.hasOwnProperty(name)) continue;\n        result.push(this.kernelConstants[i++].getSource(this.constants[name]));\n      }\n    }\n    return result.join('');\n  }\n\n  getRawValueFramebuffer(width, height) {\n    if (!this.rawValueFramebuffers[width]) {\n      this.rawValueFramebuffers[width] = {};\n    }\n    if (!this.rawValueFramebuffers[width][height]) {\n      const framebuffer = this.context.createFramebuffer();\n      framebuffer.width = width;\n      framebuffer.height = height;\n      this.rawValueFramebuffers[width][height] = framebuffer;\n    }\n    return this.rawValueFramebuffers[width][height];\n  }\n\n  getKernelResultDeclaration() {\n    switch (this.returnType) {\n      case 'Array(2)':\n        return 'vec2 kernelResult';\n      case 'Array(3)':\n        return 'vec3 kernelResult';\n      case 'Array(4)':\n        return 'vec4 kernelResult';\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Number':\n      case 'Integer':\n        return 'float kernelResult';\n      default:\n        if (this.graphical) {\n          return 'float kernelResult';\n        } else {\n          throw new Error(`unrecognized output type \"${ this.returnType }\"`);\n        }\n    }\n  }\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const { subKernels } = this;\n    if (subKernels !== null) {\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n      }\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragColor = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultMemoryOptimizedFloats() {\n    const result = [\n      '  index *= 4',\n    ];\n\n    switch (this.returnType) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        const channels = ['r', 'g', 'b', 'a'];\n        for (let i = 0; i < channels.length; i++) {\n          const channel = channels[i];\n          this.getMainResultKernelMemoryOptimizedFloats(result, channel);\n          this.getMainResultSubKernelMemoryOptimizedFloats(result, channel);\n          if (i + 1 < channels.length) {\n            result.push('  index += 1');\n          }\n        }\n        break;\n      default:\n        throw new Error(`optimized output only usable with Numbers, ${this.returnType} specified`);\n    }\n\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0].${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = float(subKernelResult_${this.subKernels[i].name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = subKernelResult_${this.subKernels[i].name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n      '  gl_FragData[0][2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n        `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    switch (this.returnType) {\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          const subKernel = this.subKernels[i];\n          if (subKernel.returnType === 'Integer') {\n            result.push(\n              `  gl_FragData[${i + 1}] = float(subKernelResult_${this.subKernels[i].name})`\n            );\n          } else {\n            result.push(\n              `  gl_FragData[${i + 1}] = subKernelResult_${this.subKernels[i].name}`\n            );\n          }\n        }\n        break;\n      case 'Array(2)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n          );\n        }\n        break;\n      case 'Array(3)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n          );\n        }\n        break;\n      case 'Array(4)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`,\n            `  gl_FragData[${i + 1}][3] = subKernelResult_${this.subKernels[i].name}[3]`\n          );\n        }\n        break;\n    }\n\n    return result;\n  }\n\n  replaceArtifacts(src, map) {\n    return src.replace(/[ ]*__([A-Z]+[0-9]*([_]?[A-Z]*[0-9]?)*)__;\\n/g, (match, artifact) => {\n      if (map.hasOwnProperty(artifact)) {\n        return map[artifact];\n      }\n      throw `unhandled artifact ${artifact}`;\n    });\n  }\n\n  getFragmentShader(args) {\n    if (this.compiledFragmentShader !== null) {\n      return this.compiledFragmentShader;\n    }\n    return this.compiledFragmentShader = this.replaceArtifacts(this.constructor.fragmentShader, this._getFragShaderArtifactMap(args));\n  }\n\n  getVertexShader(args) {\n    if (this.compiledVertexShader !== null) {\n      return this.compiledVertexShader;\n    }\n    return this.compiledVertexShader = this.replaceArtifacts(this.constructor.vertexShader, this._getVertShaderArtifactMap(args));\n  }\n\n  toString() {\n    const setupContextString = utils.linesToString([\n      `const gl = context`,\n    ]);\n    return glKernelString(this.constructor, arguments, this, setupContextString);\n  }\n\n  destroy(removeCanvasReferences) {\n    if (!this.context) return;\n    if (this.buffer) {\n      this.context.deleteBuffer(this.buffer);\n    }\n    if (this.framebuffer) {\n      this.context.deleteFramebuffer(this.framebuffer);\n    }\n    for (const width in this.rawValueFramebuffers) {\n      for (const height in this.rawValueFramebuffers[width]) {\n        this.context.deleteFramebuffer(this.rawValueFramebuffers[width][height]);\n        delete this.rawValueFramebuffers[width][height];\n      }\n      delete this.rawValueFramebuffers[width];\n    }\n    if (this.vertShader) {\n      this.context.deleteShader(this.vertShader);\n    }\n    if (this.fragShader) {\n      this.context.deleteShader(this.fragShader);\n    }\n    if (this.program) {\n      this.context.deleteProgram(this.program);\n    }\n    if (this.texture) {\n      this.texture.delete();\n      const textureCacheIndex = this.textureCache.indexOf(this.texture.texture);\n      if (textureCacheIndex > -1) {\n        this.textureCache.splice(textureCacheIndex, 1);\n      }\n      this.texture = null;\n    }\n    if (this.mappedTextures && this.mappedTextures.length) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        const mappedTexture = this.mappedTextures[i];\n        mappedTexture.delete();\n        const textureCacheIndex = this.textureCache.indexOf(mappedTexture.texture);\n        if (textureCacheIndex > -1) {\n          this.textureCache.splice(textureCacheIndex, 1);\n        }\n      }\n      this.mappedTextures = null;\n    }\n    if (this.kernelArguments) {\n      for (let i = 0; i < this.kernelArguments.length; i++) {\n        this.kernelArguments[i].destroy();\n      }\n    }\n    if (this.kernelConstants) {\n      for (let i = 0; i < this.kernelConstants.length; i++) {\n        this.kernelConstants[i].destroy();\n      }\n    }\n    while (this.textureCache.length > 0) {\n      const texture = this.textureCache.pop();\n      this.context.deleteTexture(texture);\n    }\n    if (removeCanvasReferences) {\n      const idx = canvases.indexOf(this.canvas);\n      if (idx >= 0) {\n        canvases[idx] = null;\n        maxTexSizes[idx] = null;\n      }\n    }\n    this.destroyExtensions();\n    delete this.context;\n    delete this.canvas;\n    if (!this.gpu) return;\n    const i = this.gpu.kernels.indexOf(this);\n    if (i === -1) return;\n    this.gpu.kernels.splice(i, 1);\n  }\n\n  destroyExtensions() {\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('WEBGL_lose_context');\n    if (extension) {\n      extension.loseContext();\n    }\n  }\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGLFunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGLKernel\n};\n},{\"../../plugins/math-random-uniformly-distributed\":112,\"../../utils\":114,\"../function-builder\":9,\"../gl/kernel\":13,\"../gl/kernel-string\":12,\"./fragment-shader\":37,\"./function-node\":38,\"./kernel-value-maps\":39,\"./vertex-shader\":71}],71:[function(require,module,exports){\nconst vertexShader = `__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nattribute vec2 aPos;\nattribute vec2 aTexCoord;\n\nvarying vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};\n},{}],72:[function(require,module,exports){\nconst fragmentShader = `#version 300 es\n__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n__SAMPLER_2D_ARRAY_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nin vec2 vTexCoord;\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x/y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  return texel[channel*2] * 255.0 + texel[channel*2 + 1] * 65280.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  return texel[channel] * 255.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  index = index / 4;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return texel[channel];\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, st / vec2(texSize));\n}\n\nvec4 getImage3D(sampler2DArray tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, vec3(st / vec2(texSize), z));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n\n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};\n},{}],73:[function(require,module,exports){\nconst { utils } = require('../../utils');\nconst { WebGLFunctionNode } = require('../web-gl/function-node');\n\nclass WebGL2FunctionNode extends WebGLFunctionNode {\n\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      retArr.push('intBitsToFloat(2139095039)');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  WebGL2FunctionNode\n};\n},{\"../../utils\":114,\"../web-gl/function-node\":38}],74:[function(require,module,exports){\nconst { WebGL2KernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGL2KernelValueFloat } = require('./kernel-value/float');\nconst { WebGL2KernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGL2KernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGL2KernelValueHTMLImageArray } = require('./kernel-value/html-image-array');\nconst { WebGL2KernelValueDynamicHTMLImageArray } = require('./kernel-value/dynamic-html-image-array');\n\nconst { WebGL2KernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGL2KernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGL2KernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGL2KernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGL2KernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGL2KernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGL2KernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGL2KernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGL2KernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGL2KernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGL2KernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGL2KernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGL2KernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGL2KernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGL2KernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGL2KernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGL2KernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGL2KernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGL2KernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGL2KernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGL2KernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGL2KernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGL2KernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueUnsignedInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Input': WebGL2KernelValueDynamicSingleInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueSingleArray3DI,\n      'Input': WebGL2KernelValueSingleInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  kernelValueMaps,\n  lookupKernelValueType\n};\n},{\"./kernel-value/array2\":75,\"./kernel-value/array3\":76,\"./kernel-value/array4\":77,\"./kernel-value/boolean\":78,\"./kernel-value/dynamic-html-image\":80,\"./kernel-value/dynamic-html-image-array\":79,\"./kernel-value/dynamic-html-video\":81,\"./kernel-value/dynamic-memory-optimized-number-texture\":82,\"./kernel-value/dynamic-number-texture\":83,\"./kernel-value/dynamic-single-array\":84,\"./kernel-value/dynamic-single-array1d-i\":85,\"./kernel-value/dynamic-single-array2d-i\":86,\"./kernel-value/dynamic-single-array3d-i\":87,\"./kernel-value/dynamic-single-input\":88,\"./kernel-value/dynamic-unsigned-array\":89,\"./kernel-value/dynamic-unsigned-input\":90,\"./kernel-value/float\":91,\"./kernel-value/html-image\":93,\"./kernel-value/html-image-array\":92,\"./kernel-value/html-video\":94,\"./kernel-value/integer\":95,\"./kernel-value/memory-optimized-number-texture\":96,\"./kernel-value/number-texture\":97,\"./kernel-value/single-array\":98,\"./kernel-value/single-array1d-i\":99,\"./kernel-value/single-array2d-i\":100,\"./kernel-value/single-array3d-i\":101,\"./kernel-value/single-input\":102,\"./kernel-value/unsigned-array\":103,\"./kernel-value/unsigned-input\":104}],75:[function(require,module,exports){\nconst { WebGLKernelValueArray2 } = require('../../web-gl/kernel-value/array2');\n\nclass WebGL2KernelValueArray2 extends WebGLKernelValueArray2 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray2\n};\n},{\"../../web-gl/kernel-value/array2\":41}],76:[function(require,module,exports){\nconst { WebGLKernelValueArray3 } = require('../../web-gl/kernel-value/array3');\n\nclass WebGL2KernelValueArray3 extends WebGLKernelValueArray3 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray3\n};\n},{\"../../web-gl/kernel-value/array3\":42}],77:[function(require,module,exports){\nconst { WebGLKernelValueArray4 } = require('../../web-gl/kernel-value/array4');\n\nclass WebGL2KernelValueArray4 extends WebGLKernelValueArray4 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray4\n};\n},{\"../../web-gl/kernel-value/array4\":43}],78:[function(require,module,exports){\nconst { WebGLKernelValueBoolean } = require('../../web-gl/kernel-value/boolean');\n\nclass WebGL2KernelValueBoolean extends WebGLKernelValueBoolean {}\n\nmodule.exports = {\n  WebGL2KernelValueBoolean\n};\n},{\"../../web-gl/kernel-value/boolean\":44}],79:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImageArray } = require('./html-image-array');\n\nclass WebGL2KernelValueDynamicHTMLImageArray extends WebGL2KernelValueHTMLImageArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { width, height } = images[0];\n    this.checkSize(width, height);\n    this.dimensions = [width, height, images.length];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(images);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImageArray\n};\n},{\"../../../utils\":114,\"./html-image-array\":92}],80:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicHTMLImage } = require('../../web-gl/kernel-value/dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLImage extends WebGLKernelValueDynamicHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImage\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/dynamic-html-image\":45}],81:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLVideo extends WebGL2KernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLVideo\n};\n},{\"../../../utils\":114,\"./dynamic-html-image\":80}],82:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/dynamic-memory-optimized-number-texture');\n\nclass WebGL2KernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueDynamicMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/dynamic-memory-optimized-number-texture\":47}],83:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicNumberTexture } = require('../../web-gl/kernel-value/dynamic-number-texture');\n\nclass WebGL2KernelValueDynamicNumberTexture extends WebGLKernelValueDynamicNumberTexture {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicNumberTexture\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/dynamic-number-texture\":48}],84:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray } = require('../../web-gl2/kernel-value/single-array');\n\nclass WebGL2KernelValueDynamicSingleArray extends WebGL2KernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray\n};\n},{\"../../../utils\":114,\"../../web-gl2/kernel-value/single-array\":98}],85:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray1DI } = require('../../web-gl2/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueDynamicSingleArray1DI extends WebGL2KernelValueSingleArray1DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray1DI\n};\n},{\"../../../utils\":114,\"../../web-gl2/kernel-value/single-array1d-i\":99}],86:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray2DI } = require('../../web-gl2/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueDynamicSingleArray2DI extends WebGL2KernelValueSingleArray2DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray2DI\n};\n},{\"../../../utils\":114,\"../../web-gl2/kernel-value/single-array2d-i\":100}],87:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray3DI } = require('../../web-gl2/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueDynamicSingleArray3DI extends WebGL2KernelValueSingleArray3DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray3DI\n};\n},{\"../../../utils\":114,\"../../web-gl2/kernel-value/single-array3d-i\":101}],88:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleInput } = require('../../web-gl2/kernel-value/single-input');\n\nclass WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleInput\n};\n},{\"../../../utils\":114,\"../../web-gl2/kernel-value/single-input\":102}],89:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('../../web-gl/kernel-value/dynamic-unsigned-array');\n\nclass WebGL2KernelValueDynamicUnsignedArray extends WebGLKernelValueDynamicUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedArray\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/dynamic-unsigned-array\":54}],90:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('../../web-gl/kernel-value/dynamic-unsigned-input');\n\nclass WebGL2KernelValueDynamicUnsignedInput extends WebGLKernelValueDynamicUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedInput\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/dynamic-unsigned-input\":55}],91:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueFloat } = require('../../web-gl/kernel-value/float');\n\nclass WebGL2KernelValueFloat extends WebGLKernelValueFloat {}\n\nmodule.exports = {\n  WebGL2KernelValueFloat\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/float\":56}],92:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('../../web-gl/kernel-value/array');\n\nclass WebGL2KernelValueHTMLImageArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.checkSize(value[0].width, value[0].height);\n    this.dimensions = [value[0].width, value[0].height, value.length];\n    this.textureSize = [value[0].width, value[0].height];\n  }\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    gl.texImage3D(\n      gl.TEXTURE_2D_ARRAY,\n      0,\n      gl.RGBA,\n      images[0].width,\n      images[0].height,\n      images.length,\n      0,\n      gl.RGBA,\n      gl.UNSIGNED_BYTE,\n      null\n    );\n    for (let i = 0; i < images.length; i++) {\n      const xOffset = 0;\n      const yOffset = 0;\n      const imageDepth = 1;\n      gl.texSubImage3D(\n        gl.TEXTURE_2D_ARRAY,\n        0,\n        xOffset,\n        yOffset,\n        i,\n        images[i].width,\n        images[i].height,\n        imageDepth,\n        gl.RGBA,\n        gl.UNSIGNED_BYTE,\n        this.uploadValue = images[i]\n      );\n    }\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImageArray\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/array\":40}],93:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('../../web-gl/kernel-value/html-image');\n\nclass WebGL2KernelValueHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImage\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/html-image\":57}],94:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImage } = require('./html-image');\n\nclass WebGL2KernelValueHTMLVideo extends WebGL2KernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLVideo\n};\n},{\"../../../utils\":114,\"./html-image\":93}],95:[function(require,module,exports){\nconst { WebGLKernelValueInteger } = require('../../web-gl/kernel-value/integer');\n\nclass WebGL2KernelValueInteger extends WebGLKernelValueInteger {\n  getSource(value) {\n    const variablePrecision = this.getVariablePrecisionString();\n    if (this.origin === 'constants') {\n      return `const ${ variablePrecision } int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform ${ variablePrecision } int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueInteger\n};\n},{\"../../web-gl/kernel-value/integer\":60}],96:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/memory-optimized-number-texture');\n\nclass WebGL2KernelValueMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueMemoryOptimizedNumberTexture\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/memory-optimized-number-texture\":61}],97:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('../../web-gl/kernel-value/number-texture');\n\nclass WebGL2KernelValueNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueNumberTexture\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/number-texture\":62}],98:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('../../web-gl/kernel-value/single-array');\n\nclass WebGL2KernelValueSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/single-array\":63}],99:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('../../web-gl/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray1DI\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/single-array1d-i\":64}],100:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('../../web-gl/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray2DI\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/single-array2d-i\":65}],101:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('../../web-gl/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray3DI\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/single-array3d-i\":66}],102:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('../../web-gl/kernel-value/single-input');\n\nclass WebGL2KernelValueSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleInput\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/single-input\":67}],103:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('../../web-gl/kernel-value/unsigned-array');\n\nclass WebGL2KernelValueUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedArray\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/unsigned-array\":68}],104:[function(require,module,exports){\nconst { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('../../web-gl/kernel-value/unsigned-input');\n\nclass WebGL2KernelValueUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedInput\n};\n},{\"../../../utils\":114,\"../../web-gl/kernel-value/unsigned-input\":69}],105:[function(require,module,exports){\nconst { WebGLKernel } = require('../web-gl/kernel');\nconst { WebGL2FunctionNode } = require('./function-node');\nconst { FunctionBuilder } = require('../function-builder');\nconst { utils } = require('../../utils');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\n\nlet features = null;\n\nclass WebGL2Kernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl2');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      EXT_color_buffer_float: testContext.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    if (typeof WebGL2RenderingContext !== 'undefined') {\n      return context instanceof WebGL2RenderingContext;\n    }\n    return false;\n  }\n\n  static getFeatures() {\n    const gl = this.testContext;\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      kernelMap: true,\n      isTextureFloat: true,\n      isDrawBuffers: true,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  static getIsTextureFloat() {\n    return true;\n  }\n\n  static getChannelCount() {\n    return testContext.getParameter(testContext.MAX_DRAW_BUFFERS);\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl2', settings);\n  }\n\n  initExtensions() {\n    this.extensions = {\n      EXT_color_buffer_float: this.context.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n    };\n  }\n\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n    if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Float texture outputs are not supported');\n    } else if (!this.graphical && this.precision === null) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'single') {\n        console.warn('Cannot use graphical mode and single precision at the same time');\n        this.precision = 'unsigned';\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGL2FunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  drawBuffers() {\n    this.context.drawBuffers(this.drawBuffersMap);\n  }\n\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.R32F:\n        return gl.RED;\n      case gl.RG32F:\n        return gl.RG;\n      case gl.RGBA32F:\n        return gl.RGBA;\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n  getInternalFormat() {\n    const { context: gl } = this;\n\n    if (this.precision === 'single') {\n      if (this.pipeline) {\n        switch (this.returnType) {\n          case 'Number':\n          case 'Float':\n          case 'Integer':\n            if (this.optimizeFloatMemory) {\n              return gl.RGBA32F;\n            } else {\n              return gl.R32F;\n            }\n            case 'Array(2)':\n              return gl.RG32F;\n            case 'Array(3)': \n            case 'Array(4)':\n              return gl.RGBA32F;\n            default:\n              throw new Error('Unhandled return type');\n        }\n      }\n      return gl.RGBA32F;\n    }\n    return gl.RGBA;\n  }\n\n  _setupOutputTexture() {\n    const gl = this.context;\n    if (this.texture) {\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    const texture = gl.createTexture();\n    const texSize = this.texSize;\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      const format = this.getInternalFormat();\n      if (this.precision === 'single') {\n        gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  _getHeaderString() {\n    return '';\n  }\n\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    if (subKernels === null || subKernels.length < 1) {\n      return `in ${ variablePrecision } vec2 vTexCoord;\\n`;\n    } else {\n      return `out ${ variablePrecision } vec2 vTexCoord;\\n`;\n    }\n  }\n\n  _getMainArgumentsString(args) {\n    const result = [];\n    const argumentNames = this.argumentNames;\n    for (let i = 0; i < argumentNames.length; i++) {\n      result.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return result.join('');\n  }\n\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const subKernels = this.subKernels;\n    if (subKernels !== null) {\n      result.push(\n        'layout(location = 0) out vec4 data0'\n      );\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n      }\n    } else {\n      result.push(\n        'out vec4 data0'\n      );\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0 = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0.${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}.${channel} = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}.${channel} = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}[0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}[0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n      '  data0[2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`,\n        `  data${i + 1}[2] = subKernelResult_${subKernel.name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  data${i + 1} = subKernelResult_${this.subKernels[i].name}`\n      );\n    }\n    return result;\n  }\n\n  destroyExtensions() {\n    this.extensions.EXT_color_buffer_float = null;\n    this.extensions.OES_texture_float_linear = null;\n  }\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGL2FunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGL2Kernel\n};\n},{\"../../utils\":114,\"../function-builder\":9,\"../web-gl/kernel\":70,\"./fragment-shader\":72,\"./function-node\":73,\"./kernel-value-maps\":74,\"./vertex-shader\":106}],106:[function(require,module,exports){\nconst vertexShader = `#version 300 es\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nin vec2 aPos;\nin vec2 aTexCoord;\n\nout vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};\n},{}],107:[function(require,module,exports){\nconst lib = require('./index');\nconst GPU = lib.GPU;\nfor (const p in lib) {\n  if (!lib.hasOwnProperty(p)) continue;\n  if (p === 'GPU') continue; \n  GPU[p] = lib[p];\n}\n\nif (typeof window !== 'undefined') {\n  bindTo(window);\n}\nif (typeof self !== 'undefined') {\n  bindTo(self);\n}\n\nfunction bindTo(target) {\n  if (target.GPU) return;\n  Object.defineProperty(target, 'GPU', {\n    get() {\n      return GPU;\n    }\n  });\n}\n\nmodule.exports = lib;\n},{\"./index\":109}],108:[function(require,module,exports){\nconst { gpuMock } = require('gpu-mock.js');\nconst { utils } = require('./utils');\nconst { Kernel } = require('./backend/kernel');\nconst { CPUKernel } = require('./backend/cpu/kernel');\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelRunShortcut } = require('./kernel-run-shortcut');\n\n\nconst kernelOrder = [HeadlessGLKernel, WebGL2Kernel, WebGLKernel];\n\nconst kernelTypes = ['gpu', 'cpu'];\n\nconst internalKernels = {\n  'headlessgl': HeadlessGLKernel,\n  'webgl2': WebGL2Kernel,\n  'webgl': WebGLKernel,\n};\n\nlet validate = true;\n\nclass GPU {\n  static disableValidation() {\n    validate = false;\n  }\n\n  static enableValidation() {\n    validate = true;\n  }\n\n  static get isGPUSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported);\n  }\n\n  static get isKernelMapSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.kernelMap);\n  }\n\n  static get isOffscreenCanvasSupported() {\n    return (typeof Worker !== 'undefined' && typeof OffscreenCanvas !== 'undefined') || typeof importScripts !== 'undefined';\n  }\n\n  static get isWebGLSupported() {\n    return WebGLKernel.isSupported;\n  }\n\n  static get isWebGL2Supported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  static get isHeadlessGLSupported() {\n    return HeadlessGLKernel.isSupported;\n  }\n\n  static get isCanvasSupported() {\n    return typeof HTMLCanvasElement !== 'undefined';\n  }\n\n  static get isGPUHTMLImageArraySupported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  static get isSinglePrecisionSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.isFloatRead && Kernel.features.isTextureFloat);\n  }\n\n  constructor(settings) {\n    settings = settings || {};\n    this.canvas = settings.canvas || null;\n    this.context = settings.context || null;\n    this.mode = settings.mode;\n    this.Kernel = null;\n    this.kernels = [];\n    this.functions = [];\n    this.nativeFunctions = [];\n    this.injectedNative = null;\n    if (this.mode === 'dev') return;\n    this.chooseKernel();\n    if (settings.functions) {\n      for (let i = 0; i < settings.functions.length; i++) {\n        this.addFunction(settings.functions[i]);\n      }\n    }\n\n    if (settings.nativeFunctions) {\n      for (const p in settings.nativeFunctions) {\n        if (!settings.nativeFunctions.hasOwnProperty(p)) continue;\n        const s = settings.nativeFunctions[p];\n        const { name, source } = s;\n        this.addNativeFunction(name, source, s);\n      }\n    }\n  }\n\n  chooseKernel() {\n    if (this.Kernel) return;\n\n    let Kernel = null;\n\n    if (this.context) {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        const ExternalKernel = kernelOrder[i];\n        if (ExternalKernel.isContextMatch(this.context)) {\n          if (!ExternalKernel.isSupported) {\n            throw new Error(`Kernel type ${ExternalKernel.name} not supported`);\n          }\n          Kernel = ExternalKernel;\n          break;\n        }\n      }\n      if (Kernel === null) {\n        throw new Error('unknown Context');\n      }\n    } else if (this.mode) {\n      if (this.mode in internalKernels) {\n        if (!validate || internalKernels[this.mode].isSupported) {\n          Kernel = internalKernels[this.mode];\n        }\n      } else if (this.mode === 'gpu') {\n        for (let i = 0; i < kernelOrder.length; i++) {\n          if (kernelOrder[i].isSupported) {\n            Kernel = kernelOrder[i];\n            break;\n          }\n        }\n      } else if (this.mode === 'cpu') {\n        Kernel = CPUKernel;\n      }\n      if (!Kernel) {\n        throw new Error(`A requested mode of \"${this.mode}\" and is not supported`);\n      }\n    } else {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        if (kernelOrder[i].isSupported) {\n          Kernel = kernelOrder[i];\n          break;\n        }\n      }\n      if (!Kernel) {\n        Kernel = CPUKernel;\n      }\n    }\n\n    if (!this.mode) {\n      this.mode = Kernel.mode;\n    }\n    this.Kernel = Kernel;\n  }\n\n  createKernel(source, settings) {\n    if (typeof source === 'undefined') {\n      throw new Error('Missing source parameter');\n    }\n    if (typeof source !== 'object' && !utils.isFunction(source) && typeof source !== 'string') {\n      throw new Error('source parameter not a function');\n    }\n\n    const kernels = this.kernels;\n    if (this.mode === 'dev') {\n      const devKernel = gpuMock(source, upgradeDeprecatedCreateKernelSettings(settings));\n      kernels.push(devKernel);\n      return devKernel;\n    }\n\n    source = typeof source === 'function' ? source.toString() : source;\n    const switchableKernels = {};\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings) || {};\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    function onRequestFallback(args) {\n      console.warn('Falling back to CPU');\n      const fallbackKernel = new CPUKernel(source, {\n        argumentTypes: kernelRun.argumentTypes,\n        constantTypes: kernelRun.constantTypes,\n        graphical: kernelRun.graphical,\n        loopMaxIterations: kernelRun.loopMaxIterations,\n        constants: kernelRun.constants,\n        dynamicOutput: kernelRun.dynamicOutput,\n        dynamicArgument: kernelRun.dynamicArguments,\n        output: kernelRun.output,\n        precision: kernelRun.precision,\n        pipeline: kernelRun.pipeline,\n        immutable: kernelRun.immutable,\n        optimizeFloatMemory: kernelRun.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: kernelRun.fixIntegerDivisionAccuracy,\n        functions: kernelRun.functions,\n        nativeFunctions: kernelRun.nativeFunctions,\n        injectedNative: kernelRun.injectedNative,\n        subKernels: kernelRun.subKernels,\n        strictIntegers: kernelRun.strictIntegers,\n        debug: kernelRun.debug,\n      });\n      fallbackKernel.build.apply(fallbackKernel, args);\n      const result = fallbackKernel.run.apply(fallbackKernel, args);\n      kernelRun.replaceKernel(fallbackKernel);\n      return result;\n    }\n\n    function onRequestSwitchKernel(reasons, args, _kernel) {\n      if (_kernel.debug) {\n        console.warn('Switching kernels');\n      }\n      let newOutput = null;\n      if (_kernel.signature && !switchableKernels[_kernel.signature]) {\n        switchableKernels[_kernel.signature] = _kernel;\n      }\n      if (_kernel.dynamicOutput) {\n        for (let i = reasons.length - 1; i >= 0; i--) {\n          const reason = reasons[i];\n          if (reason.type === 'outputPrecisionMismatch') {\n            newOutput = reason.needed;\n          }\n        }\n      }\n\n      const Constructor = _kernel.constructor;\n      const argumentTypes = Constructor.getArgumentTypes(_kernel, args);\n      const signature = Constructor.getSignature(_kernel, argumentTypes);\n      const existingKernel = switchableKernels[signature];\n      if (existingKernel) {\n        existingKernel.onActivate(_kernel);\n        return existingKernel;\n      }\n\n      const newKernel = switchableKernels[signature] = new Constructor(source, {\n        argumentTypes,\n        constantTypes: _kernel.constantTypes,\n        graphical: _kernel.graphical,\n        loopMaxIterations: _kernel.loopMaxIterations,\n        constants: _kernel.constants,\n        dynamicOutput: _kernel.dynamicOutput,\n        dynamicArgument: _kernel.dynamicArguments,\n        context: _kernel.context,\n        canvas: _kernel.canvas,\n        output: newOutput || _kernel.output,\n        precision: _kernel.precision,\n        pipeline: _kernel.pipeline,\n        immutable: _kernel.immutable,\n        optimizeFloatMemory: _kernel.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: _kernel.fixIntegerDivisionAccuracy,\n        functions: _kernel.functions,\n        nativeFunctions: _kernel.nativeFunctions,\n        injectedNative: _kernel.injectedNative,\n        subKernels: _kernel.subKernels,\n        strictIntegers: _kernel.strictIntegers,\n        debug: _kernel.debug,\n        gpu: _kernel.gpu,\n        validate,\n        returnType: _kernel.returnType,\n        tactic: _kernel.tactic,\n        onRequestFallback,\n        onRequestSwitchKernel,\n        texture: _kernel.texture,\n        mappedTextures: _kernel.mappedTextures,\n        drawBuffersMap: _kernel.drawBuffersMap,\n      });\n      newKernel.build.apply(newKernel, args);\n      kernelRun.replaceKernel(newKernel);\n      kernels.push(newKernel);\n      return newKernel;\n    }\n    const mergedSettings = Object.assign({\n      context: this.context,\n      canvas: this.canvas,\n      functions: this.functions,\n      nativeFunctions: this.nativeFunctions,\n      injectedNative: this.injectedNative,\n      gpu: this,\n      validate,\n      onRequestFallback,\n      onRequestSwitchKernel\n    }, settingsCopy);\n\n    const kernel = new this.Kernel(source, mergedSettings);\n    const kernelRun = kernelRunShortcut(kernel);\n\n    if (!this.canvas) {\n      this.canvas = kernel.canvas;\n    }\n\n    if (!this.context) {\n      this.context = kernel.context;\n    }\n\n    kernels.push(kernel);\n\n    return kernelRun;\n  }\n\n  createKernelMap() {\n    let fn;\n    let settings;\n    const argument2Type = typeof arguments[arguments.length - 2];\n    if (argument2Type === 'function' || argument2Type === 'string') {\n      fn = arguments[arguments.length - 2];\n      settings = arguments[arguments.length - 1];\n    } else {\n      fn = arguments[arguments.length - 1];\n    }\n\n    if (this.mode !== 'dev') {\n      if (!this.Kernel.isSupported || !this.Kernel.features.kernelMap) {\n        if (this.mode && kernelTypes.indexOf(this.mode) < 0) {\n          throw new Error(`kernelMap not supported on ${this.Kernel.name}`);\n        }\n      }\n    }\n\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings);\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    if (Array.isArray(arguments[0])) {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let i = 0; i < functions.length; i++) {\n        const source = functions[i].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name,\n          source,\n          property: i,\n        });\n      }\n    } else {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let p in functions) {\n        if (!functions.hasOwnProperty(p)) continue;\n        const source = functions[p].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name: name || p,\n          source,\n          property: p,\n        });\n      }\n    }\n    return this.createKernel(fn, settingsCopy);\n  }\n\n  combineKernels() {\n    const firstKernel = arguments[0];\n    const combinedKernel = arguments[arguments.length - 1];\n    if (firstKernel.kernel.constructor.mode === 'cpu') return combinedKernel;\n    const canvas = arguments[0].canvas;\n    const context = arguments[0].context;\n    const max = arguments.length - 1;\n    for (let i = 0; i < max; i++) {\n      arguments[i]\n        .setCanvas(canvas)\n        .setContext(context)\n        .setPipeline(true);\n    }\n\n    return function() {\n      const texture = combinedKernel.apply(this, arguments);\n      if (texture.toArray) {\n        return texture.toArray();\n      }\n      return texture;\n    };\n  }\n\n  setFunctions(functions) {\n    this.functions = functions;\n    return this;\n  }\n\n  setNativeFunctions(nativeFunctions) {\n    this.nativeFunctions = nativeFunctions;\n    return this;\n  }\n\n  addFunction(source, settings) {\n    this.functions.push({ source, settings });\n    return this;\n  }\n\n  addNativeFunction(name, source, settings) {\n    if (this.kernels.length > 0) {\n      throw new Error('Cannot call \"addNativeFunction\" after \"createKernels\" has been called.');\n    }\n    this.nativeFunctions.push(Object.assign({ name, source }, settings));\n    return this;\n  }\n\n  injectNative(source) {\n    this.injectedNative = source;\n    return this;\n  }\n\n  destroy() {\n    return new Promise((resolve, reject) => {\n      if (!this.kernels) {\n        resolve();\n      }\n      setTimeout(() => {\n        try {\n          for (let i = 0; i < this.kernels.length; i++) {\n            this.kernels[i].destroy(true); \n          }\n          let firstKernel = this.kernels[0];\n          if (firstKernel) {\n            if (firstKernel.kernel) {\n              firstKernel = firstKernel.kernel;\n            }\n            if (firstKernel.constructor.destroyContext) {\n              firstKernel.constructor.destroyContext(this.context);\n            }\n          }\n        } catch (e) {\n          reject(e);\n        }\n        resolve();\n      }, 0);\n    });\n  }\n}\n\n\nfunction upgradeDeprecatedCreateKernelSettings(settings) {\n  if (!settings) {\n    return {};\n  }\n  const upgradedSettings = Object.assign({}, settings);\n\n  if (settings.hasOwnProperty('floatOutput')) {\n    utils.warnDeprecated('setting', 'floatOutput', 'precision');\n    upgradedSettings.precision = settings.floatOutput ? 'single' : 'unsigned';\n  }\n  if (settings.hasOwnProperty('outputToTexture')) {\n    utils.warnDeprecated('setting', 'outputToTexture', 'pipeline');\n    upgradedSettings.pipeline = Boolean(settings.outputToTexture);\n  }\n  if (settings.hasOwnProperty('outputImmutable')) {\n    utils.warnDeprecated('setting', 'outputImmutable', 'immutable');\n    upgradedSettings.immutable = Boolean(settings.outputImmutable);\n  }\n  if (settings.hasOwnProperty('floatTextures')) {\n    utils.warnDeprecated('setting', 'floatTextures', 'optimizeFloatMemory');\n    upgradedSettings.optimizeFloatMemory = Boolean(settings.floatTextures);\n  }\n  return upgradedSettings;\n}\n\nmodule.exports = {\n  GPU,\n  kernelOrder,\n  kernelTypes\n};\n},{\"./backend/cpu/kernel\":8,\"./backend/headless-gl/kernel\":34,\"./backend/kernel\":36,\"./backend/web-gl/kernel\":70,\"./backend/web-gl2/kernel\":105,\"./kernel-run-shortcut\":111,\"./utils\":114,\"gpu-mock.js\":4}],109:[function(require,module,exports){\nconst { GPU } = require('./gpu');\nconst { alias } = require('./alias');\nconst { utils } = require('./utils');\nconst { Input, input } = require('./input');\nconst { Texture } = require('./texture');\nconst { FunctionBuilder } = require('./backend/function-builder');\nconst { FunctionNode } = require('./backend/function-node');\nconst { CPUFunctionNode } = require('./backend/cpu/function-node');\nconst { CPUKernel } = require('./backend/cpu/kernel');\n\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\n\nconst { WebGLFunctionNode } = require('./backend/web-gl/function-node');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelValueMaps: webGLKernelValueMaps } = require('./backend/web-gl/kernel-value-maps');\n\nconst { WebGL2FunctionNode } = require('./backend/web-gl2/function-node');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { kernelValueMaps: webGL2KernelValueMaps } = require('./backend/web-gl2/kernel-value-maps');\n\nconst { GLKernel } = require('./backend/gl/kernel');\n\nconst { Kernel } = require('./backend/kernel');\n\nconst { FunctionTracer } = require('./backend/function-tracer');\n\nconst mathRandom = require('./plugins/math-random-uniformly-distributed');\n\nmodule.exports = {\n  alias,\n  CPUFunctionNode,\n  CPUKernel,\n  GPU,\n  FunctionBuilder,\n  FunctionNode,\n  HeadlessGLKernel,\n  Input,\n  input,\n  Texture,\n  utils,\n\n  WebGL2FunctionNode,\n  WebGL2Kernel,\n  webGL2KernelValueMaps,\n\n  WebGLFunctionNode,\n  WebGLKernel,\n  webGLKernelValueMaps,\n\n  GLKernel,\n  Kernel,\n  FunctionTracer,\n\n  plugins: {\n    mathRandom\n  }\n};\n},{\"./alias\":5,\"./backend/cpu/function-node\":6,\"./backend/cpu/kernel\":8,\"./backend/function-builder\":9,\"./backend/function-node\":10,\"./backend/function-tracer\":11,\"./backend/gl/kernel\":13,\"./backend/headless-gl/kernel\":34,\"./backend/kernel\":36,\"./backend/web-gl/function-node\":38,\"./backend/web-gl/kernel\":70,\"./backend/web-gl/kernel-value-maps\":39,\"./backend/web-gl2/function-node\":73,\"./backend/web-gl2/kernel\":105,\"./backend/web-gl2/kernel-value-maps\":74,\"./gpu\":108,\"./input\":110,\"./plugins/math-random-uniformly-distributed\":112,\"./texture\":113,\"./utils\":114}],110:[function(require,module,exports){\nclass Input {\n  constructor(value, size) {\n    this.value = value;\n    if (Array.isArray(size)) {\n      this.size = size;\n    } else {\n      this.size = new Int32Array(3);\n      if (size.z) {\n        this.size = new Int32Array([size.x, size.y, size.z]);\n      } else if (size.y) {\n        this.size = new Int32Array([size.x, size.y]);\n      } else {\n        this.size = new Int32Array([size.x]);\n      }\n    }\n\n    const [w, h, d] = this.size;\n    if (d) {\n      if (this.value.length !== (w * h * d)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`);\n      }\n    } else if (h) {\n      if (this.value.length !== (w * h)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`);\n      }\n    } else {\n      if (this.value.length !== w) {\n        throw new Error(`Input size ${this.value.length} does not match ${w}`);\n      }\n    }\n\n  }\n\n  toArray() {\n    const { utils } = require('./utils');\n    const [w, h, d] = this.size;\n    if (d) {\n      return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d);\n    } else if (h) {\n      return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h);\n    } else {\n      return this.value;\n    }\n  }\n}\n\nfunction input(value, size) {\n  return new Input(value, size);\n}\n\nmodule.exports = {\n  Input,\n  input\n};\n},{\"./utils\":114}],111:[function(require,module,exports){\nconst { utils } = require('./utils');\n\nfunction kernelRunShortcut(kernel) {\n  let run = function() {\n    kernel.build.apply(kernel, arguments);\n    run = function() {\n      let result = kernel.run.apply(kernel, arguments);\n      if (kernel.switchingKernels) {\n        const reasons = kernel.resetSwitchingKernels();\n        const newKernel = kernel.onRequestSwitchKernel(reasons, arguments, kernel);\n        shortcut.kernel = kernel = newKernel;\n        result = newKernel.run.apply(newKernel, arguments);\n      }\n      if (kernel.renderKernels) {\n        return kernel.renderKernels();\n      } else if (kernel.renderOutput) {\n        return kernel.renderOutput();\n      } else {\n        return result;\n      }\n    };\n    return run.apply(kernel, arguments);\n  };\n  const shortcut = function() {\n    return run.apply(kernel, arguments);\n  };\n  shortcut.exec = function() {\n    return new Promise((accept, reject) => {\n      try {\n        accept(run.apply(this, arguments));\n      } catch (e) {\n        reject(e);\n      }\n    });\n  };\n  shortcut.replaceKernel = function(replacementKernel) {\n    kernel = replacementKernel;\n    bindKernelToShortcut(kernel, shortcut);\n  };\n\n  bindKernelToShortcut(kernel, shortcut);\n  return shortcut;\n}\n\nfunction bindKernelToShortcut(kernel, shortcut) {\n  if (shortcut.kernel) {\n    shortcut.kernel = kernel;\n    return;\n  }\n  const properties = utils.allPropertiesOf(kernel);\n  for (let i = 0; i < properties.length; i++) {\n    const property = properties[i];\n    if (property[0] === '_' && property[1] === '_') continue;\n    if (typeof kernel[property] === 'function') {\n      if (property.substring(0, 3) === 'add' || property.substring(0, 3) === 'set') {\n        shortcut[property] = function() {\n          shortcut.kernel[property].apply(shortcut.kernel, arguments);\n          return shortcut;\n        };\n      } else {\n        shortcut[property] = function() {\n          return shortcut.kernel[property].apply(shortcut.kernel, arguments);\n        };\n      }\n    } else {\n      shortcut.__defineGetter__(property, () => shortcut.kernel[property]);\n      shortcut.__defineSetter__(property, (value) => {\n        shortcut.kernel[property] = value;\n      });\n    }\n  }\n  shortcut.kernel = kernel;\n}\nmodule.exports = {\n  kernelRunShortcut\n};\n},{\"./utils\":114}],112:[function(require,module,exports){\nconst source = `// https://www.shadertoy.com/view/4t2SDh\n//note: uniformly distributed, normalized rand, [0,1]\nhighp float randomSeedShift = 1.0;\nhighp float slide = 1.0;\nuniform highp float randomSeed1;\nuniform highp float randomSeed2;\n\nhighp float nrand(highp vec2 n) {\n  highp float result = fract(sin(dot((n.xy + 1.0) * vec2(randomSeed1 * slide, randomSeed2 * randomSeedShift), vec2(12.9898, 78.233))) * 43758.5453);\n  randomSeedShift = result;\n  if (randomSeedShift > 0.5) {\n    slide += 0.00009; \n  } else {\n    slide += 0.0009;\n  }\n  return result;\n}`;\n\nconst name = 'math-random-uniformly-distributed';\n\nconst functionMatch = `Math.random()`;\n\nconst functionReplace = `nrand(vTexCoord)`;\n\nconst functionReturnType = 'Number';\nconst onBeforeRun = (kernel) => {\n  kernel.setUniform1f('randomSeed1', Math.random());\n  kernel.setUniform1f('randomSeed2', Math.random());\n};\n\nconst plugin = {\n  name,\n  onBeforeRun,\n  functionMatch,\n  functionReplace,\n  functionReturnType,\n  source\n};\n\nmodule.exports = plugin;\n},{}],113:[function(require,module,exports){\nclass Texture {\n  constructor(settings) {\n    const {\n      texture,\n      size,\n      dimensions,\n      output,\n      context,\n      type = 'NumberTexture',\n      kernel,\n      internalFormat,\n      textureFormat\n    } = settings;\n    if (!output) throw new Error('settings property \"output\" required.');\n    if (!context) throw new Error('settings property \"context\" required.');\n    if (!texture) throw new Error('settings property \"texture\" required.');\n    if (!kernel) throw new Error('settings property \"kernel\" required.');\n    this.texture = texture;\n    if (texture._refs) {\n      texture._refs++;\n    } else {\n      texture._refs = 1;\n    }\n    this.size = size;\n    this.dimensions = dimensions;\n    this.output = output;\n    this.context = context;\n    this.kernel = kernel;\n    this.type = type;\n    this._deleted = false;\n    this.internalFormat = internalFormat;\n    this.textureFormat = textureFormat;\n  }\n\n  toArray() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  clone() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  delete() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  clear() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n}\n\nmodule.exports = {\n  Texture\n};\n},{}],114:[function(require,module,exports){\nconst acorn = require('acorn');\nconst { Input } = require('./input');\nconst { Texture } = require('./texture');\n\nconst FUNCTION_NAME = /function ([^(]*)/;\nconst STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/mg;\nconst ARGUMENT_NAMES = /([^\\s,]+)/g;\n\nconst utils = {\n  systemEndianness() {\n    return _systemEndianness;\n  },\n  getSystemEndianness() {\n    const b = new ArrayBuffer(4);\n    const a = new Uint32Array(b);\n    const c = new Uint8Array(b);\n    a[0] = 0xdeadbeef;\n    if (c[0] === 0xef) return 'LE';\n    if (c[0] === 0xde) return 'BE';\n    throw new Error('unknown endianness');\n  },\n\n  isFunction(funcObj) {\n    return typeof(funcObj) === 'function';\n  },\n\n  isFunctionString(fn) {\n    if (typeof fn === 'string') {\n      return (fn\n        .slice(0, 'function'.length)\n        .toLowerCase() === 'function');\n    }\n    return false;\n  },\n\n  getFunctionNameFromString(funcStr) {\n    const result = FUNCTION_NAME.exec(funcStr);\n    if (!result || result.length === 0) return null;\n    return result[1].trim();\n  },\n\n  getFunctionBodyFromString(funcStr) {\n    return funcStr.substring(funcStr.indexOf('{') + 1, funcStr.lastIndexOf('}'));\n  },\n\n  getArgumentNamesFromString(fn) {\n    const fnStr = fn.replace(STRIP_COMMENTS, '');\n    let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);\n    if (result === null) {\n      result = [];\n    }\n    return result;\n  },\n\n  clone(obj) {\n    if (obj === null || typeof obj !== 'object' || obj.hasOwnProperty('isActiveClone')) return obj;\n\n    const temp = obj.constructor(); \n\n    for (let key in obj) {\n      if (Object.prototype.hasOwnProperty.call(obj, key)) {\n        obj.isActiveClone = null;\n        temp[key] = utils.clone(obj[key]);\n        delete obj.isActiveClone;\n      }\n    }\n\n    return temp;\n  },\n\n  isArray(array) {\n    return !isNaN(array.length);\n  },\n\n  getVariableType(value, strictIntegers) {\n    if (utils.isArray(value)) {\n      if (value.length > 0 && value[0].nodeName === 'IMG') {\n        return 'HTMLImageArray';\n      }\n      return 'Array';\n    }\n\n    switch (value.constructor) {\n      case Boolean:\n        return 'Boolean';\n      case Number:\n        if (strictIntegers && Number.isInteger(value)) {\n          return 'Integer';\n        }\n        return 'Float';\n      case Texture:\n        return value.type;\n      case Input:\n        return 'Input';\n    }\n    if ('nodeName' in value) {\n      switch (value.nodeName) {\n        case 'IMG':\n          return 'HTMLImage';\n        case 'CANVAS':\n          return 'HTMLImage';\n        case 'VIDEO':\n          return 'HTMLVideo';\n      }\n    } else if (value.hasOwnProperty('type')) {\n      return value.type;\n    } else if (typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas) {\n      return 'OffscreenCanvas';\n    } else if (typeof ImageBitmap !== 'undefined' && value instanceof ImageBitmap) {\n      return 'ImageBitmap';\n    } else if (typeof ImageData !== 'undefined' && value instanceof ImageData) {\n      return 'ImageData';\n    }\n    return 'Unknown';\n  },\n\n  getKernelTextureSize(settings, dimensions) {\n    let [w, h, d] = dimensions;\n    let texelCount = (w || 1) * (h || 1) * (d || 1);\n\n    if (settings.optimizeFloatMemory && settings.precision === 'single') {\n      w = texelCount = Math.ceil(texelCount / 4);\n    }\n    if (h > 1 && w * h === texelCount) {\n      return new Int32Array([w, h]);\n    }\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  closestSquareDimensions(length) {\n    const sqrt = Math.sqrt(length);\n    let high = Math.ceil(sqrt);\n    let low = Math.floor(sqrt);\n    while (high * low < length) {\n      high--;\n      low = Math.ceil(length / high);\n    }\n    return new Int32Array([low, Math.ceil(length / low)]);\n  },\n\n  getMemoryOptimizedFloatTextureSize(dimensions, bitRatio) {\n    const totalArea = utils.roundTo((dimensions[0] || 1) * (dimensions[1] || 1) * (dimensions[2] || 1) * (dimensions[3] || 1), 4);\n    const texelCount = totalArea / bitRatio;\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  getMemoryOptimizedPackedTextureSize(dimensions, bitRatio) {\n    const [w, h, d] = dimensions;\n    const totalArea = utils.roundTo((w || 1) * (h || 1) * (d || 1), 4);\n    const texelCount = totalArea / (4 / bitRatio);\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  roundTo(n, d) {\n    return Math.floor((n + d - 1) / d) * d;\n  },\n  getDimensions(x, pad) {\n    let ret;\n    if (utils.isArray(x)) {\n      const dim = [];\n      let temp = x;\n      while (utils.isArray(temp)) {\n        dim.push(temp.length);\n        temp = temp[0];\n      }\n      ret = dim.reverse();\n    } else if (x instanceof Texture) {\n      ret = x.output;\n    } else if (x instanceof Input) {\n      ret = x.size;\n    } else {\n      throw new Error(`Unknown dimensions of ${x}`);\n    }\n\n    if (pad) {\n      ret = Array.from(ret);\n      while (ret.length < 3) {\n        ret.push(1);\n      }\n    }\n\n    return new Int32Array(ret);\n  },\n\n  flatten2dArrayTo(array, target) {\n    let offset = 0;\n    for (let y = 0; y < array.length; y++) {\n      target.set(array[y], offset);\n      offset += array[y].length;\n    }\n  },\n\n  flatten3dArrayTo(array, target) {\n    let offset = 0;\n    for (let z = 0; z < array.length; z++) {\n      for (let y = 0; y < array[z].length; y++) {\n        target.set(array[z][y], offset);\n        offset += array[z][y].length;\n      }\n    }\n  },\n\n  flatten4dArrayTo(array, target) {\n    let offset = 0;\n    for (let l = 0; l < array.length; l++) {\n      for (let z = 0; z < array[l].length; z++) {\n        for (let y = 0; y < array[l][z].length; y++) {\n          target.set(array[l][z][y], offset);\n          offset += array[l][z][y].length;\n        }\n      }\n    }\n  },\n\n  flattenTo(array, target) {\n    if (utils.isArray(array[0])) {\n      if (utils.isArray(array[0][0])) {\n        if (utils.isArray(array[0][0][0])) {\n          utils.flatten4dArrayTo(array, target);\n        } else {\n          utils.flatten3dArrayTo(array, target);\n        }\n      } else {\n        utils.flatten2dArrayTo(array, target);\n      }\n    } else {\n      target.set(array);\n    }\n  },\n\n  splitArray(array, part) {\n    const result = [];\n    for (let i = 0; i < array.length; i += part) {\n      result.push(new array.constructor(array.buffer, i * 4 + array.byteOffset, part));\n    }\n    return result;\n  },\n\n  getAstString(source, ast) {\n    const lines = Array.isArray(source) ? source : source.split(/\\r?\\n/g);\n    const start = ast.loc.start;\n    const end = ast.loc.end;\n    const result = [];\n    if (start.line === end.line) {\n      result.push(lines[start.line - 1].substring(start.column, end.column));\n    } else {\n      result.push(lines[start.line - 1].slice(start.column));\n      for (let i = start.line; i < end.line; i++) {\n        result.push(lines[i]);\n      }\n      result.push(lines[end.line - 1].slice(0, end.column));\n    }\n    return result.join('\\n');\n  },\n\n  allPropertiesOf(obj) {\n    const props = [];\n\n    do {\n      props.push.apply(props, Object.getOwnPropertyNames(obj));\n    } while (obj = Object.getPrototypeOf(obj));\n\n    return props;\n  },\n\n  linesToString(lines) {\n    if (lines.length > 0) {\n      return lines.join(';\\n') + ';\\n';\n    } else {\n      return '\\n';\n    }\n  },\n  warnDeprecated(type, oldName, newName) {\n    if (newName) {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been replaced with \"${ newName }\". Fixing, but please upgrade as it will soon be removed.`);\n    } else {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been removed. Fixing, but please upgrade as it will soon be removed.`);\n    }\n  },\n  flipPixels: (pixels, width, height) => {\n    const halfHeight = height / 2 | 0; \n    const bytesPerRow = width * 4;\n    const temp = new Uint8ClampedArray(width * 4);\n    const result = pixels.slice(0);\n    for (let y = 0; y < halfHeight; ++y) {\n      const topOffset = y * bytesPerRow;\n      const bottomOffset = (height - y - 1) * bytesPerRow;\n\n      temp.set(result.subarray(topOffset, topOffset + bytesPerRow));\n\n      result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);\n\n      result.set(temp, bottomOffset);\n    }\n    return result;\n  },\n  erectPackedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erect2DPackedFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xStart = y * width;\n      const xEnd = xStart + width;\n      yResults[y] = array.subarray(xStart, xEnd);\n    }\n    return yResults;\n  },\n  erect3DPackedFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xStart = (z * height * width) + y * width;\n        const xEnd = xStart + width;\n        yResults[y] = array.subarray(xStart, xEnd);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectMemoryOptimizedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erectMemoryOptimized2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const offset = y * width;\n      yResults[y] = array.subarray(offset, offset + width);\n    }\n    return yResults;\n  },\n  erectMemoryOptimized3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const offset = (z * height * width) + (y * width);\n        yResults[y] = array.subarray(offset, offset + width);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectFloat: (array, width) => {\n    const xResults = new Float32Array(width);\n    let i = 0;\n    for (let x = 0; x < width; x++) {\n      xResults[x] = array[i];\n      i += 4;\n    }\n    return xResults;\n  },\n  erect2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    let i = 0;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Float32Array(width);\n      for (let x = 0; x < width; x++) {\n        xResults[x] = array[i];\n        i += 4;\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    let i = 0;\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Float32Array(width);\n        for (let x = 0; x < width; x++) {\n          xResults[x] = array[i];\n          i += 4;\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray2: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 2);\n    }\n    return xResults;\n  },\n  erect2DArray2: (array, width, height) => {\n    const yResults = new Array(height);\n    const XResultsMax = width * 4;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * XResultsMax;\n      let i = 0;\n      for (let x = 0; x < XResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 2);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray2: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 2);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray3: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 3);\n    }\n    return xResults;\n  },\n  erect2DArray3: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 3);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray3: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 3);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray4: (array, width) => {\n    const xResults = new Array(array);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 4);\n    }\n    return xResults;\n  },\n  erect2DArray4: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 4);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray4: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 4);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n\n  flattenFunctionToString: (source, settings) => {\n    const { findDependency, thisLookup, doNotDefine } = settings;\n    let flattened = settings.flattened;\n    if (!flattened) {\n      flattened = settings.flattened = {};\n    }\n    const ast = acorn.parse(source);\n    const functionDependencies = [];\n    let indent = 0;\n\n    function flatten(ast) {\n      if (Array.isArray(ast)) {\n        const results = [];\n        for (let i = 0; i < ast.length; i++) {\n          results.push(flatten(ast[i]));\n        }\n        return results.join('');\n      }\n      switch (ast.type) {\n        case 'Program':\n          return flatten(ast.body) + (ast.body[0].type === 'VariableDeclaration' ? ';' : '');\n        case 'FunctionDeclaration':\n          return `function ${ast.id.name}(${ast.params.map(flatten).join(', ')}) ${ flatten(ast.body) }`;\n        case 'BlockStatement': {\n          const result = [];\n          indent += 2;\n          for (let i = 0; i < ast.body.length; i++) {\n            const flat = flatten(ast.body[i]);\n            if (flat) {\n              result.push(' '.repeat(indent) + flat, ';\\n');\n            }\n          }\n          indent -= 2;\n          return `{\\n${result.join('')}}`;\n        }\n        case 'VariableDeclaration':\n          const declarations = utils.normalizeDeclarations(ast)\n            .map(flatten)\n            .filter(r => r !== null);\n          if (declarations.length < 1) {\n            return '';\n          } else {\n            return `${ast.kind} ${declarations.join(',')}`;\n          }\n          case 'VariableDeclarator':\n            if (ast.init.object && ast.init.object.type === 'ThisExpression') {\n              const lookup = thisLookup(ast.init.property.name, true);\n              if (lookup) {\n                return `${ast.id.name} = ${flatten(ast.init)}`;\n              } else {\n                return null;\n              }\n            } else {\n              return `${ast.id.name} = ${flatten(ast.init)}`;\n            }\n            case 'CallExpression': {\n              if (ast.callee.property.name === 'subarray') {\n                return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              }\n              if (ast.callee.object.name === 'gl' || ast.callee.object.name === 'context') {\n                return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              }\n              if (ast.callee.object.type === 'ThisExpression') {\n                functionDependencies.push(findDependency('this', ast.callee.property.name));\n                return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              } else if (ast.callee.object.name) {\n                const foundSource = findDependency(ast.callee.object.name, ast.callee.property.name);\n                if (foundSource === null) {\n                  return `${ast.callee.object.name}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n                } else {\n                  functionDependencies.push(foundSource);\n                  return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n                }\n              } else if (ast.callee.object.type === 'MemberExpression') {\n                return `${flatten(ast.callee.object)}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              } else {\n                throw new Error('unknown ast.callee');\n              }\n            }\n            case 'ReturnStatement':\n              return `return ${flatten(ast.argument)}`;\n            case 'BinaryExpression':\n              return `(${flatten(ast.left)}${ast.operator}${flatten(ast.right)})`;\n            case 'UnaryExpression':\n              if (ast.prefix) {\n                return `${ast.operator} ${flatten(ast.argument)}`;\n              } else {\n                return `${flatten(ast.argument)} ${ast.operator}`;\n              }\n              case 'ExpressionStatement':\n                return `${flatten(ast.expression)}`;\n              case 'SequenceExpression':\n                return `(${flatten(ast.expressions)})`;\n              case 'ArrowFunctionExpression':\n                return `(${ast.params.map(flatten).join(', ')}) => ${flatten(ast.body)}`;\n              case 'Literal':\n                return ast.raw;\n              case 'Identifier':\n                return ast.name;\n              case 'MemberExpression':\n                if (ast.object.type === 'ThisExpression') {\n                  return thisLookup(ast.property.name);\n                }\n                if (ast.computed) {\n                  return `${flatten(ast.object)}[${flatten(ast.property)}]`;\n                }\n                return flatten(ast.object) + '.' + flatten(ast.property);\n              case 'ThisExpression':\n                return 'this';\n              case 'NewExpression':\n                return `new ${flatten(ast.callee)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n              case 'ForStatement':\n                return `for (${flatten(ast.init)};${flatten(ast.test)};${flatten(ast.update)}) ${flatten(ast.body)}`;\n              case 'AssignmentExpression':\n                return `${flatten(ast.left)}${ast.operator}${flatten(ast.right)}`;\n              case 'UpdateExpression':\n                return `${flatten(ast.argument)}${ast.operator}`;\n              case 'IfStatement':\n                return `if (${flatten(ast.test)}) ${flatten(ast.consequent)}`;\n              case 'ThrowStatement':\n                return `throw ${flatten(ast.argument)}`;\n              case 'ObjectPattern':\n                return ast.properties.map(flatten).join(', ');\n              case 'ArrayPattern':\n                return ast.elements.map(flatten).join(', ');\n              case 'DebuggerStatement':\n                return 'debugger;';\n              case 'ConditionalExpression':\n                return `${flatten(ast.test)}?${flatten(ast.consequent)}:${flatten(ast.alternate)}`;\n              case 'Property':\n                if (ast.kind === 'init') {\n                  return flatten(ast.key);\n                }\n      }\n      throw new Error(`unhandled ast.type of ${ ast.type }`);\n    }\n    const result = flatten(ast);\n    if (functionDependencies.length > 0) {\n      const flattenedFunctionDependencies = [];\n      for (let i = 0; i < functionDependencies.length; i++) {\n        const functionDependency = functionDependencies[i];\n        if (!flattened[functionDependency]) {\n          flattened[functionDependency] = true;\n        }\n        functionDependency ? flattenedFunctionDependencies.push(utils.flattenFunctionToString(functionDependency, settings) + '\\n') : '';\n      }\n      return flattenedFunctionDependencies.join('') + result;\n    }\n    return result;\n  },\n\n  normalizeDeclarations: (ast) => {\n    if (ast.type !== 'VariableDeclaration') throw new Error('Ast is not of type \"VariableDeclaration\"');\n    const normalizedDeclarations = [];\n    for (let declarationIndex = 0; declarationIndex < ast.declarations.length; declarationIndex++) {\n      const declaration = ast.declarations[declarationIndex];\n      if (declaration.id && declaration.id.type === 'ObjectPattern' && declaration.id.properties) {\n        const { properties } = declaration.id;\n        for (let propertyIndex = 0; propertyIndex < properties.length; propertyIndex++) {\n          const property = properties[propertyIndex];\n          if (property.value.type === 'ObjectPattern' && property.value.properties) {\n            for (let subPropertyIndex = 0; subPropertyIndex < property.value.properties.length; subPropertyIndex++) {\n              const subProperty = property.value.properties[subPropertyIndex];\n              if (subProperty.type === 'Property') {\n                normalizedDeclarations.push({\n                  type: 'VariableDeclarator',\n                  id: {\n                    type: 'Identifier',\n                    name: subProperty.key.name\n                  },\n                  init: {\n                    type: 'MemberExpression',\n                    object: {\n                      type: 'MemberExpression',\n                      object: declaration.init,\n                      property: {\n                        type: 'Identifier',\n                        name: property.key.name\n                      },\n                      computed: false\n                    },\n                    property: {\n                      type: 'Identifier',\n                      name: subProperty.key.name\n                    },\n                    computed: false\n                  }\n                });\n              } else {\n                throw new Error('unexpected state');\n              }\n            }\n          } else if (property.value.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: property.value && property.value.name ? property.value.name : property.key.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Identifier',\n                  name: property.key.name\n                },\n                computed: false\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else if (declaration.id && declaration.id.type === 'ArrayPattern' && declaration.id.elements) {\n        const { elements } = declaration.id;\n        for (let elementIndex = 0; elementIndex < elements.length; elementIndex++) {\n          const element = elements[elementIndex];\n          if (element.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: element.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Literal',\n                  value: elementIndex,\n                  raw: elementIndex.toString(),\n                  start: element.start,\n                  end: element.end\n                },\n                computed: true\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else {\n        normalizedDeclarations.push(declaration);\n      }\n    }\n    return normalizedDeclarations;\n  },\n\n  splitHTMLImageToRGB: (gpu, image) => {\n    const rKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.r * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const gKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.g * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const bKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.b * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const aKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.a * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const result = [\n      rKernel(image),\n      gKernel(image),\n      bKernel(image),\n      aKernel(image),\n    ];\n    result.rKernel = rKernel;\n    result.gKernel = gKernel;\n    result.bKernel = bKernel;\n    result.aKernel = aKernel;\n    result.gpu = gpu;\n    return result;\n  },\n\n  splitRGBAToCanvases: (gpu, rgba, width, height) => {\n    const visualKernelR = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(pixel.r / 255, 0, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelR(rgba);\n\n    const visualKernelG = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, pixel.g / 255, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelG(rgba);\n\n    const visualKernelB = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, 0, pixel.b / 255, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelB(rgba);\n\n    const visualKernelA = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(255, 255, 255, pixel.a / 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelA(rgba);\n    return [\n      visualKernelR.canvas,\n      visualKernelG.canvas,\n      visualKernelB.canvas,\n      visualKernelA.canvas,\n    ];\n  },\n\n  getMinifySafeName: (fn) => {\n    try {\n      const ast = acorn.parse(`const value = ${fn.toString()}`);\n      const { init } = ast.body[0].declarations[0];\n      return init.body.name || init.body.body[0].argument.name;\n    } catch (e) {\n      throw new Error('Unrecognized function type.  Please use `() => yourFunctionVariableHere` or function() { return yourFunctionVariableHere; }');\n    }\n  },\n  sanitizeName: function(name) {\n    if (dollarSign.test(name)) {\n      name = name.replace(dollarSign, 'S_S');\n    }\n    if (doubleUnderscore.test(name)) {\n      name = name.replace(doubleUnderscore, 'U_U');\n    } else if (singleUnderscore.test(name)) {\n      name = name.replace(singleUnderscore, 'u_u');\n    }\n    return name;\n  }\n};\n\nconst dollarSign = /\\$/;\nconst doubleUnderscore = /__/;\nconst singleUnderscore = /_/;\n\nconst _systemEndianness = utils.getSystemEndianness();\n\nmodule.exports = {\n  utils\n};\n},{\"./input\":110,\"./texture\":113,\"acorn\":1}]},{},[107])(107)\n});\n"
  },
  {
    "path": "examples/advanced-typescript.ts",
    "content": "/**\n * This is an arbitrary example (overly complex with types for overly simplified kernel) to show type inheritance\n * throughout the kernel's usage.\n *\n * The whole idea here is that you can define custom:\n * - `constants`\n * - `this` context\n * - mapped kernels\n * - arguments\n * - kernel output\n */\n\nimport {\n  GPU,\n  Texture,\n  IKernelFunctionThis,\n  IConstantsThis,\n  KernelOutput,\n  ISubKernelsResults\n} from '../src';\n\nconst gpu = new GPU();\n\ninterface IConstants extends IConstantsThis {\n  rotation: number,\n}\n\ninterface IThis extends IKernelFunctionThis {\n  constants: IConstants,\n}\n\nfunction kernelFunction(this: IThis, degrees: number, divisors: [number, number]): [number, number] {\n  const bounds = subKernel(this.constants.rotation * degrees);\n  return [bounds[0] / divisors[0], bounds[1] / divisors[1]];\n}\n\nfunction subKernel(value: number): [number, number] {\n  return [-value, value];\n}\n\ninterface IKernelMapResult extends ISubKernelsResults {\n  test: KernelOutput;\n}\n\nconst kernelMap = gpu.createKernelMap<Parameters<typeof kernelFunction>>({\n  test: subKernel,\n}, kernelFunction)\n  .setConstants<IConstants>({\n    rotation: 45,\n  })\n  .setOutput([1])\n  .setPrecision('single')\n  .setPipeline(true)\n  .setImmutable(true);\n\nconst { test, result } = kernelMap(360, [256, 512]);\nconst testTexture = test as Texture;\nconst resultTexture = result as Texture;\n\nconsole.log(testTexture.toArray() as [number, number][]);\nconsole.log(resultTexture.toArray() as [number, number][]);\n\ntestTexture.delete();\nresultTexture.delete();\n\nkernelMap.destroy();\n"
  },
  {
    "path": "examples/cat-image/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Cat image with GPU.js</title>\n  <script src=\"../../dist/gpu-browser.min.js\"></script>\n</head>\n<body>\n<h1>Image to GPU.js from <a href=\"https://observablehq.com/@fil/image-to-gpu\">https://observablehq.com/@fil/image-to-gpu</a></h1>\n<div id=\"log-fps\"></div>\n</body>\n<script>\n  const gpu = new GPU();\n  function imageToArray(image) {\n    const kernel = gpu.createKernel(function(image) {\n      const pixel = image[this.thread.y][this.thread.x];\n      this.color(pixel.r, pixel.g, pixel.b, pixel.a);\n    }, {\n      output: [image.width, image.height],\n      graphical: true,\n      pipeline: true,\n    });\n    kernel(image);\n    const result = kernel.getPixels(true);\n    kernel.destroy();\n    return result;\n  }\n  const kernel = function(data, wobble) {\n    let x = this.thread.x,\n      y = this.thread.y;\n\n    //var data = this.constants.data;\n    // wouldn't be fun if the kernel did _nothing_\n    x = Math.floor(x + wobble * Math.sin(y / 10));\n    y = Math.floor(y + wobble * Math.cos(x / 10));\n\n    const n = 4 * (x + (this.constants.w * y));\n    this.color(data[n]/256, data[n+1]/256,data[n+2]/256,1);\n  };\n\n  const logFps = document.querySelector('#log-fps');\n  const image = new Image();\n  image.src = './cat.jpg';\n  image.onload = () => {\n    const array = imageToArray(image);\n    const render = (new GPU({mode: \"gpu\"}))\n      .createKernel(kernel)\n      .setConstants({ w: image.width, h: image.height })\n      .setOutput([image.width, image.height])\n      .setGraphical(true);\n    const canvas = render.canvas;\n    document.body.appendChild(canvas);\n    let lastCalledTime;\n    let fps;\n    function callRender() {\n      const wobble = 14 * Math.sin(Date.now() / 400);\n      render(array, wobble);\n      const delta = (Date.now() - lastCalledTime)/1000;\n      lastCalledTime = Date.now();\n      fps = 1 / delta;\n      logFps.innerHTML = fps.toFixed(0) + ' FPS';\n      window.requestAnimationFrame(() => {\n        callRender();\n      });\n    }\n    callRender();\n  };\n</script>\n</html>\n"
  },
  {
    "path": "examples/fluid.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>SSPS - Chadams Studios (slightly upgraded)</title>\n  <script src=\"../dist/gpu-browser.min.js\"></script>\n  <script>\n    function XSSPS(particleCount, renderSize, onAfterInput) {\n      this.dt = 0;\n      this.renderSize = renderSize || 512;\n      this.particleCount = particleCount || 512;\n      this.onAfterInput = onAfterInput;\n\n      this.hashLength = 128;\n      this.hashWSize = 3.0;\n      this.hashBinLen = 128;\n      this.rayCount = 128;\n      this.rayDist = 20;\n      this.rfScale = 2.0;\n\n      this.restDensity = 0.1;\n      this.fieldLen = 4;\n      this.gConst = 0.02;\n      this.bound = 8;\n\n      this.shootIndex = 0;\n      this.keys = {};\n\n      this.canvas = document.createElement('canvas');\n      const gpu = this.gpu = new GPU({\n        canvas: this.canvas,\n        mode: 'gpu'\n      });\n\n      /*\n          RENDER SUPPORT FUNCTIONS (GLSL)\n       */\n\n      gpu.addNativeFunction('raySphere',\n        `vec2 raySphere(vec3 r0, vec3 rd, vec3 s0, float sr) {\n          float a = dot(rd, rd);\n          vec3 s0_r0 = r0 - s0;\n          float b = 2.0 * dot(rd, s0_r0);\n          float c = dot(s0_r0, s0_r0) - (sr * sr);\n          float test = b*b - 4.0*a*c;\n          if (test < 0.0) {\n              return vec2(-1.0, 0.);\n          }\n          test = sqrt(test);\n          float X = (-b - test)/(2.0*a);\n          float Y = (-b + test)/(2.0*a);\n          return vec2(\n              X,\n              Y-X\n          );\n        }`\n      );\n\n      gpu.addNativeFunction('getRay0',\n        `vec3 getRay0(vec2 uv,  vec3 c0, vec3 cd, vec3 up, float s0, float s1, float slen) {\n          up = normalize(cross(cross(cd, up), cd));\n\n          vec3 vup = normalize(cross(cd, up));\n          vec3 vleft = up;\n\n          vec2 X = (uv - vec2(0.5, 0.5)) * s0;\n\n          return c0 + vup * X.y + vleft * X.x;\n        }`\n      );\n\n      gpu.addNativeFunction('getRayDir',\n        `vec3 getRayDir(vec3 R0,  vec2 uv,  vec3 c0, vec3 cd, vec3 up, float s0, float s1, float slen) {\n          up = normalize(cross(cross(cd, up), cd));\n\n          vec3 vup = normalize(cross(cd, up));\n          vec3 vleft = up;\n\n          vec2 X = (uv - vec2(0.5, 0.5)) * s1;\n\n          vec3 far = vup * X.y + vleft * X.x;\n\n          return normalize((normalize(cd) * slen + far + c0) - R0);\n        }`\n      );\n\n      gpu.addNativeFunction('reflectWrap',\n        `vec3 reflectWrap(vec3 I, vec3 N) {\n          return normalize(reflect(normalize(I), normalize(N)));\n\n        }`\n      );\n\n      gpu.addNativeFunction('refractWrap',\n        `vec3 refractWrap(vec3 I, vec3 N, float eta) {\n          return normalize(refract(normalize(I), normalize(N), eta));\n\n        }`\n      );\n\n      gpu.addNativeFunction('distanceWrap',\n        `float distanceWrap(vec3 A, vec3 B) {\n          return length(A - B);\n        }`\n      );\n\n      /*\n          UPDATE SUPPORT FUNCTIONS (GLSL)\n       */\n\n      gpu.addNativeFunction('compGravity',\n        `vec3 compGravity(vec3 M, vec3 J, float jMass, float f) {\n          vec3 delta = J - M;\n          float dlen = length(delta);\n\n          if (dlen > 0.05) {\n              float dlen2 = jMass / (max(dlen*dlen, 1.) * 0.1);\n              return dlen2 * (delta / dlen) * f;\n          }\n          return vec3(0., 0., 0.);\n        }`\n      );\n\n      gpu.addNativeFunction('partDensity',\n        `vec2 partDensity(vec3 M, vec3 J, float fLen) {\n          vec3 delta = J - M;\n          float len = length(delta);\n\n          if (len < fLen) {\n              float t = 1. - (len / fLen);\n              return vec2(t*t, t*t*t);\n          }\n          return vec2(0., 0.);\n        }`\n      );\n\n      gpu.addNativeFunction('pressForce',\n        `vec3 pressForce(vec3 M, vec3 J, vec3 Mv, vec3 Jv, float fLen, float dt, float spressure, float snpressure, float viscdt) {\n          vec3 delta = J - M;\n          float len = length(delta);\n          if (len < fLen) {\n              float t = 1. - (len / fLen);\n              delta *= dt * t * (spressure + snpressure * t) / (2. * len);\n              vec3 deltaV = Jv - Mv;\n              deltaV *= dt * t * viscdt;\n              return -(delta - deltaV);\n          }\n          return vec3(0., 0., 0.);\n        }`\n      );\n\n      /*\n          INIT SUPPORT FUNCTIONS\n       */\n\n      gpu.addNativeFunction('random',\n        `float random(float sequence, float seed) {\n          return fract(sin(dot(vec2(seed, sequence), vec2(12.9898, 78.233))) * 43758.5453);\n        }`\n      );\n\n      /*\n          VELOCITY UPDATE KERNEL\n       */\n\n      this.updateVelocity = gpu.createKernel(function(\n        positions,\n        velocities,\n        attrs,\n        pullMass,\n        camP,\n        camDir,\n        moveTPullR,\n        oDensity,\n        oVisc,\n        oMass,\n        oIncomp,\n        dt\n      ) {\n        var playerPos = [\n          camP[0] + camDir[0] * moveTPullR,\n          camP[1] + camDir[1] * moveTPullR,\n          camP[2] + camDir[2] * moveTPullR,\n        ];\n        // Unpack particle\n        var comp = this.thread.x % 3;\n        var me = (this.thread.x - comp) / 3;\n        var mx = positions[me*3],\n          my = positions[me*3+1],\n          mz = positions[me*3+2];\n        var mpos = [mx, my, mz];\n        var mvx = velocities[me*3],\n          mvy = velocities[me*3+1],\n          mvz = velocities[me*3+2];\n        var mvel = [mvx, mvy, mvz];\n        var mmass = oMass;\n        var incomp = oIncomp;\n        var viscdt = oVisc;\n        var ddensity = 0.0,\n          nddensity = 0.0;\n\n        // Compute pressure on this particle\n        for (var i=0; i<this.constants.particleCount; i++) {\n          var opos = [positions[i*3], positions[i*3+1], positions[i*3+2]];\n          var density = oDensity;\n          var dret = partDensity(mpos, opos, this.constants.fieldLen);\n          ddensity += dret[0] * density;\n          nddensity += dret[1] * density;\n        }\n\n        // Interact with player by adding pressure\n        var opos = [playerPos[0], playerPos[1], playerPos[2]];\n        var fl2 = this.constants.fieldLen;\n        var pf = [0, 0];\n        if (pullMass > 0.0) {\n          pf = partDensity(mpos, opos, fl2);\n        }\n        ddensity += pf[0] * (pullMass / 10.0 * this.constants.restDensity);\n        nddensity += pf[1] * (pullMass / 10.0 * this.constants.restDensity);\n\n        var spressure = (ddensity - this.constants.restDensity) * incomp;\n        var snpressure = nddensity * incomp;\n\n        // Compute force from pressure on this particle\n        var ret = [mvx, mvy, mvz];\n\n        for (var i=0; i<this.constants.particleCount; i++) {\n          if (i - me > 0.01) {\n            var opos = [positions[i*3], positions[i*3+1], positions[i*3+2]];\n            var ovel = [velocities[i*3], velocities[i*3+1], velocities[i*3+2]];\n            var mass = oMass;\n            var jmf = (2. * mass) / (mass + mmass);\n            var dret = pressForce(mpos, opos, mvel, ovel, this.constants.fieldLen, dt, spressure, snpressure, viscdt);\n            ret[0] += dret[0] * jmf; ret[1] += dret[1] * jmf; ret[2] += dret[2] * jmf;\n          }\n        }\n\n        // Player pressure\n        if (pullMass > 0.0) {\n          var opos = [playerPos[0], playerPos[1], playerPos[2]];\n          var ovel = [0, 0, 0];\n          var mass = 1.0;\n          var jmf = (2. * mass) / (mass + mmass);\n          var dret = pressForce(mpos, opos, mvel, ovel, this.constants.fieldLen, dt, spressure, snpressure, viscdt);\n          ret[0] += dret[0] * jmf;\n          ret[1] += dret[1] * jmf;\n          ret[2] += dret[2] * jmf;\n        }\n\n        // Compute gravitational force on this particle\n        var gf = this.constants.gConst * dt * 0.1;\n        for (var i=0; i<this.constants.particleCount; i++) {\n          var opos = [positions[i*3], positions[i*3+1], positions[i*3+2]];\n          var mass = attrs[i*6+2];\n          var dret = compGravity(mpos, opos, mass, gf);\n          ret[0] += dret[0]; ret[1] += dret[1]; ret[2] += dret[2];\n        }\n\n        // Enforce scene boundaries\n        if (ret[0] < 0 && mpos[0] < -this.constants.bound) {\n          ret[0] = -ret[0];\n        } else if (ret[0] > 0 && mpos[0] > this.constants.bound) {\n          ret[0] = -ret[0];\n        }\n\n        if (ret[1] < 0 && mpos[1] < -this.constants.bound) {\n          ret[1] = -ret[1];\n        } else if (ret[1] > 0 && mpos[1] > this.constants.bound) {\n          ret[1] = -ret[1];\n        }\n\n        if (ret[2] < 0 && mpos[2] < -this.constants.bound) {\n          ret[2] = -ret[2];\n        } else if (ret[2] > 0 && mpos[2] > this.constants.bound) {\n          ret[2] = -ret[2];\n        }\n\n        if (comp < 0.01) {\n          return ret[0];\n        } else if (comp < 1.01) {\n          return ret[1];\n        } else if (comp < 2.01) {\n          return ret[2];\n        }\n      }, {\n        constants: {\n          particleCount: this.particleCount,\n          bound: this.bound,\n          fieldLen: this.fieldLen,\n          gConst: this.gConst,\n          restDensity: ((4 / 3) * Math.PI * Math.pow(this.fieldLen, 3)) * this.restDensity\n        },\n        loopMaxIterations: this.particleCount,\n        output: [ this.particleCount*3 ],\n        pipeline: true,\n        tactic: 'speed',\n        immutable: true,\n      });\n\n      /*\n          SET POSITION/VELOCITES\n       */\n      this.setPosition = gpu.createKernel(function(list, index, camP, camDir, amount) {\n        if (Math.abs(index - Math.floor(this.thread.x / this.constants.pitch)) < 0.01) {\n          const i = this.thread.x % this.constants.pitch;\n          return camP[i] + camDir[i] * amount;\n        } else {\n          return list[this.thread.x];\n        }\n      }, {\n        output: [ this.particleCount * 3 ],\n        pipeline: true,\n        constants: { particleCount: this.particleCount, pitch: 3 },\n        loopMaxIterations: this.particleCount,\n        tactic: 'speed',\n        immutable: true,\n      });\n\n      this.setVelocity = gpu.createKernel(function(list, index, camDir, amount) {\n        if (Math.abs(index - Math.floor(this.thread.x / this.constants.pitch)) < 0.01) {\n          return camDir[this.thread.x % this.constants.pitch] * amount;\n        } else {\n          return list[this.thread.x];\n        }\n      }, {\n        output: [ this.particleCount * 3 ],\n        pipeline: true,\n        constants: { particleCount: this.particleCount, pitch: 3 },\n        loopMaxIterations: this.particleCount,\n        tactic: 'speed',\n        immutable: true,\n      });\n\n      /*\n          POSITION UPDATE KERNEL\n       */\n\n      this.updatePosition = gpu.createKernel(function(positions, velocities, dt) {\n        return positions[this.thread.x] + velocities[this.thread.x] * dt;\n      }, {\n        output: [ this.particleCount * 3 ],\n        pipeline: true,\n        loopMaxIterations: this.particleCount,\n        tactic: 'speed',\n        immutable: true,\n      });\n\n      this.rotateCamera = gpu.createKernel(function(rotLR, rotUD, camCenter, camDir, camUp, camNearWidth, camFarWidth, camDist) {\n        var uv = [0.5+rotLR, 0.5+rotUD];\n\n        var CC = [camCenter[0], camCenter[1], camCenter[2]],\n          CD = [camDir[0], camDir[1], camDir[2]],\n          CU = [camUp[0], camUp[1], camUp[2]];\n        var ray0 = getRay0(uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n        var rayDir = getRayDir(ray0, uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n\n        if (this.thread.x === 0) {\n          return rayDir[0];\n        } else if (this.thread.x === 1) {\n          return rayDir[1];\n        } else {\n          return rayDir[2];\n        }\n      }, {\n        output: [ 3 ],\n        tactic: 'speed',\n        pipeline: true,\n        immutable: true,\n      });\n\n      this.updateCameraPosition = gpu.createKernel(function(camDir, camP, moveTFR, dt) {\n        var dlen = Math.sqrt(\n          (camDir[0] * camDir[0])\n          + (camDir[1] * camDir[1])\n          + (camDir[2] * camDir[2])\n        );\n        return camP[this.thread.x] + (camDir[this.thread.x] / dlen) * moveTFR * dt * 6;\n      }, {\n        output: [3],\n        tactic: 'speed',\n        pipeline: true,\n        immutable: true,\n      });\n\n      /*\n          RENDER KERNEL\n       */\n\n      this.renderMode = 0;\n\n      this.renderKernel = [\n        gpu.createKernel(function(positions, camCenter, camDir, camUp, camNearWidth, camFarWidth, camDist) {\n\n          let uv = [this.thread.x / (this.constants.renderSize-1), this.thread.y / (this.constants.renderSize-1)];\n\n          let CC = [camCenter[0], camCenter[1], camCenter[2]],\n            CD = [camDir[0], camDir[1], camDir[2]],\n            CU = [camUp[0], camUp[1], camUp[2]];\n\n          let ray0 = getRay0(uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n          let rayDir = getRayDir(ray0, uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n\n          this.color(0., 0., 0., 1.);\n\n          let outClr = [\n            0., 0., 0.\n          ];\n\n          let ds = 0.0;\n          let done = 0.0;\n          let norm = [0., 0., 0.];\n          for (let i=0; i<this.constants.ICOUNT; i++) {\n            if (done < 0.5) {\n              let rnow = [\n                ray0[0] + rayDir[0] * ds,\n                ray0[1] + rayDir[1] * ds,\n                ray0[2] + rayDir[2] * ds\n              ];\n\n              norm[0] = 0.; norm[1] = 0.; norm[2] = 0.;\n\n              let m = 0.0;\n              let fq = 0.0;\n              let nt = 0.0;\n              let dmin = 1000.0;\n              for (let j=0; j<this.constants.particleCount; j++) {\n                let off = j * 3;\n                let dx  = positions[off], dy  = positions[off+1.], dz  = positions[off+2.];\n                dx -= rnow[0];        dy -= rnow[1];           dz -= rnow[2];\n                let r = Math.sqrt(dx*dx + dy*dy + dz*dz);\n                let x = r / this.constants.rfScale;\n                if (x < 1.0) {\n                  let q = 1.0 - x*x*x*(x*(x*6.0-15.0)+10.0);\n                  norm[0] += (dx/r) * q;\n                  norm[1] += (dy/r) * q;\n                  norm[2] += (dz/r) * q;\n                  fq += q;\n                  nt += q;\n                  m += 1.0;\n                }\n                else {\n                  dmin = Math.min(dmin, r - this.constants.rfScale);\n                }\n              }\n              let dist = dmin + 0.1;\n\n              norm[0] /= nt; norm[1] /= nt; norm[2] /= nt;\n\n              if (m > 0.5) {\n                dist = (0.5333 * this.constants.rfScale) * (0.5 - fq);\n              }\n\n              ds += dist;\n              if (dist < 0.01) {\n                done = 1.0;\n              }\n              if (ds >= this.constants.rayDist) {\n                done = 1.0;\n              }\n            }\n          }\n          ds = Math.min(ds, this.constants.rayDist);\n\n          let ref = refractWrap(norm, rayDir, 1./1.5);\n          let dot = ref[0] * rayDir[0] + ref[1] * rayDir[1] + ref[2] * rayDir[2];\n          let int = (1. - Math.pow(ds / this.constants.rayDist, 1.5));\n          let light = Math.min(1., Math.max(dot*dot, 0.)) * int;\n\n          outClr[2] = int + light;\n          outClr[1] = light;\n          outClr[0] = light;\n\n          this.color(Math.min(outClr[0], 1.), Math.min(outClr[1], 1.), Math.min(outClr[2], 1.), 1.);\n\n        }, {\n          graphical: true,\n\n          constants: {\n            rayDist: this.rayDist,\n            rfScale: this.rfScale,\n            bound: this.bound,\n            renderSize: this.renderSize,\n            particleCount: this.particleCount,\n            ICOUNT: 48\n          },\n          loopMaxIterations: 48 * this.particleCount,\n          output: [this.renderSize, this.renderSize],\n          tactic: 'speed',\n        }),\n        gpu.createKernel(function(positions, camCenter, camDir, camUp, camNearWidth, camFarWidth, camDist) {\n          let uv = [this.thread.x / (this.constants.renderSize-1), this.thread.y / (this.constants.renderSize-1)];\n\n          let CC = [camCenter[0], camCenter[1], camCenter[2]],\n            CD = [camDir[0], camDir[1], camDir[2]],\n            CU = [camUp[0], camUp[1], camUp[2]];\n\n          let ray0 = getRay0(uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n          let rayDir = getRayDir(ray0, uv, CC, CD, CU, camNearWidth, camFarWidth, camDist);\n\n          this.color(0., 0., 0., 1.);\n\n          let outClr = [\n            0., 0., 0.\n          ];\n\n          let ds = 0.0;\n          let done = 0.0;\n          let norm = [0., 0., 0.];\n          for (let i=0; i<this.constants.ICOUNT; i++) {\n            if (done < 0.5) {\n              let rnow = [\n                ray0[0] + rayDir[0] * ds,\n                ray0[1] + rayDir[1] * ds,\n                ray0[2] + rayDir[2] * ds\n              ];\n\n              norm[0] = 0.; norm[1] = 0.; norm[2] = 0.;\n\n              let m = 0.0;\n              let fq = 0.0;\n              let nt = 0.0;\n              let dmin = 1000.0;\n              for (let j=0; j<this.constants.particleCount; j++) {\n                let off = j * 3;\n                let dx  = positions[off], dy  = positions[off+1.], dz  = positions[off+2.];\n                dx -= rnow[0];        dy -= rnow[1];           dz -= rnow[2];\n                let r = Math.sqrt(dx*dx + dy*dy + dz*dz);\n                let x = r / this.constants.rfScale;\n                if (x < 1.0) {\n                  let q = 1.0 - x*x*x*(x*(x*6.0-15.0)+10.0);\n                  norm[0] += (dx/r) * q;\n                  norm[1] += (dy/r) * q;\n                  norm[2] += (dz/r) * q;\n                  fq += q;\n                  nt += q;\n                  m += 1.0;\n                }\n                else {\n                  dmin = Math.min(dmin, r - this.constants.rfScale);\n                }\n              }\n              let dist = dmin + 0.1;\n\n              norm[0] /= nt; norm[1] /= nt; norm[2] /= nt;\n\n              if (m > 0.5) {\n                dist = (0.5333 * this.constants.rfScale) * (0.5 - fq);\n              }\n\n              ds += dist;\n              if (dist < 0.01) {\n                done = 1.0;\n              }\n              if (ds >= this.constants.rayDist) {\n                done = 1.0;\n              }\n            }\n          }\n          ds = Math.min(ds, this.constants.rayDist);\n\n          let ref = refractWrap(norm, rayDir, 1./1.5);\n          let dot = ref[0] * rayDir[0] + ref[1] * rayDir[1] + ref[2] * rayDir[2];\n          let int = (1. - Math.pow(ds / this.constants.rayDist, 1.5));\n          let light = Math.min(1., Math.max(dot*dot, 0.)) * int;\n\n          outClr[2] = int + light;\n          outClr[1] = light;\n          outClr[0] = light;\n\n          if (ds < (this.constants.rayDist - 0.001)) {\n            ray0[0] += rayDir[0] * ds;\n            ray0[1] += rayDir[1] * ds;\n            ray0[2] += rayDir[2] * ds;\n            let ds = 0.00;\n            let done = 0.0;\n            let nrayDir = [-norm[0], -norm[1], -norm[2]];\n            nrayDir = reflectWrap(nrayDir, rayDir);\n            norm = [0., 0., 0.];\n            ray0[0] += nrayDir[0] * 0.01;\n            ray0[1] += nrayDir[1] * 0.01;\n            ray0[2] += nrayDir[2] * 0.01;\n            for (let i=0; i<this.constants.ICOUNTR; i++) {\n              if (done < 0.5) {\n                let rnow = [\n                  ray0[0] + nrayDir[0] * ds,\n                  ray0[1] + nrayDir[1] * ds,\n                  ray0[2] + nrayDir[2] * ds\n                ];\n\n                norm[0] = 0.; norm[1] = 0.; norm[2] = 0.;\n\n                let m = 0.0;\n                let fq = 0.0;\n                let nt = 0.0;\n                let dmin = 1000.0;\n                for (let j=0; j<this.constants.particleCount; j++) {\n                  let off = j * 3;\n                  let dx  = positions[off], dy  = positions[off+1.], dz  = positions[off+2.];\n                  dx -= rnow[0];        dy -= rnow[1];           dz -= rnow[2];\n                  let r = Math.sqrt(dx*dx + dy*dy + dz*dz);\n                  let x = r / this.constants.rfScale;\n                  if (x < 1.0) {\n                    let q = 1.0 - x*x*x*(x*(x*6.0-15.0)+10.0);\n                    norm[0] += (dx/r) * q;\n                    norm[1] += (dy/r) * q;\n                    norm[2] += (dz/r) * q;\n                    fq += q;\n                    nt += q;\n                    m += 1.0;\n                  } else {\n                    dmin = Math.min(dmin, r - this.constants.rfScale);\n                  }\n                }\n                let dist = dmin + 0.1;\n\n                norm[0] /= nt; norm[1] /= nt; norm[2] /= nt;\n\n                if (m > 0.5) {\n                  dist = (0.5333 * this.constants.rfScale) * (0.5 - fq);\n                }\n\n                ds += dist;\n                if (dist < 0.001) {\n                  done = 1.0;\n                }\n                if (ds >= this.constants.rayDistRef) {\n                  done = 1.0;\n                }\n              }\n            }\n            ds = Math.min(ds, this.constants.rayDistRef);\n\n            if (ds < (this.constants.rayDistRef - 0.001)) {\n              let ref = refractWrap(norm, nrayDir, 1./1.5);\n              let dot = ref[0] * nrayDir[0] + ref[1] * nrayDir[1] + ref[2] * nrayDir[2];\n              let int = 1. - Math.pow(Math.abs(ds) / this.constants.rayDistRef, 1.5);\n              let light = Math.min(1., Math.max(dot*dot, 0.)) * int;\n              outClr[2] += (int + light) * 0.1;\n              outClr[1] += (light) * 0.1;\n              outClr[0] += (light) * 0.1;\n            }\n          }\n\n          this.color(Math.min(outClr[0], 1.), Math.min(outClr[1], 1.), Math.min(outClr[2], 1.), 1.);\n        }, {\n          graphical: true,\n          constants: {\n            rayCount: this.rayCount,\n            rayDist: this.rayDist,\n            rayDistRef: Math.floor(this.rayDist / 4),\n            rfScale: this.rfScale,\n            bound: this.bound,\n            renderSize: this.renderSize,\n            particleCount: this.particleCount,\n            ICOUNT: 48,\n            ICOUNTR: 24\n          },\n          loopMaxIterations: 48 * this.particleCount,\n          output: [this.renderSize, this.renderSize],\n          tactic: 'speed',\n        })\n      ];\n\n      /*\n          INIT KERNEL\n       */\n      const seedPositions = gpu.createKernel(function(){\n        var t = random(Math.floor(this.thread.x / 3), this.constants.seed + 0.5);\n        var r = t * this.constants.maxr;\n        var a = 3.141592 * 2.0 * random(Math.floor(this.thread.x / 3), this.constants.seed + 1.5);\n        var comp = this.thread.x % 3;\n        if (comp < 0.01) {\n          return Math.cos(a) * r;\n        } else if (comp < 1.01) {\n          return Math.sin(a) * r;\n        } else {\n          return 0.0;\n        }\n      }, {\n        constants: {\n          maxr: 25,\n          seed: Math.random() * 1e6,\n          particleCount: this.particleCount\n        },\n        pipeline: true,\n        output: [this.particleCount * 3],\n        tactic: 'speed',\n      });\n\n      const seedVelocities = gpu.createKernel(function(){\n        var t = random(this.thread.x, this.constants.seed + 0.5);\n        return (t - 0.5) * this.constants.iv;\n      }, {\n        constants: {\n          iv: 1.5,\n          seed: Math.random() * 1e6,\n          particleCount: this.particleCount,\n        },\n        pipeline: true,\n        output: [this.particleCount * 3],\n        tactic: 'speed',\n      });\n\n      this.sDensity = [0.125, 0.25, 0.5, 1.0, 1.5, 2.0];\n      this.sDensityI = 5;\n      this.sMass = [0.05, 0.1, 0.2, 0.4, 0.8];\n      this.sMassI = 2;\n      this.sIncomp = [1.0, 0.8, 0.6, 0.4, 0.2];\n      this.sIncompI = 3;\n      this.sVisc = [0.05, 0.1, 0.25, 0.35, 0.5, 0.75, 0.95];\n      this.sViscI = 0;\n\n      const seedAttrs = gpu.createKernel(function(){\n        var comp = this.thread.x % 6;\n        if (comp < 0.01) {\n          return 0.25; // radius\n        } else if (comp < 1.01) {\n          return 0.5; // density\n        } else if (comp < 2.01) {\n          return 0.2; // mass\n        } else if (comp < 3.01) {\n          return 0.8; // incompress\n        } else if (comp < 4.01) {\n          return 0.35; // visc\n        } else {\n          // type\n          if (Math.floor(this.thread.x / 6) > (this.constants.particleCount-33)) {\n            return 1.0;\n          } else {\n            return 0.0;\n          }\n        }\n      }, {\n        constants: {\n          iv: 50,\n          seed: Math.random() * 1e6,\n          particleCount: this.particleCount\n        },\n        pipeline: true,\n        output: [this.particleCount * 6],\n        tactic: 'speed',\n      });\n\n      /*\n       * Seed Particles\n       */\n      this.reset = () => {\n        this.cam = {\n          p: [0, 0, 17.5],\n          dir: [0, 0, -1],\n          up: [0, 1, 0],\n          nearW: 0.0001,\n          farW: 1000,\n          farDist: 1000\n        };\n\n        this.move = {\n          toLR: 0,\n          toUD: 0,\n          toFR: 0,\n          tLR: 0,\n          tUD: 0,\n          tFR: 0,\n          toPull: 0,\n          tPull: 0,\n          toPullR: 0,\n          tPullR: 0\n        };\n\n        this.data = {\n          pos: seedPositions(),\n          vel: seedVelocities(),\n          attr: seedAttrs()\n        };\n      };\n\n      this.reset();\n\n      this.hash = [];\n      for (let i=0; i<this.hashLength; i++) {\n        for (let j=0; j<this.hashBinLen; j++) {\n          this.hash.push(0); this.hash.push(0); this.hash.push(0); this.hash.push(-1);\n        }\n      }\n\n      this.normv = gpu.createKernel(function(a) {\n        const length = Math.sqrt(Math.abs(a[0]*a[0]) + Math.abs(a[1]*a[1]) + Math.abs(a[2]*a[2]));\n        return a[this.thread.x] / length;\n      }, {\n        output: [3],\n        pipeline: true,\n        tactic: 'speed',\n        immutable: true,\n      });\n\n      this.crossv = gpu.createKernel(function(a, b) {\n        const a1 = a[0], a2 = a[1], a3 = a[2];\n        const b1 = b[0], b2 = b[1], b3 = b[2];\n        if (this.thread.x === 0) {\n          return a2 * b3 - a3 * b2;\n        } else if (this.thread.x === 1) {\n          return a3 * b1 - a1 * b3;\n        } else {\n          return a1 * b2 - a2 * b1;\n        }\n      }, {\n        output: [3],\n        pipeline: true,\n        tactic: 'speed',\n        immutable: true,\n      });\n    }\n\n    XSSPS.prototype.hashFn = function(x, y, z) {\n      const max2 = Math.floor(this.bound * 2);\n      const ix = (Math.floor(x/this.hashWSize)) + max2,\n        iy = (Math.floor(y/this.hashWSize)) + max2,\n        iz = (Math.floor(z/this.hashWSize)) + max2;\n      return ((ix * 137) + (iy * 197) + (iz * 167)) % this.hashLength;\n    };\n\n    XSSPS.prototype.updateRender = function(keys, dt) {\n      this.dt = dt;\n      this.handleInput(keys);\n      this.sDensityI = this.sDensityI % this.sDensity.length;\n      this.sViscI = this.sViscI % this.sVisc.length;\n      this.sMassI = this.sMassI % this.sMass.length;\n      this.sIncompI = this.sIncompI % this.sIncomp.length;\n\n      const SUBSTEPS = 2;\n\n      for (let i=0; i<SUBSTEPS; i++) {\n        // Update velocities via gravity & pressure\n        const prevVel = this.data.vel;\n        this.data.vel = this.updateVelocity(\n          this.data.pos,\n          this.data.vel,\n          this.data.attr,\n          this.move.tPull,\n          this.cam.p,\n          this.cam.dir,\n          this.move.tPullR,\n          this.sDensity[this.sDensityI],\n          this.sVisc[this.sViscI],\n          this.sMass[this.sMassI],\n          this.sIncomp[this.sIncompI],\n          this.dt / SUBSTEPS\n        );\n        deleteTexture(prevVel);\n\n        // Update positions\n        const prevPos = this.data.pos;\n        this.data.pos = this.updatePosition(\n          this.data.pos,\n          this.data.vel,\n          this.dt / SUBSTEPS\n        );\n        deleteTexture(prevPos);\n      }\n\n      // Render\n      this.renderMode = this.renderMode % this.renderKernel.length;\n      this.renderKernel[this.renderMode](\n        this.data.pos,\n        this.cam.p,\n        this.cam.dir,\n        this.cam.up,\n        this.cam.nearW, this.cam.farW, this.cam.farDist\n      );\n    };\n\n    XSSPS.prototype.handleInput = function(keys) {\n      this.move.toLR = this.move.toFR = this.move.toUD = 0;\n\n      if (keys[37]) {\n        this.move.toLR -= 1;\n      }\n      if (keys[39]) {\n        this.move.toLR += 1;\n      }\n      if (keys[38]) {\n        this.move.toUD -= 1;\n      }\n      if (keys[40]) {\n        this.move.toUD += 1;\n      }\n      if (keys[83]) {\n        this.move.toFR -= 1;\n      }\n      if (keys[87]) {\n        this.move.toFR += 1;\n      }\n\n      if (this.keys[82] && !keys[82]) {\n        this.renderMode += 1;\n      }\n      if (this.keys[27] && !keys[27]) {\n        this.reset();\n      }\n      if (this.keys[49] && !keys[49]) {\n        this.sDensityI += 1;\n      }\n      if (this.keys[50] && !keys[50]) {\n        this.sViscI += 1;\n      }\n      if (this.keys[51] && !keys[51]) {\n        this.sMassI += 1;\n      }\n      if (this.keys[52] && !keys[52]) {\n        this.sIncompI += 1;\n      }\n\n      if (this.keys[32] && !keys[32]) {\n        const prevPos = this.data.pos;\n        this.data.pos = this.setPosition(\n          this.data.pos,\n          (this.particleCount-1) - this.shootIndex,\n          this.cam.p,\n          this.cam.dir,\n          0.5\n        );\n        deleteTexture(prevPos);\n        const prevVel = this.data.vel;\n        this.data.vel = this.setVelocity(\n          this.data.vel,\n          (this.particleCount-1) - this.shootIndex,\n          this.cam.dir,\n          15\n        );\n        deleteTexture(prevVel);\n        this.shootIndex = (this.shootIndex + 1) % 32;\n      }\n      if (keys[69]) {\n        this.move.toPull = 10;\n        this.move.toPullR = 5;\n      }\n      else {\n        this.move.toPull = -1;\n        this.move.toPullR = 0.25;\n      }\n\n      this.keys = {...keys};\n\n      this.move.tLR += (this.move.toLR - this.move.tLR) * this.dt * 1.5;\n      this.move.tUD += (this.move.toUD - this.move.tUD) * this.dt * 1.5;\n      this.move.tFR += (this.move.toFR - this.move.tFR) * this.dt * 1.5;\n      this.move.tPull += (this.move.toPull - this.move.tPull) * this.dt * 1.5;\n      this.move.tPullR += (this.move.toPullR - this.move.tPullR) * this.dt * 1.5;\n      this.updateCamera();\n      if (this.onAfterInput) this.onAfterInput();\n    };\n\n    XSSPS.prototype.updateCamera = function() {\n      const prevUp = this.cam.up;\n      const crossv1 = this.crossv(\n        this.cam.dir,\n        this.cam.up\n      );\n      const crossv2 = this.crossv(\n        crossv1,\n        this.cam.dir\n      );\n      deleteTexture(this.cam.up);\n      this.cam.up = this.normv(crossv2);\n      deleteTexture(crossv1);\n      deleteTexture(crossv2);\n      deleteTexture(prevUp);\n      const prevDir = this.cam.dir;\n      this.cam.dir = this.rotateCamera(\n        this.move.tLR / 35,\n        -this.move.tUD / 35,\n        this.cam.p,\n        this.cam.dir,\n        this.cam.up,\n        1, 3.5, 2\n      );\n      deleteTexture(prevDir);\n      const prevP = this.cam.p;\n      this.cam.p = this.updateCameraPosition(\n        this.cam.dir,\n        this.cam.p,\n        this.move.tFR,\n        this.dt\n      );\n      deleteTexture(prevP);\n    };\n  </script>\n  <script>\n    function SSPS(psim) {\n      this.dt = 0;\n      this.psim = psim;\n      this.keys = {};\n      this.controls = {};\n      this.buildControls();\n    }\n\n    SSPS.prototype.buildControls = function() {\n      let html = `<div>\n      SSPS by Chadams - <span id=\"fps\"></span> fps - <span id=\"particle-count\"></span> particles @ <span id=\"size\"></span><br />\n      [W] - Forward, [S] - Reverse, [ARROWS] - Look<br />\n      [E] - Grab, [SPACE] - Shoot Particle<br />\n      [R] - Cycle Render Mode: <span id=\"render-mode\"></span><br />\n      [1] - Cycle Particle Density: <span id=\"particle-density\"></span><br />\n      [2] - Cycle Particle Viscosity: <span id=\"particle-viscosity\"></span><br />\n      [3] - Cycle Particle Mass: <span id=\"particle-mass\"></span><br />\n      [4] - Cycle Particle Incompressiveness: <span id=\"particle-incompressiveness\"></span><br />\n      [ESC] - Reset Particles & Camera<br />\n      [P] - Pause<br />\n      </div>`;\n      const renderer = document.createElement('div');\n      renderer.innerHTML = html;\n      html = this.controls.html = renderer.children[0];\n\n      this.controls.fps = html.querySelector('#fps');\n      this.controls.particleCount = html.querySelector('#particle-count');\n      this.controls.particleCount.innerHTML = this.psim.particleCount;\n      this.controls.size = html.querySelector('#size');\n      this.controls.size.innerHTML = this.psim.renderSize + 'x' + this.psim.renderSize;\n      this.controls.renderMode = html.querySelector('#render-mode');\n      this.controls.particleDensity = html.querySelector('#particle-density');\n      this.controls.particleViscosity = html.querySelector('#particle-viscosity');\n      this.controls.particleMass = html.querySelector('#particle-mass');\n      this.controls.particleIncompressiveness = html.querySelector('#particle-incompressiveness');\n      this.updateControls();\n    };\n\n    SSPS.prototype.mkOpt = function(lst, i) {\n      i = i % lst.length;\n      let ret = '';\n      for (let j=0; j<lst.length; j++) {\n        if (j === i) {\n          ret += '>>' + lst[j] + '<< ';\n        }\n        else {\n          ret += ' ' + lst[j] + ' ';\n        }\n      }\n      return ret;\n    };\n\n    SSPS.prototype.updateFPS = function() {\n      this.controls.fps.innerHTML = Math.round(1 / this.dt);\n    };\n\n    SSPS.prototype.updateControls = function() {\n      this.controls.renderMode.innerHTML = this.mkOpt([1, 2], this.psim.renderMode);\n      this.controls.particleDensity.innerHTML = this.mkOpt(this.psim.sDensity, this.psim.sDensityI);\n      this.controls.particleViscosity.innerHTML = this.mkOpt(this.psim.sVisc, this.psim.sViscI);\n      this.controls.particleMass.innerHTML = this.mkOpt(this.psim.sMass, this.psim.sMassI);\n      this.controls.particleIncompressiveness.innerHTML = this.mkOpt(this.psim.sIncomp, this.psim.sIncompI);\n    };\n\n    SSPS.prototype.updateRender = function() {\n      this.psim.updateRender(this.keys, this.dt);\n      this.updateFPS();\n    };\n\n    SSPS.prototype.start = function(tick) {\n      if (this.running) return;\n      document.title = 'SSPS - Chadams Studios (slightly upgraded)';\n      this.running = true;\n      requestAnimationFrame(tick);\n    };\n\n    SSPS.prototype.init = function () {\n      let lTime = Date.timeStamp();\n      let lDt = 1/60;\n      this.time = 0;\n\n      document.body.addEventListener('keydown', (e) => {\n        e = e || window.event;\n        this.keys[e.keyCode] = true;\n      });\n\n      document.body.addEventListener('keyup', (e) => {\n        e = e || window.event;\n        if (e.keyCode === 80) {\n          if (this.running) {\n            this.stop();\n          } else {\n            this.start(tick);\n          }\n          return;\n        }\n        this.keys[e.keyCode] = false;\n      });\n\n      const tick = () => {\n        if (!this.running) {\n          return;\n        }\n\n        const cTime = Date.timeStamp();\n        const dt = (Math.max(Math.min(cTime - lTime, 1/10), 1/240) || (1/60)) * 0.1 + lDt * 0.9;\n        this.dt = dt;\n        lDt = dt;\n        lTime = cTime;\n\n        this.time += dt;\n\n        this.updateRender();\n\n        requestAnimationFrame(tick);\n      };\n      this.start(tick);\n    };\n\n    SSPS.prototype.stop = function () {\n      this.running = false;\n\n      document.title = 'SSPS Stopped - Chadams Studios (slightly upgraded)';\n    };\n\n    Date.timeStamp = function() {\n      return new Date().getTime() / 1000.0;\n    };\n\n    function deleteTexture(texture) {\n      if (texture && texture.delete) {\n        texture.delete();\n      }\n    }\n  </script>\n</head>\n<body style=\"background-color: black; padding: 0; margin: 0;\"></body>\n<script>\n  const psim = new XSSPS(64, 256, () => {\n    inst.updateControls();\n  });\n  const inst = new SSPS(psim);\n  const { html } = inst.controls;\n  html.style.color = '#78788b';\n  html.style.position = 'absolute';\n  document.body.appendChild(html);\n  psim.canvas.style.margin = '0 auto';\n  psim.canvas.style.display = 'block';\n  psim.canvas.style.width = '100vh';\n  psim.canvas.style.padding = '0';\n  document.body.appendChild(psim.canvas);\n  inst.init();\n</script>\n</html>\n"
  },
  {
    "path": "examples/internal-variable-precision.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>GPU.js Internal Variable Precision</title>\n</head>\n<body>\n<h2>GPU.js Internal variable precision: <span id=\"ivp\"></span></h2>\n</body>\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  const ivp = document.getElementById('ivp');\n  const gpu = new GPU();\n  const startSize = 10;\n  const fullSize = window.innerWidth;\n  const kernel = gpu.createKernel(function() {\n    const x = this.thread.x / this.output.x;\n    const y = this.thread.y / this.output.y;\n    this.color(x, y, y, 1);\n  }, {\n    graphical: true,\n    dynamicOutput: true,\n    precision: 'unsigned'\n  });\n  document.body.appendChild(kernel.canvas);\n\n  let currentSize = startSize;\n  // const expected = 0;\n  // let tore = false;\n  function render() {\n    if (currentSize < fullSize) {\n      kernel.setOutput([\n        ++currentSize,\n        currentSize\n      ]);\n      kernel();\n      // const pixels = kernel.getPixels();\n      // if (tore || pixels[kernel.canvas.width * 4] !== expected) {\n      //   tore = true;\n      //   if (!confirm(`canvas tore at ${currentSize}x${currentSize}, continue?`)) {\n      //     return;\n      //   }\n      // }\n      ivp.textContent = kernel.getVariablePrecisionString();\n      requestAnimationFrame(render);\n    } else {\n      alert('reached the width of the window without tearing');\n    }\n  }\n  requestAnimationFrame(render);\n</script>\n</html>\n"
  },
  {
    "path": "examples/json-saving.js",
    "content": "const { GPU } = require('../src');\nconst gpu1 = new GPU();\n\nconst kernel1 = gpu1.createKernel(function(value) {\n  return value * 100;\n}, { output: [1] });\n\nconst resultFromRegularKernel = kernel1(42);\nconst json = kernel1.toJSON();\nconsole.log(resultFromRegularKernel);\n\n// Use bin/gpu-browser-core.js to get \"CORE\" mode, which works only with JSON\nconst gpu2 = new GPU();\nconst kernel2 = gpu2.createKernel(json);\nconst resultFromJsonKernel = kernel2(42);\nconsole.log(resultFromJsonKernel);\n\n"
  },
  {
    "path": "examples/mandelbrot-set.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<style>\n  body {\n    text-align: center;\n    margin: 0;\n  }\n  canvas {\n    padding: 0;\n    margin: 0;\n  }\n  div {\n    position: absolute;\n    color: white;\n    margin-left: auto;\n    margin-right: auto;\n    left: 0;\n    right: 0;\n  }\n</style>\n<body>\n  <div>Left click to zoom in, right click to zoom out.</div>\n</body>\n<script>\n  function f(x, c) {\n    return [\n      (x[0] * x[0]) - (x[1] * x[1]) + c[0],\n      (x[0] * x[1]) + (x[0] * x[1]) + c[1],\n    ];\n  }\n\n  function palette(t, a, b, c, d) {\n    return [\n      a[0] + b[0] * Math.cos(6.28318 * (c[0] * t + d[0])),\n      a[1] + b[1] * Math.cos(6.28318 * (c[1] * t + d[1])),\n      a[2] + b[2] * Math.cos(6.28318 * (c[2] * t + d[2]))\n    ];\n  }\n\n  function vectorLength(vector) {\n    return Math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]);\n  }\n\n  const gpu = new GPU();\n\n  gpu\n    .addFunction(f)\n    .addFunction(palette)\n    .addFunction(vectorLength);\n\n  const calculateMandelbrotSet = gpu.createKernel(function(zoomCenter, zoomSize, maxIterations) {\n    let x = [0, 0];\n    let c = [\n      zoomCenter[0] + ((this.thread.x / this.output.x) * 4 - 2) * (zoomSize / 4),\n      zoomCenter[1] + ((this.thread.y / this.output.y) * 4 - 2) * (zoomSize / 4)\n    ];\n    let escaped = false;\n    let iterations = 0;\n    for (let i = 0; i < maxIterations; i++) {\n      iterations = i;\n      x = f(x, c);\n      if (vectorLength(x) > 2) {\n        escaped = true;\n        break;\n      }\n    }\n    if (escaped) {\n      const pixel = palette(iterations / maxIterations, [0, 0, 0], [.59, .55, .75], [.1, .2, .3], [.75, .75, .75]);\n      this.color(pixel[0], pixel[1], pixel[2], 1);\n    } else {\n      this.color(.85, .99, 1, 1);\n    }\n  })\n    .setGraphical(true)\n    .setOutput([800, 800]);\n\n  let targetZoomCenter = [0, 0],\n    zoomFactor = 1,\n    zoomCenter = [0, 0],\n    zoomSize = 4,\n    maxIterations = 500,\n    stopZooming = true;\n\n  calculateMandelbrotSet(zoomCenter, zoomSize, maxIterations);\n  const canvas = calculateMandelbrotSet.canvas;\n  document.body.appendChild(canvas);\n\n  canvas.onmousedown = (e) => {\n    let x = e.offsetX / canvas.width,\n      y = e.offsetY / canvas.height;\n    targetZoomCenter[0] = zoomCenter[0] - zoomSize / 2.0 + x * zoomSize;\n    targetZoomCenter[1] = zoomCenter[1] + zoomSize / 2.0 - y * zoomSize;\n    stopZooming = false;\n    zoomFactor = e.buttons & 1 ? 0.99 : 1.01;\n    render();\n    return true;\n  };\n  canvas.oncontextmenu = () => { return false; };\n  canvas.onmouseup = () => { stopZooming = true; };\n\n  function render() {\n    calculateMandelbrotSet(zoomCenter, zoomSize, maxIterations);\n    if (!stopZooming) {\n      maxIterations -= 10;\n      if (maxIterations < 50) {\n        maxIterations = 50;\n      }\n\n      zoomSize *= zoomFactor;\n\n      zoomCenter[0] += 0.1 * (targetZoomCenter[0] - zoomCenter[0]);\n      zoomCenter[1] += 0.1 * ( targetZoomCenter[1] - zoomCenter[1]);\n\n      window.requestAnimationFrame(render);\n    } else if (maxIterations < 500) {\n      maxIterations += 10;\n      window.requestAnimationFrame(render);\n    }\n  }\n  window.requestAnimationFrame(render);\n</script>\n</html>\n"
  },
  {
    "path": "examples/mandelbulb.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n  <title>Frakal</title>\n  <style>\n    body { background: black; display: flex; justify-content: center; align-items: center; margin: 0; min-height: 100vh; overflow: hidden; }\n    .panel { display: flex; flex-direction: row-reverse; color: white }\n    .settings { margin-left: 20px; display: flex; flex-direction: column }\n    .field { margin-top: 20px; }\n    .keys { color: #666; }\n    input { border: white; }\n    #myCanvas { cursor: pointer }\n    #rayMinDist { background: black; color: #666;}\n  </style>\n</head>\n<body>\n<div class=\"panel\">\n  <canvas id=\"myCanvas\" class=\"viewport\" width=500 height=500></canvas>\n  <div class=\"settings\">\n    <h1>Mandelbulb - Real-time 3d fractal<br> in Javascript</h1>\n    <small>Kamil Kiełczewski <a href=\"http://airavana.net\">Airavana</a></small>\n    <div class=\"field\">\n      <div>Instruction</div>\n      <div>Click on picture and <span class=\"keys\">move mouse</span> around<br>\n        <span class=\"keys\">A S D W E C</span> keys for change view poin position <br>\n        Push ESC to stop move and unlock mouse\n      </div>\n    </div>\n    <div class=\"field\">\n      <div>Calculate Light</div>\n      <div><input type=\"checkbox\" onclick=\"settingsSetLight(event)\" checked></div>\n    </div>\n    <div class=\"field\">\n      <div>\n        Level of Details (and camera speed) <br>\n        <span class=\"keys\">Mouse whell</span> or <span class=\"keys\">+/-</span> keys\n\n      </div>\n      <div>\n        <input id=\"rayMinDist\" type=\"text\" value=\"166\" disabled >\n      </div>\n    </div>\n    <div class=\"field\">\n      <div>Info</div>\n      <div>\n        <a href=\"http://blog.hvidtfeldts.net/index.php/2011/06/distance-estimated-3d-fractals-part-i/\" class=\"c\">Fraactals 3d/ray-marching</a>\n      </div>\n      <div><a href=\"http://iquilezles.org/www/articles/distfunctions/distfunctions.htm\">Distance functions</a></div>\n      <div><a href=\"https://github.com/gpujs/gpu.js\">GPU JS</a></div>\n      <div><a href=\"https://github.com/kamil-kielczewski/fractals\">This project</a></div>\n    </div>\n  </div>\n</div>\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  // ----------------------------------------------------\n  //\n  // Init params\n  //\n  // ----------------------------------------------------\n  class RaytracerParams {\n    constructor() {\n      this.params = {\n        ray: {rayMaxSteps:512, rayMinDist:0.001, calcLight: 1 },\n        eye:    { x:0, y:1, z:0 },\n        light:    { x:-10, y:10, z:10 },\n        target:    { x:0, y:0, z:1 },\n        vertical: { x:0, y:1, z: 0}, // wektor pionu\n        screen: { pxWidth: 0, pxHeight: 0 },\n        camera: { yaw: 0, pitch: 0, speed: 0.01, rotateSpeed: 0.01, fov: 90, locked: true }\n      }\n    }\n\n    getParams() {\n      return this.params;\n    }\n\n    setScreenSize(width, height) {\n      this.params.screen.pxWidth = width;\n      this.params.screen.pxHeight = height;\n      return this;\n    }\n\n    // yaw = left/right, pitch = up/down\n    // [x,y,z] = position\n    moveCamera(yawDelta, pitchDelta, forwardBackward, leftRight, upDown) {\n      let e = this.params.eye;\n      let t = this.params.target;\n      let v = this.params.vertical;\n      let d = this.sub(t,e);\n\n      this.params.camera.yaw += yawDelta * this.params.camera.rotateSpeed;\n      this.params.camera.pitch += pitchDelta * this.params.camera.rotateSpeed;\n      let yaw = this.params.camera.yaw;\n      let pitch = this.params.camera.pitch;\n\n      // rotate\n      t.x = e.x + Math.sin(yaw)*Math.cos(pitch);\n      t.z = e.z + Math.cos(yaw)*Math.cos(pitch);\n      t.y = e.y + Math.sin(pitch);\n\n      // move forward\n      let fb = this.scale( this.params.camera.speed * forwardBackward , d);\n      this.addInPlace(e,fb);\n      this.addInPlace(t,fb);\n\n\n      // move left-right\n      let lr = this.scale( this.params.camera.speed * leftRight , this.norm(this.cross(v, d)));\n      this.addInPlace(e,lr);\n      this.addInPlace(t,lr);\n\n      // move up-down\n      let ud = this.scale( this.params.camera.speed * upDown, v);\n      this.addInPlace(e,ud);\n      this.addInPlace(t,ud);\n    }\n\n    calcRayBase() {\n      let E = this.params.eye;\n      let T = this.params.target;\n      let w = this.params.vertical;\n\n      let t = this.sub(T,E); // = viewport center\n      let tn = this.norm(t);\n\n      let b = this.cross(w, t);\n      let bn = this.norm(b);\n      let vn = this.cross(tn,bn);\n\n      let m = this.params.screen.pxHeight;\n      let k = this.params.screen.pxWidth;\n      let gx = Math.tan((2*Math.PI*this.params.camera.fov/360)/2);\n      let gy = gx*m/k;\n\n      // P1M is left bottom viewport pixel\n      let P1M = this.add( tn, this.scale(-gx, bn), this.scale(-gy, vn) ) // chnage C to tn (tn= C-E)\n\n      let QX = this.scale(2*gx/(k-1), bn);\n      let QY = this.scale(2*gy/(m-1), vn);\n\n      // Pij = P1M + (i-1)*bnp + (j-1)*vnp\n      return {E, P1M, QX, QY};\n\n    }\n\n    cross(a,b) {\n      let x = a.y*b.z - a.z*b.y;\n      let y = a.z*b.x - a.x*b.z;\n      let z = a.x*b.y - a.y*b.x;\n      return {x,y,z};\n    }\n\n    norm(a) {\n      let size = 1/Math.sqrt(a.x*a.x + a.y*a.y + a.z*a.z);\n      return {x: a.x*size, y: a.y*size, z: a.z*size };\n    }\n\n    addInPlace(a,b) {\n      a.x += b.x;\n      a.y += b.y;\n      a.z += b.z;\n      return a;\n    }\n\n    add(...vs) {\n      return vs.reduce( (a,b) => ({ x: a.x+b.x, y: a.y+b.y, z: a.z+b.z }) )\n      // return {\n      //     x: a.x + b.x,\n      //     y: a.y + b.y,\n      //     z: a.z + b.z\n      // }\n\n    }\n\n    sub(a,b) {\n      return this.add(a,this.scale(-1,b));\n    }\n\n    scale(s, a) {\n      return { x: a.x*s, y: a.y*s, z: a.z*s }\n    }\n  }\n\n\n  // ----------------------------------------------------\n  //\n  // GPU & Canvas\n  //\n  // ----------------------------------------------------\n\n  // T - target\n  // E - eye\n  // vvn - wektor pionu kamery (normalny)\n  // fov - kont widzenia (stopnie)\n  // ar - aspect ratio\n  // k - liczba pixeli widht\n  // m - liczba pixeli height\n  let kernelMarchingRays = function(calcLight, rayMaxSteps, rayMinDist, Ex,Ey,Ez, P1Mx, P1My, P1Mz, QXx, QXy, QXz, QYx, QYy,QYz, Lx, Ly, Lz) {\n    let i = this.thread.x;\n    let j = this.thread.y;\n    let rx = P1Mx + QXx*(i-1) + QYx*(j-1);\n    let ry = P1My + QXy*(i-1) + QYy*(j-1);\n    let rz = P1Mz + QXz*(i-1) + QYz*(j-1);\n\n    let sr = 1/Math.sqrt(rx*rx + ry*ry + rz*rz);\n    let rnx = rx*sr;\n    let rny = ry*sr;\n    let rnz = rz*sr;\n\n    let totalDistance = 0.0;\n    let MaximumRaySteps= rayMaxSteps; //255;\n    let MinimumDistance= rayMinDist; //0.0001;\n    let stepsVal = 0;\n    let ppx = 0;\n    let ppy = 0;\n    let ppz = 0;\n    let distance = 0;\n    let hit = 0;\n    let dd= [0,0,0];\n    let hitObjectId = 0; // 0 = no hit\n\n    for (let steps=0 ; steps < MaximumRaySteps; steps++) {\n      ppx = Ex + totalDistance * rnx;\n      ppy = Ey + totalDistance * rny;\n      ppz = Ez + totalDistance * rnz;\n\n      dd = distScene(ppx,ppy,ppz);\n      distance = dd[0]; // --- Distance estimator\n\n      totalDistance += distance;\n      if (distance < MinimumDistance) {\n        stepsVal = steps;\n        hit=1;\n        hitObjectId = dd[2];\n        break\n      }\n    }\n\n    let iterFrac = dd[1];\n\n    let color_r = 0;\n    let color_g = 0;\n    let color_b = 0;\n\n    // --- calculate normals and light ---\n    if(calcLight==1) {\n      let eps = rayMinDist*10;\n      if(eps>0.00015) { eps = 0.00015; }\n\n      dd = distScene(ppx + eps, ppy, ppz);\n      let nx = dd[0] - distance; // - distScene(ppx - eps, ppy, ppz);\n      dd = distScene(ppx, ppy + eps, ppz);\n      let ny = dd[0] - distance; // - distScene(ppx, ppy - eps, ppz);\n      dd = distScene(ppx, ppy, ppz + eps);\n      let nz = dd[0] - distance; // - distScene(ppx, ppy, ppz - eps);\n\n      let sn = 1/Math.sqrt(nx*nx + ny*ny + nz*nz);\n      nx = nx * sn;\n      ny = ny * sn;\n      nz = nz * sn;\n\n      let lx = Lx - ppx;\n      let ly = Lz - ppy;\n      let lz = Lz - ppz;\n      let sl = 1/Math.sqrt(lx*lx + ly*ly + lz*lz);\n      lx *= sl;\n      ly *= sl;\n      lz *= sl;\n\n      //let colBcg = hsv2rgb(0.6,1,0.2*(0.4+((i+j))/(1024)));\n      //let colBcg = [j/4024,i/4024,0];\n      let colBcg = [0,0,0];\n      let light = lx * nx + ly*ny + lz*nz;\n      light = (light+1)/2;\n      //let col = hsv2rgb(((iterFrac*666)%100)/10, 1, light);\n      let distLight = -10/Math.log2(rayMinDist);///totalDistance; //1.0/(Math.pow(totalDistance,5));\n      //if(distLight>1) distLight=1;\n      let col = hsv2rgb(\n        (ppx+ppy+ppz),\n        1,\n        distLight*8*light*stepsVal/MaximumRaySteps\n      );\n\n      color_r = col[0];\n      color_g = col[1];\n      color_b = col[2];\n\n      if(hitObjectId===0) {\n        color_r = colBcg[0];\n        color_g = colBcg[1];\n        color_b = colBcg[2];\n      }\n\n      if(hitObjectId===1) {\n        color_r = 0;\n        color_g = 1*light;\n        color_b = 0;\n      }\n\n      //color_r = Math.max(light,0) + hit*0.2;\n      //color_g = light;\n      //color_b = light;\n\n    } else {\n      let trace= 2*stepsVal/MaximumRaySteps;\n      color_r = trace;\n      color_g = trace;\n      color_b = trace;\n    }\n\n    this.color(color_r, color_g, color_b ,1);\n  };\n\n  function hsv2rgb(h,s,v) {\n    //h = 3.1415*2*(h%360)/360;\n    // h=3.1415*2*h/100; // 100 is from max number of function mandelbulb iterations\n\n    let c = v*s;\n    let k = (h%1)*6;\n    let x = c*(1 - Math.abs(k%2-1));\n\n    let r=0;\n    let g=0;\n    let b=0;\n\n    if(k>=0 && k<=1) { r=c; g=x }\n    if(k>1 && k<=2)  { r=x; g=c }\n    if(k>2 && k<=3)  { g=c; b=x }\n    if(k>3 && k<=4)  { g=x; b=c }\n    if(k>4 && k<=5)  { r=x; b=c }\n    if(k>5 && k<=6)  { r=c; b=x }\n\n    let m = v-c;\n\n    return [r+m,g+m,b+m];\n  }\n\n  // x,y,z - point on ray (marching)\n  function distScene(x,y,z) {\n    //let p = sdPlane(x,y,z, 0,1,0,1);\n    let mm = mandelbulb(x,y,z);\n    let m = mm[0];\n    let letIter = mm[1];\n    let dis = m; //Math.min(p,m);\n    //let dis = Math.min(p,m);\n    let objId = 1; // 1= plane\n    if(dis===m) objId = 2; // 2= plane\n\n    return [ dis, letIter, objId ] ;\n  }\n\n  function sdPlane(px,py,pz, nx,ny,nz,nw) {\n    return px*nx + py*ny + pz*nz + nw;\n  }\n\n  function mandelbulb(px,py,pz) {\n    let zx=px; let zy=py; let zz=pz;\n    let dr = 1;\n    let r = 0;\n    let bailout = 2;\n    let power = 8;\n    let j=0;\n\n    for (let i=0 ; i < this.constants.iterations; i++) {\n      r = Math.sqrt(zx*zx + zy*zy + zz*zz);\n      if (r>bailout) break;\n\n      // convert to polar coordinates\n      let theta = Math.acos(zz/r);\n      let phi = Math.atan(zy,zx);\n\n      dr =  Math.pow( r, power-1.0)*power*dr + 1.0;\n\n      // scale and rotate the point\n      let zzr = Math.pow( r,power);\n      theta = theta*power;\n      phi = phi*power;\n\n      // convert back to cartesian coordinates\n      zx = zzr * Math.sin(theta) * Math.cos(phi);\n      zy = zzr * Math.sin(phi) * Math.sin(theta);\n      zz = zzr * Math.cos(theta);\n      zx+=px;\n      zy+=py;\n      zz+=pz;\n\n      j++;\n    }\n    return [0.5*Math.log(r)*r/dr, j];\n  }\n\n  // Pointer Locking enable\n  // https://www.html5rocks.com/en/tutorials/pointerlock/intro/\n  // https://w3c.github.io/pointerlock/\n  // continue this tommorow...\n  function canvasOnClick_enablePointerLocking(event) {\n    if(par.camera.locked) {\n      let canvas = event.target;\n\n      canvas.requestPointerLock = canvas.requestPointerLock ||\n        canvas.mozRequestPointerLock ||\n        canvas.webkitRequestPointerLock;\n      // Ask the browser to lock the pointer\n\n      canvas.requestPointerLock();\n    }\n  }\n\n  function lockChange(e) {\n    par = raytracerParams.getParams();\n    par.camera.locked = !par.camera.locked;\n  }\n\n  function initFractalGPU(raytracerParams) {\n    params = raytracerParams.getParams();\n    let pxWidth = params.screen.pxWidth;\n    let pxHeight = params.screen.pxHeight;\n    let canvas = document.getElementById(\"myCanvas\");\n    if(canvas) { canvas.remove(); }\n    canvas = document.createElement('canvas');\n    canvas.id = 'myCanvas';\n    document.body.querySelector('.panel').appendChild(canvas);\n\n    //canvas.width = pxWidth;\n    //canvas.height = pxHeight;\n\n    canvas.addEventListener('click', canvasOnClick_enablePointerLocking);\n    document.addEventListener('pointerlockchange', lockChange, false);\n\n    const gl = canvas.getContext('webgl2', { premultipliedAlpha: false });\n    const gpu = new GPU({ canvas, webGl: gl })\n      .addFunction(sdPlane)\n      .addFunction(mandelbulb)\n      .addFunction(distScene)\n      .addFunction(hsv2rgb);\n    return gpu.createKernel(kernelMarchingRays)\n      .setDebug(true)\n      .setConstants({ pxWidth, pxHeight, iterations: 100 })\n      .setOutput([pxWidth, pxHeight])\n      .setGraphical(true)\n      .setLoopMaxIterations(10000);\n  }\n\n  // ----------------------------------------------------\n  //\n  // UX\n  //\n  // ----------------------------------------------------\n\n  function initGui() {\n    raytracer.canvas.addEventListener('wheel',(event) => { mouseWheel(event); return false; }, false);\n    raytracer.canvas.addEventListener('mousemove',(event) => { mouseMove(event); return false; }, false);\n    document.onkeypress = (e) => {\n      if(e.key === \"w\") moveCamera(1,0,0);\n      if(e.key === \"s\") moveCamera(-1,0,0);\n      if(e.key === \"a\") moveCamera(0,-1,0);\n      if(e.key === \"d\") moveCamera(0,1,0);\n      if(e.key === \"e\") moveCamera(0,0,1);\n      if(e.key === \"c\") moveCamera(0,0,-1);\n\n      if(e.key === \"+\") mouseWheel({deltaY: -10});\n      if(e.key === \"-\") mouseWheel({deltaY: 10});\n    };\n    chceckScreenResize();\n\n    refresh();\n  }\n\n  function moveCamera(forwardBackward, leftRight, upDown) {\n    par = raytracerParams.getParams();\n    // par.camera.locked\n    raytracerParams.moveCamera(0,0, forwardBackward, leftRight, upDown);\n    refresh();\n  }\n\n  function refresh() {\n    // unlock on refresh demand\n    // (after changing some render parameter by key/mose move)\n    locked = false;\n  }\n\n  function refreshWindow(timestamp) {\n    // redraw only if some render parameter change (user move mose, push button etc.)\n    if(!locked) {\n      locked = true;\n      let par = raytracerParams.getParams();\n      let r = raytracerParams.calcRayBase(); // {E, P1M, Bn, Vn};\n\n      raytracer(\n        par.ray.calcLight, par.ray.rayMaxSteps, par.ray.rayMinDist,\n        r.E.x, r.E.y, r.E.z,\n        r.P1M.x, r.P1M.y, r.P1M.z,\n        r.QX.x, r.QX.y, r.QX.z,\n        r.QY.x, r.QY.y, r.QY.z,\n        par.light.x, par.light.y, par.light.z,\n      );\n    }\n\n    //\n    window.requestAnimationFrame(refreshWindow);\n  }\n\n  function mouseMove(e) {\n    par = raytracerParams.getParams();\n\n    //let canvas = document.getElementById(\"myCanvas\");\n    //console.log('clock',canvas.requestPointerLock);\n\n    //raytracerParams.moveCamera(e.offsetX/100, e.offsetY/100); // yaw = left/right, pitch = up/down\n    if(!par.camera.locked) {\n      raytracerParams.moveCamera(e.movementX, -e.movementY, 0,0,0); // yaw = left/right, pitch = up/down\n      refresh();\n    }\n  }\n\n  function mouseWheel(e) {\n    tmpMouseWhellY += e.deltaY;\n    tmpMouseWhellY = tmpMouseWhellY>-2000 ? -2000 : tmpMouseWhellY;\n    tmpMouseWhellY = tmpMouseWhellY<-8000 ? -8000 : tmpMouseWhellY;\n    n = Math.pow(10, tmpMouseWhellY/1000);\n\n    par.ray.rayMinDist = n;\n    par.camera.speed = n*10;\n\n    document.querySelector(\"#rayMinDist\").value = Math.floor((-tmpMouseWhellY-2000)/6);\n    refresh();\n  }\n\n  function chceckScreenResize() {\n    window.addEventListener(\"resize\",() => {\n      //refreshScreenSize();\n    });\n  }\n\n  function refreshScreenSize() {\n    //raytracerParams.setScreenSize(window.innerWidth, window.innerHeight);\n    raytracerParams.setScreenSize(500, 500);\n    raytracer = initFractalGPU(raytracerParams);\n    refresh();\n  }\n\n  function settingsSetLight(e) {\n    par = raytracerParams.getParams();\n    par.ray.calcLight = 1-par.ray.calcLight;\n    refresh();\n  }\n\n  function settingsRayMinDist(e, direct=false) {\n    let n = (\"0.\" + \"0\".repeat(e.target.value-1) + \"1\")*1;\n    par.ray.rayMinDist = n;\n    par.camera.speed = n*10;\n\n    document.querySelector(\"#rayMinDist\").value = e.target.value;\n    refresh();\n    //rayMinDist\n    //par = raytracerParams.getParams();\n  }\n\n  function settingsRayMaxSteps(e) {\n    par = raytracerParams.getParams();\n    par.ray.rayMaxSteps = e.target.value;\n    document.querySelector(\"#rayMaxSteps\").value = par.ray.rayMaxSteps;\n    refresh();\n  }\n\n\n  // ----------------------------------------------------\n  //\n  // Main\n  //\n  // ----------------------------------------------------\n  let raytracerParams = new RaytracerParams();\n  let tmpMouseWhellY = -3000;\n  let locked = false;\n  let start;\n  refreshScreenSize();\n  //let raytracer = initFractalGPU(raytracerParams);\n  initGui(raytracerParams);\n\n  window.requestAnimationFrame(refreshWindow);\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/parallel-raytracer.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width\">\n  <title>GPU.js Raytracer</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style type=\"text/css\" media=\"all\">\n    body {\n      font-family: 'Open Sans', sans-serif;\n    }\n\n    .flex-container {\n      display: -ms-flex;\n      display: -webkit-flex;\n      display: flex;\n    }\n\n    .flex-container div.left-col {\n      margin-left: 10px;\n      margin-right: 10px;\n      width: 40%;\n    }\n\n    .flex-container div.right-col {\n      width: 60%;\n    }\n\n    ul {\n      padding-left: 10px;\n      list-style: none;\n    }\n\n    span.canvas {\n      padding-left: 2px;\n      padding-right: 2px;\n    }\n\n  </style>\n</head>\n<body>\n<div class=\"flex-container\">\n  <div class=\"left-col\">\n    <p>A parallel raytracer built with TypeScript and <a href=\"http://gpu.rocks\">GPU.js</a> (WebGL/GLSL).</p>\n    <p>GitHub: <a href=\"http://github.com/jin/raytracer\">http://github.com/jin/raytracer</a></p>\n\n    <p>Press W, A, S, D to move the camera around.</p>\n\n    <p>\n    <h3>Current mode</h3>\n    <span id=\"mode\">GPU</span>\n    <input type=\"button\" value=\"Switch mode\" onclick=\"toggleMode()\" />\n    </p>\n    <p>\n    <h3>FPS</h3><span id=\"fps\">Loading..</span>\n    <input type=\"button\" value=\"Pause\" onclick=\"togglePause(this)\" />\n    </p>\n    <p>\n    <h3>Number of spheres</h3>\n    <span id=\"sphere-count\">4</span> <input type=\"range\" name=\"points\" min=\"1\" max=\"30\" value=\"4\" onchange=\"updateSphereSlider(this)\">\n    </p>\n\n    <p>\n    <h3>Grid dimension</h3>\n    <span id=\"grid-dimension\">2</span> <input type=\"range\" name=\"points\" min=\"1\" max=\"4\" value=\"2\" id=\"dimension-slider\" onchange=\"updateDimension(this)\">\n    </p>\n    <p>\n      <!-- Capped at 4. -->\n      <!-- WARNING: Too many active WebGL contexts. Oldest context will be lost. -->\n    <p>\n      The canvas is 640px by 640px. Each canvas object is controlled by a single GPU.js kernel and a single thread is spawned for each pixel to compute the color of the pixel.\n    </p>\n\n    <p>\n      Increase the dimensions of the grid to break the canvas up into tiles, so that there are multiple kernels controlling multiple tiles. With this approach, the kernels will run sequentially, computing one canvas after another. <br />\n    </p>\n    </p>\n\n    <p>\n    <h3>Benchmark</h3>\n    <div>\n      <input type=\"button\" value=\"Benchmark\" onclick=\"benchmark(this)\" />\n    </div>\n    Benchmark the performance of the parallelized GPU and sequential CPU kernels.\n    This will render 30 frames each and compute min, max, median frame rendering durations, and speedups. <br />\n    </p>\n    <div id=\"speedup\"></div>\n    <div id=\"results\"></div>\n  </div>\n  <div class=\"right-col\" id=\"canvas-container\"></div>\n</div>\n\n<!-- Generate the canvas elements based on the kernel dimensions -->\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  var Entity;\n  (function(Entity_1) {\n    (function(Type) {\n      Type[Type[\"EMPTY\"] = 0] = \"EMPTY\";\n      Type[Type[\"SPHERE\"] = 1] = \"SPHERE\";\n      Type[Type[\"CUBOID\"] = 2] = \"CUBOID\";\n      Type[Type[\"CYLINDER\"] = 3] = \"CYLINDER\";\n      Type[Type[\"CONE\"] = 4] = \"CONE\";\n      Type[Type[\"TRIANGLE\"] = 5] = \"TRIANGLE\";\n      Type[Type[\"PLANE\"] = 6] = \"PLANE\";\n      Type[Type[\"LIGHTSPHERE\"] = 7] = \"LIGHTSPHERE\";\n    })(Entity_1.Type || (Entity_1.Type = {}));\n    var Type = Entity_1.Type;\n    var Entity = (function() {\n      function Entity(opts) {\n        this.entityType = opts.entityType;\n        this.opacity = opts.opacity;\n        this.ambientColor = opts.ambientColor;\n        this.specularReflection = opts.specularReflection;\n        this.lambertianReflection = opts.lambertianReflection;\n        this.x = opts.x || 0;\n        this.y = opts.y || 0;\n        this.z = opts.z || 0;\n        this.red = opts.red || 0;\n        this.blue = opts.blue || 0;\n        this.green = opts.green || 0;\n        this.width = opts.width || 0;\n        this.height = opts.height || 0;\n        this.depth = opts.depth || 0;\n        this.radius = opts.radius || 0;\n        this.directionX = opts.directionX || 0;\n        this.directionY = opts.directionY || 0;\n        this.directionZ = opts.directionZ || 0;\n        this.constant = opts.constant || 0;\n      }\n      Entity.prototype.toVector = function() {\n        var array = [\n          this.entityType,\n          this.x,\n          this.y,\n          this.z,\n          this.width,\n          this.height,\n          this.depth,\n          this.radius,\n          this.red,\n          this.green,\n          this.blue,\n          this.lambertianReflection,\n          this.opacity,\n          this.specularReflection,\n          this.ambientColor,\n          this.directionX,\n          this.directionY,\n          this.directionZ,\n          this.constant\n        ];\n        return array;\n      };\n      Entity.prototype.fromNumberArray = function(array) {};\n      return Entity;\n    }());\n    Entity_1.Entity = Entity;\n  })(Entity || (Entity = {}));\n  var upVector = [0, 1, 0];\n  var intoVector = [0, 0, 1];\n  var rightVector = [1, 0, 0];\n  var zeroVector = [0, 0, 0];\n\n  function vecDotProduct(a, b) {\n    return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);\n  }\n\n  function dotProduct(x1, x2, x3, y1, y2, y3) {\n    return x1 * y1 + x2 * y2 + x3 * y3;\n  }\n\n  function vecCrossProduct(a, b) {\n    return [\n      a[1] * b[2] - a[2] * b[1],\n      a[2] * b[0] - a[0] * b[2],\n      a[0] * b[1] - a[1] * b[0]\n    ];\n  }\n\n  function crossProductX(x1, x2, x3, y1, y2, y3) {\n    return x2 * y3 - x3 * y2;\n  }\n\n  function crossProductY(x1, x2, x3, y1, y2, y3) {\n    return x3 * y1 - x1 * y3;\n  }\n\n  function crossProductZ(x1, x2, x3, y1, y2, y3) {\n    return x1 * y2 - x2 * y1;\n  }\n\n  function vecScale(a, sc) {\n    return [a[0] * sc, a[1] * sc, a[2] * sc];\n  }\n\n  function scaleX(x1, x2, x3, scale) {\n    return scale * x1;\n  }\n\n  function scaleY(x1, x2, x3, scale) {\n    return scale * x2;\n  }\n\n  function scaleZ(x1, x2, x3, scale) {\n    return scale * x3;\n  }\n\n  function vecMagnitude(a) {\n    return Math.sqrt(vecDotProduct(a, a));\n  }\n\n  function magnitude(x1, x2, x3) {\n    return Math.sqrt(x1 * x1 + x2 * x2 + x3 * x3);\n  }\n\n  function vecNormalize(a) {\n    var mag = vecMagnitude(a);\n    return vecScale(a, 1 / mag);\n  }\n\n  function normalizeX(x1, x2, x3) {\n    var mag = magnitude(x1, x2, x3);\n    return scaleX(x1, x2, x3, 1 / mag);\n  }\n\n  function normalizeY(x1, x2, x3) {\n    var mag = magnitude(x1, x2, x3);\n    return scaleY(x1, x2, x3, 1 / mag);\n  }\n\n  function normalizeZ(x1, x2, x3) {\n    var mag = magnitude(x1, x2, x3);\n    return scaleZ(x1, x2, x3, 1 / mag);\n  }\n\n  function vecAdd(a, b) {\n    return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];\n  }\n\n  function addX(x1, x2, x3, y1, y2, y3) {\n    return x1 + y1;\n  }\n\n  function addY(x1, x2, x3, y1, y2, y3) {\n    return x2 + y2;\n  }\n\n  function addZ(x1, x2, x3, y1, y2, y3) {\n    return x3 + y3;\n  }\n\n  function vecAdd3(a, b, c) {\n    return [a[0] + b[0] + c[0], a[1] + b[1] + c[1], a[2] + b[2] + c[2]];\n  }\n\n  function add3X(x1, x2, x3, y1, y2, y3, z1, z2, z3) {\n    return x1 + y1 + z1;\n  }\n\n  function add3Y(x1, x2, x3, y1, y2, y3, z1, z2, z3) {\n    return x2 + y2 + z2;\n  }\n\n  function add3Z(x1, x2, x3, y1, y2, y3, z1, z2, z3) {\n    return x3 + y3 + z3;\n  }\n\n  function vecSubtract(a, b) {\n    return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];\n  }\n\n  function subtractX(x1, x2, x3, y1, y2, y3) {\n    return x1 - y1;\n  }\n\n  function subtractY(x1, x2, x3, y1, y2, y3) {\n    return x2 - y2;\n  }\n\n  function subtractZ(x1, x2, x3, y1, y2, y3) {\n    return x3 - y3;\n  }\n\n  function sphereIntersection(spherePtX, spherePtY, spherePtZ, sphereRadius, rayPtX, rayPtY, rayPtZ, rayVecX, rayVecY, rayVecZ) {\n    var eyeToCenterX = spherePtX - rayPtX;\n    var eyeToCenterY = spherePtY - rayPtY;\n    var eyeToCenterZ = spherePtZ - rayPtZ;\n    var sideLength = dotProduct(eyeToCenterX, eyeToCenterY, eyeToCenterZ, rayVecX, rayVecY, rayVecZ);\n    var cameraToCenterLength = dotProduct(eyeToCenterX, eyeToCenterY, eyeToCenterZ, eyeToCenterX, eyeToCenterY, eyeToCenterZ);\n    var discriminant = (sphereRadius * sphereRadius) - cameraToCenterLength + (sideLength * sideLength);\n    if (discriminant < 0) {\n      return -1;\n    } else {\n      return sideLength - Math.sqrt(discriminant);\n    }\n  }\n\n  function sphereNormalX(spherePtX, spherePtY, spherePtZ, surfacePtX, surfacePtY, surfacePtZ) {\n    var x = surfacePtX - spherePtX,\n      y = surfacePtY - spherePtY,\n      z = surfacePtZ - spherePtZ;\n    return x * (1 / magnitude(x, y, z));\n  }\n\n  function sphereNormalY(spherePtX, spherePtY, spherePtZ, surfacePtX, surfacePtY, surfacePtZ) {\n    var x = surfacePtX - spherePtX,\n      y = surfacePtY - spherePtY,\n      z = surfacePtZ - spherePtZ;\n    return y * (1 / magnitude(x, y, z));\n  }\n\n  function sphereNormalZ(spherePtX, spherePtY, spherePtZ, surfacePtX, surfacePtY, surfacePtZ) {\n    var x = surfacePtX - spherePtX,\n      y = surfacePtY - spherePtY,\n      z = surfacePtZ - spherePtZ;\n    return z * (1 / magnitude(x, y, z));\n  }\n\n  function reflectVecX(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ) {\n    var scaleFactor = dotProduct(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ);\n    var normalVecXScaled = normalVecX * scaleFactor * 2;\n    return normalVecXScaled - incidentVecX;\n  }\n\n  function reflectVecY(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ) {\n    var scaleFactor = dotProduct(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ);\n    var normalVecYScaled = normalVecY * scaleFactor * 2;\n    return normalVecYScaled - incidentVecY;\n  }\n\n  function reflectVecZ(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ) {\n    var scaleFactor = dotProduct(incidentVecX, incidentVecY, incidentVecZ, normalVecX, normalVecY, normalVecZ);\n    var normalVecZScaled = normalVecZ * scaleFactor * 2;\n    return normalVecZScaled - incidentVecZ;\n  }\n\n  function planeIntersection(normalVecX, normalVecY, normalVecZ, distance, rayPtX, rayPtY, rayPtZ, rayVecX, rayVecY, rayVecZ) {\n    var denom = dotProduct(rayVecX, rayVecY, rayVecZ, normalVecX, normalVecY, normalVecZ);\n    if (denom !== 0) {\n      var t = -(distance + (rayPtX * normalVecX + rayPtY * normalVecY + rayPtZ * normalVecZ)) / denom;\n      if (t < 0) {\n        return -1;\n      } else {\n        return t;\n      }\n    } else {\n      return -1;\n    }\n  }\n  (function unitTests() {\n    function expect(desc, expr, val) {\n      if (expr !== val) {\n        throw (\"FAIL: \" + desc + \" / Actual: \" + expr + \" / Expected: \" + val);\n      }\n    }\n    expect(\"addX\", addX(1, 2, 3, 4, 5, 6), 5);\n    expect(\"addY\", addY(1, 2, 3, 4, 5, 6), 7);\n    expect(\"addZ\", addZ(1, 2, 3, 4, 5, 6), 9);\n    expect(\"add3X\", add3X(1, 2, 3, 4, 5, 6, 7, 8, 9), 12);\n    expect(\"add3Y\", add3Y(1, 2, 3, 4, 5, 6, 7, 8, 9), 15);\n    expect(\"add3Z\", add3Z(1, 2, 3, 4, 5, 6, 7, 8, 9), 18);\n    expect(\"subtractX\", subtractX(1, 2, 3, 4, 5, 6), -3);\n    expect(\"subtractY\", subtractY(1, 2, 3, 4, 5, 6), -3);\n    expect(\"subtractZ\", subtractZ(1, 2, 3, 4, 5, 6), -3);\n    expect(\"magnitude\", magnitude(2, 3, 4), Math.sqrt(4 + 9 + 16));\n    expect(\"normalizeX\", normalizeX(7, 24, 0), 7 / 25);\n    expect(\"normalizeY\", normalizeY(7, 24, 0), 24 / 25);\n    expect(\"normalizeZ\", normalizeZ(0, 24, 7), 7 / 25);\n    expect(\"scaleX\", scaleX(1, 2, 3, 3), 3);\n    expect(\"scaleY\", scaleY(1, 2, 3, 3), 6);\n    expect(\"scaleZ\", scaleZ(1, 2, 3, 3), 9);\n    expect(\"crossProductX\", crossProductX(2, 3, 4, 5, 6, 7), -3);\n    expect(\"crossProductY\", crossProductY(2, 3, 4, 5, 6, 7), 6);\n    expect(\"crossProductZ\", crossProductZ(2, 3, 4, 5, 6, 7), -3);\n    expect(\"dotProduct\", dotProduct(2, 3, 4, 5, 6, 7), 56);\n  })();\n  var vectorFunctions = [\n    addX, addY, addZ,\n    add3X, add3Y, add3Z,\n    subtractX, subtractY, subtractZ,\n    magnitude,\n    normalizeX, normalizeY, normalizeZ,\n    crossProductX, crossProductY, crossProductZ,\n    scaleX, scaleY, scaleZ,\n    dotProduct,\n    sphereIntersection,\n    sphereNormalX, sphereNormalY, sphereNormalZ,\n    reflectVecX, reflectVecY, reflectVecZ,\n    planeIntersection\n  ];\n\n  function square(x) {\n    return x * x;\n  }\n\n  function dist(x1, y1, x2, y2) {\n    return Math.sqrt(square(x2 - x1) + square(y2 - y1));\n  }\n  var rand = function(min, max) {\n    return Math.random() * (max - min) + min;\n  };\n  var utilityFunctions = [square, dist];\n  var Scene;\n  (function(Scene) {\n    var camera = [\n      0, 0, 25,\n      0, 0, 24,\n      45\n    ];\n    var light_opts = [{\n      entityType: Entity.Type.LIGHTSPHERE,\n      red: 1,\n      green: 1,\n      blue: 1,\n      x: 0,\n      y: 7,\n      z: 0,\n      radius: 0.1,\n      specularReflection: 0.0,\n      lambertianReflection: 1,\n      ambientColor: 1,\n      opacity: 1.0,\n      directionX: 0,\n      directionY: 0,\n      directionZ: 0\n    }];\n    var lights = light_opts.map(function(light) {\n      return [light.x, light.y + 0.5, light.z, light.red, light.green, light.blue];\n    });\n    var sphere_opts = [{\n      entityType: Entity.Type.SPHERE,\n      red: 1.0,\n      green: 0.7,\n      blue: 0.7,\n      x: -4,\n      y: 3.5,\n      z: -2,\n      radius: 0.5,\n      specularReflection: 0.1,\n      lambertianReflection: 1,\n      ambientColor: 0.1,\n      opacity: 1.0,\n      directionX: 0.05,\n      directionY: 0.05,\n      directionZ: -0.05\n    },\n      {\n        entityType: Entity.Type.SPHERE,\n        red: 1.0,\n        green: 0.7,\n        blue: 0.2,\n        x: -4,\n        y: 3.5,\n        z: -2,\n        radius: 0.7,\n        specularReflection: 0.2,\n        lambertianReflection: 0.7,\n        ambientColor: 0.1,\n        opacity: 1.0,\n        directionX: 0.07,\n        directionY: 0.02,\n        directionZ: 0.11\n      },\n      {\n        entityType: Entity.Type.SPHERE,\n        red: 0.6,\n        green: 0.7,\n        blue: 0.3,\n        x: -4,\n        y: 3.5,\n        z: -2,\n        radius: 0.4,\n        specularReflection: 0.2,\n        lambertianReflection: 0.6,\n        ambientColor: 0.1,\n        opacity: 1.0,\n        directionX: 0.02,\n        directionY: -0.02,\n        directionZ: -0.05\n      },\n      {\n        entityType: Entity.Type.SPHERE,\n        red: 0.4,\n        green: 0.2,\n        blue: 0.4,\n        x: -4,\n        y: 3.5,\n        z: -2,\n        radius: 0.9,\n        specularReflection: 0.2,\n        lambertianReflection: 0.8,\n        ambientColor: 0.1,\n        opacity: 1.0,\n        directionX: 0.04,\n        directionY: -0.06,\n        directionZ: 0.11\n      }\n    ];\n    var generateRandomSpheres = function(count) {\n      var ary = [];\n      var minDirection = -0.15,\n        maxDirection = 0.15;\n      for (var i = 0; i < count; i++) {\n        ary.push({\n          entityType: Entity.Type.SPHERE,\n          red: rand(0.05, 0.95),\n          green: rand(0.05, 0.95),\n          blue: rand(0.05, 0.95),\n          x: rand(-7, 7),\n          y: rand(0, 7),\n          z: rand(-7, 2),\n          radius: rand(0.3, 2.5),\n          specularReflection: rand(0.1, 0.2),\n          lambertianReflection: rand(0.8, 1),\n          ambientColor: rand(0.1, 0.4),\n          opacity: rand(0, 1),\n          directionX: rand(minDirection, maxDirection),\n          directionY: rand(minDirection, maxDirection),\n          directionZ: rand(minDirection, maxDirection)\n        });\n      }\n      return ary;\n    };\n    var plane_opts = [{\n      entityType: Entity.Type.PLANE,\n      red: 1.0,\n      green: 1.0,\n      blue: 1.0,\n      x: 0,\n      y: 3,\n      z: 0,\n      specularReflection: 0,\n      lambertianReflection: 1,\n      ambientColor: 0.2,\n      opacity: 1.0,\n      directionX: 0,\n      directionY: 0,\n      directionZ: 0,\n      constant: 28\n    }];\n    var opts = generateRandomSpheres(4)\n      .concat(light_opts);\n    var entities = opts.map(function(opt) {\n      return new Entity.Entity(opt);\n    }).map(function(ent) {\n      return ent.toVector();\n    });\n    var eyeVector = vecNormalize(vecSubtract([camera[3], camera[4], camera[5]], [camera[0], camera[1], camera[2]]));\n    var vpRight = vecNormalize(vecCrossProduct(eyeVector, upVector));\n    var vpUp = vecNormalize(vecCrossProduct(vpRight, eyeVector));\n    var canvasHeight = 640;\n    var canvasWidth = 640;\n    var fieldOfViewRadians = Math.PI * (camera[6] / 2) / 180;\n    var heightWidthRatio = canvasHeight / canvasWidth;\n    var halfWidth = Math.tan(fieldOfViewRadians);\n    var halfHeight = heightWidthRatio * halfWidth;\n    var cameraWidth = halfWidth * 2;\n    var cameraHeight = halfHeight * 2;\n    var pixelWidth = cameraWidth / (canvasWidth - 1);\n    var pixelHeight = cameraHeight / (canvasHeight - 1);\n    Scene.generateScene = function() {\n      return {\n        camera: camera,\n        lights: lights,\n        entities: entities,\n        eyeVector: eyeVector,\n        vpRight: vpRight,\n        vpUp: vpUp,\n        canvasHeight: canvasHeight,\n        canvasWidth: canvasWidth,\n        fovRadians: fieldOfViewRadians,\n        heightWidthRatio: heightWidthRatio,\n        halfWidth: halfWidth,\n        halfHeight: halfHeight,\n        cameraWidth: cameraWidth,\n        cameraHeight: cameraHeight,\n        pixelWidth: pixelWidth,\n        pixelHeight: pixelHeight\n      };\n    };\n    Scene.updateSphereCount = function(count) {\n      opts = generateRandomSpheres(count).concat(light_opts);\n      entities = opts.map(function(opt) {\n        return new Entity.Entity(opt);\n      }).map(function(ent) {\n        return ent.toVector();\n      });\n      return entities;\n    };\n  })(Scene || (Scene = {}));\n  var Benchmark;\n  (function(Benchmark_1) {\n    var Benchmark = (function() {\n      function Benchmark() {\n        this.mode = \"\";\n        this.benchmarkDuration = 8000;\n        this.resetBenchmark();\n        this.resultHistory = [];\n        this.framesToRender = 60;\n      }\n      Benchmark.prototype.resetBenchmark = function() {\n        this.isBenchmarking = false;\n        this.latestResults = {\n          mode: this.mode,\n          actualBenchmarkDuration: 0,\n          totalFrameCount: 0,\n          frameRenderDurations: []\n        };\n        this.startTime = null;\n      };\n      Benchmark.prototype.startBenchmark = function(mode, callback) {\n        this.resetBenchmark();\n        this.setMode(mode);\n        this.setCallback(callback);\n        this.startTime = performance.now();\n        this.isBenchmarking = true;\n      };\n      Benchmark.prototype.stopBenchmark = function() {\n        this.isBenchmarking = false;\n        this.latestResults.actualBenchmarkDuration = performance.now() - this.startTime;\n        this.computeMinMaxAvg();\n        this.saveResults();\n        this.callback();\n      };\n      Benchmark.prototype.setCallback = function(callback) {\n        this.callback = callback;\n      };\n      Benchmark.prototype.getResults = function() {\n        return this.latestResults;\n      };\n      Benchmark.prototype.saveResults = function() {\n        this.resultHistory.push(this.latestResults);\n        localStorage.setItem(this.mode, JSON.stringify(this.latestResults));\n        localStorage.setItem(\"history\", JSON.stringify(this.resultHistory));\n      };\n      Benchmark.prototype.displaySpeedup = function(elem) {\n        var cpuResults = JSON.parse(localStorage.getItem('cpu'));\n        var gpuResults = JSON.parse(localStorage.getItem('gpu'));\n        var minSpeedup = cpuResults.minFrameRenderDuration / gpuResults.minFrameRenderDuration;\n        var medianSpeedup = cpuResults.medianFrameRenderDuration / gpuResults.medianFrameRenderDuration;\n        var fpsSpeedup = gpuResults.averageFPS / cpuResults.averageFPS;\n        elem.innerHTML = \"Speedups:\\n      <ul>\\n        <li>FPS speedup: \" + fpsSpeedup + \"</li>\\n        <li>Min frame render speedup: \" + minSpeedup + \"</li>\\n        <li>Median frame render speedup: \" + medianSpeedup + \"</li>\\n      </ul>\";\n      };\n      Benchmark.prototype.displayResults = function(elem) {\n        var dimElem = document.getElementById('grid-dimension');\n        var sphereElem = document.getElementById('sphere-count');\n        elem.innerHTML = \"\\n      <ul>\\n        <li> Mode: \" + this.getResults().mode.toUpperCase() + \" </li>\\n        <li> Dimension: \" + dimElem.innerHTML + \" </li>\\n        <li> Sphere count: \" + sphereElem.innerHTML + \" </li>\\n        <li> Total frames rendered: \" + this.getResults().totalFrameCount + \" </li>\\n        <li> Actual time spent (ms): \" + this.getResults().actualBenchmarkDuration + \" </li>\\n        <li> Average FPS: \" + this.getResults().averageFPS + \" </li>\\n        <li> Frame render time - min (ms): \" + this.getResults().minFrameRenderDuration + \" </li>\\n        <li> Frame render time - median (ms): \" + this.getResults().medianFrameRenderDuration + \" </li>\\n      </ul>\\n      \" + elem.innerHTML + \"\\n      \";\n      };\n      Benchmark.prototype.setMode = function(mode) {\n        this.mode = mode;\n        this.latestResults.mode = mode;\n      };\n      Benchmark.prototype.addFrameGenDuration = function(duration) {\n        if (!this.isBenchmarking) {\n          return;\n        }\n        this.latestResults.frameRenderDurations.push(duration);\n      };\n      Benchmark.prototype.incrementTotalFrameCount = function() {\n        if (!this.isBenchmarking) {\n          return;\n        }\n        this.latestResults.totalFrameCount += 1;\n        if (this.latestResults.totalFrameCount >= this.framesToRender) {\n          this.stopBenchmark();\n        }\n      };\n      Benchmark.prototype.computeMinMaxAvg = function() {\n        this.latestResults.averageFPS = this.getAverageFPS();\n        this.latestResults.minFrameRenderDuration = this.getMinFrameRenderDuration();\n        this.latestResults.maxFrameRenderDuration = this.getMaxFrameRenderDuration();\n        this.latestResults.avgFrameRenderDuration = this.getAvgFrameRenderDuration();\n        this.latestResults.medianFrameRenderDuration = this.getMedianFrameRenderDuration();\n      };\n      Benchmark.prototype.getAverageFPS = function() {\n        var durationSeconds = this.getResults().actualBenchmarkDuration / 1000;\n        var frameCount = this.getResults().totalFrameCount;\n        return frameCount / durationSeconds;\n      };\n      Benchmark.prototype.getMinFrameRenderDuration = function() {\n        return Math.min.apply(Math, this.getResults().frameRenderDurations);\n      };\n      Benchmark.prototype.getMaxFrameRenderDuration = function() {\n        return Math.max.apply(Math, this.getResults().frameRenderDurations);\n      };\n      Benchmark.prototype.getMedianFrameRenderDuration = function() {\n        var count = this.getResults().frameRenderDurations.length;\n        if (count % 2 == 0) {\n          count -= 1;\n        }\n        var median = this.getResults().frameRenderDurations.sort()[Math.floor(count / 2)];\n        return median;\n      };\n      Benchmark.prototype.getAvgFrameRenderDuration = function() {\n        var sum = this.getResults().frameRenderDurations.reduce(function(a, b) {\n          return a + b;\n        });\n        var avg = sum / this.getResults().frameRenderDurations.length;\n        return avg;\n      };\n      return Benchmark;\n    }());\n    Benchmark_1.Benchmark = Benchmark;\n  })(Benchmark || (Benchmark = {}));\n  var Mode;\n  (function(Mode) {\n    Mode[Mode[\"GPU\"] = 0] = \"GPU\";\n    Mode[Mode[\"CPU\"] = 1] = \"CPU\";\n  })(Mode || (Mode = {}));\n  var kernelDimension = kernelDimension || 2;\n  if (sessionStorage.getItem(\"kernelDimension\")) {\n    kernelDimension = parseInt(sessionStorage.getItem(\"kernelDimension\"));\n    var slider = document.getElementById(\"dimension-slider\");\n    slider.value = kernelDimension;\n    var label = document.getElementById(\"grid-dimension\");\n    label.innerHTML = kernelDimension;\n  }\n  var mode = Mode.GPU;\n  var canvasNeedsUpdate = true;\n  var isRunning = true;\n  var stringOfMode = function(mode) {\n    switch (mode) {\n      case Mode.CPU:\n        return \"cpu\";\n      case Mode.GPU:\n        return \"gpu\";\n    }\n  };\n  var togglePause = function(el) {\n    el.value = isRunning ? \"Start\" : \"Pause\";\n    isRunning = !isRunning;\n    if (isRunning) {\n      renderLoop();\n    };\n  };\n  var toggleMode = function() {\n    canvasNeedsUpdate = true;\n    mode = (mode == Mode.GPU) ? Mode.CPU : Mode.GPU;\n    document.getElementById('mode').innerHTML = stringOfMode(mode).toUpperCase();\n  };\n  var updateFPS = function(fps) {\n    var f = document.querySelector(\"#fps\");\n    f.innerHTML = fps.toString();\n  };\n  var updateDimension = function(elem) {\n    renderLoop = null;\n    kernelDimension = parseInt(elem.value);\n    sessionStorage.setItem(\"kernelDimension\", JSON.stringify(kernelDimension));\n    isRunning = false;\n    document.getElementById('grid-dimension').innerHTML = elem.value;\n    _a = generateKernels(kernelDimension, scene), gpuKernels = _a[0], cpuKernels = _a[1];\n    canvasNeedsUpdate = true;\n    renderLoop = renderer(gpuKernels, cpuKernels, scene);\n    setTimeout(function() {\n      isRunning = true;\n      renderLoop();\n    }, 1000);\n    var _a;\n  };\n\n  function generateCanvasGrid(dim) {\n    var canvasContainer = document.getElementById(\"canvas-container\");\n    var divs = [];\n    for (var i = 0; i < dim; i++) {\n      var divElem = document.createElement('div');\n      for (var j = 0; j < dim; j++) {\n        var spanElem = document.createElement('span');\n        spanElem.id = \"canvas\" + (j + (i * dim));\n        spanElem.className = \"canvas\";\n        divElem.appendChild(spanElem);\n      }\n      divs.push(divElem);\n    }\n    for (var i = divs.length - 1; i >= 0; i--) {\n      canvasContainer.appendChild(divs[i]);\n    }\n  }\n\n  function removeCanvasGrid() {\n    var canvasContainer = document.getElementById(\"canvas-container\");\n    while (canvasContainer.lastChild) {\n      for (var i = 0; i < canvasContainer.lastChild.childNodes.length; i++) {\n        canvasContainer.lastChild.childNodes[i] = null;\n      }\n      canvasContainer.removeChild(canvasContainer.lastChild);\n    }\n  }\n  var updateSphereSlider = function(elem) {\n    isRunning = false;\n    document.getElementById('sphere-count').innerHTML = elem.value;\n    updateSphereCount(parseInt(elem.value));\n  };\n  var updateSphereCount = function(count) {\n    var newEntities = Scene.updateSphereCount(count);\n    scene.entities = newEntities;\n    _a = generateKernels(kernelDimension, scene), gpuKernels = _a[0], cpuKernels = _a[1];\n    renderLoop = renderer(gpuKernels, cpuKernels, scene);\n    canvasNeedsUpdate = true;\n    setTimeout(function() {\n      isRunning = true;\n      if (isRunning) {\n        renderLoop();\n      };\n    }, 1000);\n    var _a;\n  };\n  var bm = new Benchmark.Benchmark();\n  var benchmark = function(elem) {\n    elem.value = \"Running..\";\n    elem.disabled = true;\n    var resultsElem = document.getElementById(\"results\");\n    var speedupElem = document.getElementById(\"speedup\");\n    updateFPS(\"Benchmarking..\");\n    bm.startBenchmark(stringOfMode(mode), function() {\n      bm.displayResults(resultsElem);\n      toggleMode();\n      bm.startBenchmark(stringOfMode(mode), function() {\n        bm.displayResults(resultsElem);\n        bm.displaySpeedup(speedupElem);\n        toggleMode();\n        elem.value = \"Benchmark\";\n        elem.disabled = false;\n      });\n    });\n  };\n  var renderer = function(gpuKernels, cpuKernel, scene) {\n    var camera = scene.camera,\n      lights = scene.lights,\n      entities = scene.entities,\n      eyeVector = scene.eyeVector,\n      vpRight = scene.vpRight,\n      vpUp = scene.vpUp,\n      canvasHeight = scene.canvasHeight,\n      canvasWidth = scene.canvasWidth,\n      fovRadians = scene.fovRadians,\n      heightWidthRatio = scene.heightWidthRatio,\n      halfWidth = scene.halfWidth,\n      halfHeight = scene.halfHeight,\n      cameraWidth = scene.cameraWidth,\n      cameraHeight = scene.cameraHeight,\n      pixelWidth = scene.pixelWidth,\n      pixelHeight = scene.pixelHeight;\n    var Movement;\n    (function(Movement) {\n      Movement[Movement[\"Forward\"] = 0] = \"Forward\";\n      Movement[Movement[\"Backward\"] = 1] = \"Backward\";\n      Movement[Movement[\"LeftStrafe\"] = 2] = \"LeftStrafe\";\n      Movement[Movement[\"RightStrafe\"] = 3] = \"RightStrafe\";\n    })(Movement || (Movement = {}));\n    document.onkeydown = function(e) {\n      var keyMap = {\n        87: Movement.Forward,\n        83: Movement.Backward,\n        65: Movement.LeftStrafe,\n        68: Movement.RightStrafe,\n      };\n      var forwardSpeed = 0.2;\n      var backwardSpeed = 0.2;\n      var strafeSpeed = 0.2;\n      switch (keyMap[e.keyCode]) {\n        case Movement.Forward:\n          camera[2] -= forwardSpeed;\n          break;\n        case Movement.Backward:\n          camera[2] += backwardSpeed;\n          break;\n        case Movement.LeftStrafe:\n          camera[0] -= strafeSpeed;\n          break;\n        case Movement.RightStrafe:\n          camera[0] += strafeSpeed;\n          break;\n        default:\n          break;\n      }\n    };\n    var fps = {\n      startTime: 0,\n      frameNumber: 0,\n      totalFrameCount: 0,\n      getFPS: function() {\n        this.totalFrameCount++;\n        this.frameNumber++;\n        var d = new Date().getTime();\n        var currentTime = (d - this.startTime) / 1000;\n        var result = Math.floor(this.frameNumber / currentTime);\n        if (currentTime > 1) {\n          this.startTime = new Date().getTime();\n          this.frameNumber = 0;\n        }\n        return result;\n      }\n    };\n    var nextTick = function() {\n      if (!isRunning) {\n        return;\n      }\n      if (!bm.isBenchmarking) {\n        updateFPS(fps.getFPS().toString());\n      }\n      if (bm.isBenchmarking) {\n        startTime = performance.now();\n      }\n      var startTime, endTime;\n      if (mode == Mode.CPU) {\n        for (var i = 0; i < cpuKernels.length; i++) {\n          var cpuKernel_1 = cpuKernels[i];\n          cpuKernel_1(camera, lights, entities, eyeVector, vpRight, vpUp, canvasHeight, canvasWidth, fovRadians, heightWidthRatio, halfWidth, halfHeight, cameraWidth, cameraHeight, pixelWidth, pixelHeight, i % kernelDimension, Math.floor(i / kernelDimension));\n          if (canvasNeedsUpdate) {\n            var cv = document.getElementById(\"canvas\" + i).childNodes[0];\n            var bdy = cv.parentNode;\n            var newCanvas = cpuKernel_1.canvas;\n            bdy.replaceChild(newCanvas, cv);\n          }\n        }\n        if (canvasNeedsUpdate) {\n          canvasNeedsUpdate = false;\n        }\n      } else {\n        for (var i = 0; i < gpuKernels.length; i++) {\n          var gpuKernel = gpuKernels[i];\n          gpuKernel(camera, lights, entities, eyeVector, vpRight, vpUp, canvasHeight, canvasWidth, fovRadians, heightWidthRatio, halfWidth, halfHeight, cameraWidth, cameraHeight, pixelWidth, pixelHeight, i % kernelDimension, Math.floor(i / kernelDimension));\n          if (canvasNeedsUpdate) {\n            var cv = document.getElementById(\"canvas\" + i).childNodes[0];\n            var bdy = cv.parentNode;\n            var newCanvas = gpuKernel.canvas;\n            bdy.replaceChild(newCanvas, cv);\n          }\n        }\n        if (canvasNeedsUpdate) {\n          canvasNeedsUpdate = false;\n        }\n      }\n      for (var idx = 0; idx < entities.length; idx++) {\n        entities[idx] = moveEntity(canvasWidth, canvasWidth, canvasWidth, entities[idx]);\n      }\n      entities = checkSphereSphereCollision(entities);\n      if (bm.isBenchmarking) {\n        var timeTaken = performance.now() - startTime;\n        bm.addFrameGenDuration(timeTaken);\n        bm.incrementTotalFrameCount();\n        setTimeout(nextTick, 1);\n      } else {\n        requestAnimationFrame(nextTick);\n      }\n    };\n    var checkSphereSphereCollision = function(allEntities) {\n      for (var first = 0; first < allEntities.length - 1; first++) {\n        if (allEntities[first][0] !== Entity.Type.SPHERE) {\n          continue;\n        }\n        var sphere = allEntities[first];\n        for (var second = first + 1; second < allEntities.length; second++) {\n          if (allEntities[second][0] !== Entity.Type.SPHERE) {\n            continue;\n          }\n          var other = allEntities[second];\n          var distance = vecMagnitude(vecSubtract([sphere[1], sphere[2], sphere[3]], [other[1], other[2], other[3]]));\n          var radiusSum = sphere[7] + other[7];\n          if (distance < radiusSum + (0.05 * radiusSum)) {\n            var basisVector = vecNormalize(vecSubtract([sphere[1], sphere[2], sphere[3]], [other[1], other[2], other[3]]));\n            var v1 = [sphere[15], sphere[16], sphere[17]];\n            var x1 = vecDotProduct(basisVector, v1);\n            var v1x = vecScale(basisVector, x1);\n            var v1y = vecSubtract(v1, v1x);\n            var m1 = 1;\n            basisVector = vecScale(basisVector, -1);\n            var v2 = [other[15], other[16], other[17]];\n            var x2 = vecDotProduct(basisVector, v2);\n            var v2x = vecScale(basisVector, x2);\n            var v2y = vecSubtract(v2, v2x);\n            var m2 = 1;\n            var newSphereVelocity = vecAdd3(vecScale(v1x, (m1 - m2) / (m1 + m2)), vecScale(v2x, (2 * m2) / (m1 + m2)), v1y);\n            allEntities[first][15] = newSphereVelocity[0];\n            allEntities[first][16] = newSphereVelocity[1];\n            allEntities[first][17] = newSphereVelocity[2];\n            var otherSphereVelocity = vecAdd3(vecScale(v1x, (2 * m2) / (m1 + m2)), vecScale(v2x, (m2 - m1) / (m1 + m2)), v2y);\n            allEntities[second][15] = otherSphereVelocity[0];\n            allEntities[second][16] = otherSphereVelocity[1];\n            allEntities[second][17] = otherSphereVelocity[2];\n            for (var colIdx = 8; colIdx < 11; colIdx++) {\n              allEntities[first][colIdx] = rand(0, 1);\n              allEntities[second][colIdx] = rand(0, 1);\n            }\n          }\n        }\n      }\n      return allEntities;\n    };\n    var moveEntity = function(width, height, depth, entity) {\n      var reflect = function(entity, normal) {\n        var incidentVec = [entity[15], entity[16], entity[17]];\n        var dp = vecDotProduct(incidentVec, normal);\n        var tmp = vecSubtract(incidentVec, vecScale(normal, 2 * dp));\n        return tmp;\n      };\n      entity[1] += entity[15];\n      entity[2] += entity[16];\n      entity[3] += entity[17];\n      if (entity[1] < -7) {\n        _a = reflect(entity, [1, 0, 0]), entity[15] = _a[0], entity[16] = _a[1], entity[17] = _a[2];\n      }\n      if (entity[1] > 7) {\n        _b = reflect(entity, [-1, 0, 0]), entity[15] = _b[0], entity[16] = _b[1], entity[17] = _b[2];\n      }\n      if (entity[2] < -7) {\n        _c = reflect(entity, [0, 1, 0]), entity[15] = _c[0], entity[16] = _c[1], entity[17] = _c[2];\n      }\n      if (entity[2] > 7) {\n        _d = reflect(entity, [0, -1, 0]), entity[15] = _d[0], entity[16] = _d[1], entity[17] = _d[2];\n      }\n      if (entity[3] < -15) {\n        _e = reflect(entity, [0, 0, 1]), entity[15] = _e[0], entity[16] = _e[1], entity[17] = _e[2];\n      }\n      if (entity[3] > 7) {\n        _f = reflect(entity, [0, 0, -1]), entity[15] = _f[0], entity[16] = _f[1], entity[17] = _f[2];\n      }\n      return entity;\n      var _a, _b, _c, _d, _e, _f;\n    };\n    return nextTick;\n  };\n  var createKernel = function(modeNumber, scene) {\n    var mode = stringOfMode(modeNumber);\n    var opt = {\n      mode: mode,\n      output: [Math.floor(scene.canvasWidth / kernelDimension), Math.floor(scene.canvasHeight / kernelDimension)],\n      graphical: true,\n      safeTextureReadHack: false,\n      constants: {\n        ENTITY_COUNT: scene.entities.length,\n        LIGHT_COUNT: scene.lights.length,\n        SPHERE: Entity.Type.SPHERE,\n        PLANE: Entity.Type.PLANE,\n        LIGHTSPHERE: Entity.Type.LIGHTSPHERE\n      }\n    };\n    var gpu = new GPU({ mode });\n    addFunctions(gpu, vectorFunctions);\n    addFunctions(gpu, utilityFunctions);\n    return gpu.createKernel(function(camera, lights, entities, eyeVector, vpRight, vpUp, canvasHeight, canvasWidth, fovRadians, heightWidthRatio, halfWidth, halfHeight, cameraWidth, cameraHeight, pixelWidth, pixelHeight, xOffset, yOffset) {\n      var x = this.thread.x + (this.output.x * xOffset);\n      var y = this.thread.y + (this.output.y * yOffset);\n      var xCompX = vpRight[0] * (x * pixelWidth - halfWidth);\n      var xCompY = vpRight[1] * (x * pixelWidth - halfWidth);\n      var xCompZ = vpRight[2] * (x * pixelWidth - halfWidth);\n      var yCompX = vpUp[0] * (y * pixelHeight - halfHeight);\n      var yCompY = vpUp[1] * (y * pixelHeight - halfHeight);\n      var yCompZ = vpUp[2] * (y * pixelHeight - halfHeight);\n      var rayPtX = camera[0];\n      var rayPtY = camera[1];\n      var rayPtZ = camera[2];\n      var rayVecX = eyeVector[0] + xCompX + yCompX;\n      var rayVecY = eyeVector[1] + xCompY + yCompY;\n      var rayVecZ = eyeVector[2] + xCompZ + yCompZ;\n      var normRayVecX = normalizeX(rayVecX, rayVecY, rayVecZ);\n      var normRayVecY = normalizeY(rayVecX, rayVecY, rayVecZ);\n      var normRayVecZ = normalizeZ(rayVecX, rayVecY, rayVecZ);\n      var red = 0.30;\n      var green = 0.35;\n      var blue = 0.31;\n      var nearestEntityIndex = -1;\n      var maxEntityDistance = Math.pow(2, 32);\n      var nearestEntityDistance = Math.pow(2, 32);\n      for (var i = 0; i < this.constants.ENTITY_COUNT; i++) {\n        var distance = -1;\n        var entityType = entities[i][0];\n        if (entityType == this.constants.SPHERE ||\n          entityType == this.constants.LIGHTSPHERE) {\n          distance = sphereIntersection(entities[i][1], entities[i][2], entities[i][3], entities[i][7], rayPtX, rayPtY, rayPtZ, normRayVecX, normRayVecY, normRayVecZ);\n        } else if (entityType == this.constants.PLANE) {\n          distance = planeIntersection(entities[i][1], entities[i][2], entities[i][3], entities[i][18], rayPtX, rayPtY, rayPtZ, normRayVecX, normRayVecY, normRayVecZ);\n        }\n        if (distance >= 0 && distance < nearestEntityDistance) {\n          nearestEntityIndex = i;\n          nearestEntityDistance = distance;\n          red = entities[i][8];\n          green = entities[i][9];\n          blue = entities[i][10];\n        }\n      }\n      this.color(red, green, blue);\n      if (nearestEntityIndex >= 0) {\n        var entityPtX = entities[nearestEntityIndex][1];\n        var entityPtY = entities[nearestEntityIndex][2];\n        var entityPtZ = entities[nearestEntityIndex][3];\n        var entityRed = entities[nearestEntityIndex][8];\n        var entityGreen = entities[nearestEntityIndex][9];\n        var entityBlue = entities[nearestEntityIndex][10];\n        var intersectPtX = rayPtX + normRayVecX * nearestEntityDistance;\n        var intersectPtY = rayPtY + normRayVecY * nearestEntityDistance;\n        var intersectPtZ = rayPtZ + normRayVecZ * nearestEntityDistance;\n        var sphereNormPtX = sphereNormalX(entityPtX, entityPtY, entityPtZ, intersectPtX, intersectPtY, intersectPtZ);\n        var sphereNormPtY = sphereNormalY(entityPtX, entityPtY, entityPtZ, intersectPtX, intersectPtY, intersectPtZ);\n        var sphereNormPtZ = sphereNormalZ(entityPtX, entityPtY, entityPtZ, intersectPtX, intersectPtY, intersectPtZ);\n        var lambertRed = 0,\n          lambertGreen = 0,\n          lambertBlue = 0;\n        for (var i = 0; i < this.constants.LIGHT_COUNT; i++) {\n          var lightPtX = lights[i][0];\n          var lightPtY = lights[i][1];\n          var lightPtZ = lights[i][2];\n          var vecToLightX = sphereNormalX(intersectPtX, intersectPtY, intersectPtZ, lightPtX, lightPtY, lightPtZ);\n          var vecToLightY = sphereNormalY(intersectPtX, intersectPtY, intersectPtZ, lightPtX, lightPtY, lightPtZ);\n          var vecToLightZ = sphereNormalZ(intersectPtX, intersectPtY, intersectPtZ, lightPtX, lightPtY, lightPtZ);\n          var shadowCast = -1;\n          var lambertAmount = 0;\n          var entityLambert = entities[nearestEntityIndex][11];\n          for (var j = 0; j < this.constants.ENTITY_COUNT; j++) {\n            if (entities[j][0] == this.constants.SPHERE) {\n              var distance = sphereIntersection(entities[j][1], entities[j][2], entities[j][3], entities[j][7], intersectPtX, intersectPtY, intersectPtZ, vecToLightX, vecToLightY, vecToLightZ);\n              if (distance > -0.005) {\n                shadowCast = 1;\n              }\n            }\n          }\n          if (shadowCast < 0) {\n            var contribution = dotProduct(vecToLightX, vecToLightY, vecToLightZ, sphereNormPtX, sphereNormPtY, sphereNormPtZ);\n            if (contribution > 0) {\n              lambertAmount += contribution;\n            }\n          }\n          lambertAmount = Math.min(1, lambertAmount);\n          lambertRed += entityRed * lambertAmount * entityLambert;\n          lambertGreen += entityGreen * lambertAmount * entityLambert;\n          lambertBlue += entityBlue * lambertAmount * entityLambert;\n        }\n        var specularRed = 0,\n          specularBlue = 0,\n          specularGreen = 0;\n        var incidentRayVecX = rayVecX;\n        var incidentRayVecY = rayVecY;\n        var incidentRayVecZ = rayVecZ;\n        var reflectedPtX = intersectPtX;\n        var reflectedPtY = intersectPtY;\n        var reflectedPtZ = intersectPtZ;\n        var depthLimit = 3;\n        var depth = 0;\n        var entitySpecular = entities[nearestEntityIndex][13];\n        while (depth < depthLimit) {\n          var reflectedVecX = -reflectVecX(incidentRayVecX, incidentRayVecY, incidentRayVecZ, sphereNormPtX, sphereNormPtY, sphereNormPtZ);\n          var reflectedVecY = -reflectVecY(incidentRayVecX, incidentRayVecY, incidentRayVecZ, sphereNormPtX, sphereNormPtY, sphereNormPtZ);\n          var reflectedVecZ = -reflectVecZ(incidentRayVecX, incidentRayVecY, incidentRayVecZ, sphereNormPtX, sphereNormPtY, sphereNormPtZ);\n          var nearestEntityIndexSpecular = -1;\n          var maxEntityDistanceSpecular = Math.pow(2, 32);\n          var nearestEntityDistanceSpecular = Math.pow(2, 32);\n          for (var i = 0; i < this.constants.ENTITY_COUNT; i++) {\n            if (entities[i][0] == this.constants.SPHERE) {\n              var distance = sphereIntersection(entities[i][1], entities[i][2], entities[i][3], entities[i][7], reflectedPtX, reflectedPtY, reflectedPtZ, reflectedVecX, reflectedVecY, reflectedVecZ);\n              if (distance >= 0 && distance < nearestEntityDistance) {\n                nearestEntityIndexSpecular = i;\n                nearestEntityDistanceSpecular = distance;\n              }\n            }\n          }\n          if (nearestEntityIndexSpecular >= 0) {\n            entityPtX = entities[nearestEntityIndexSpecular][1];\n            entityPtY = entities[nearestEntityIndexSpecular][2];\n            entityPtZ = entities[nearestEntityIndexSpecular][3];\n            specularRed += entities[nearestEntityIndexSpecular][8] * entitySpecular;\n            specularGreen += entities[nearestEntityIndexSpecular][9] * entitySpecular;\n            specularBlue += entities[nearestEntityIndexSpecular][10] * entitySpecular;\n            reflectedPtX = reflectedPtX + normalizeX(reflectedVecX, reflectedVecY, reflectedVecZ) * nearestEntityDistance;\n            reflectedPtY = reflectedPtY + normalizeY(reflectedVecX, reflectedVecY, reflectedVecZ) * nearestEntityDistance;\n            reflectedPtZ = reflectedPtZ + normalizeZ(reflectedVecX, reflectedVecY, reflectedVecZ) * nearestEntityDistance;\n            sphereNormPtX = sphereNormalX(entityPtX, entityPtY, entityPtZ, reflectedPtX, reflectedPtY, reflectedPtZ);\n            sphereNormPtY = sphereNormalZ(entityPtX, entityPtY, entityPtZ, reflectedPtX, reflectedPtY, reflectedPtZ);\n            sphereNormPtY = sphereNormalZ(entityPtX, entityPtY, entityPtZ, reflectedPtX, reflectedPtY, reflectedPtZ);\n            incidentRayVecX = reflectedVecX;\n            incidentRayVecY = reflectedVecY;\n            incidentRayVecZ = reflectedVecZ;\n            depth += 1;\n          } else {\n            depth = depthLimit;\n          }\n        }\n        var ambient = entities[nearestEntityIndex][14];\n        this.color(lambertRed + (lambertRed * specularRed) + entityRed * ambient, lambertGreen + (lambertGreen * specularGreen) + entityGreen * ambient, lambertBlue + (lambertBlue * specularBlue) + entityBlue * ambient);\n      }\n    }, opt);\n  };\n  var generateKernels = function(dim, scene) {\n    removeCanvasGrid();\n    generateCanvasGrid(dim);\n    var kernelCount = dim * dim;\n    var gpuKernels = [];\n    var cpuKernels = [];\n    for (var i = 0; i < kernelCount; i++) {\n      var kernel = createKernel(Mode.GPU, scene);\n      gpuKernels.push(kernel);\n      document.getElementById('canvas' + i).appendChild(kernel.canvas);\n      cpuKernels.push(createKernel(Mode.CPU, scene));\n    }\n    return [gpuKernels, cpuKernels];\n  };\n  var addFunctions = function(gpu, functions) {\n    return functions.forEach(function(f) {\n      return gpu.addFunction(f);\n    });\n  };\n  var scene = Scene.generateScene();\n  var _a = generateKernels(kernelDimension, scene),\n    gpuKernels = _a[0],\n    cpuKernels = _a[1];\n  var renderLoop = renderer(gpuKernels, cpuKernels, scene);\n  window.onload = renderLoop;\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/random.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>GPU.js Random Examples</title>\n</head>\n<body>\n  <h2>CPU Random</h2>\n  <canvas id=\"cpu-random-output\"></canvas>\n  <h2>WebGL1 Random</h2>\n  <canvas id=\"web-gl-random-output\"></canvas>\n  <h2>WebGL2 Random</h2>\n  <canvas id=\"web-gl2-random-output\"></canvas>\n</body>\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  let cpu, webGL, webGL2;\n\n  cpu = new GPU({\n    mode: 'cpu',\n    canvas: document.getElementById('cpu-random-output')\n  });\n  try {\n    webGL = new GPU({\n      mode: 'webgl',\n      canvas: document.getElementById('web-gl-random-output')\n    });\n  } catch (e) {}\n  try {\n    webGL2 = new GPU({\n      mode: 'webgl2',\n      canvas: document.getElementById('web-gl2-random-output')\n    });\n  } catch (e) {}\n\n  function drawRandomFunction() {\n    this.color(Math.random(), Math.random(), Math.random());\n  }\n\n  const cpuDrawRandom = cpu.createKernel(drawRandomFunction)\n    .setGraphical(true)\n    .setOutput([100, 100]);\n\n  const webGLDrawRandom = webGL\n    ? webGL.createKernel(drawRandomFunction)\n      .setGraphical(true)\n      .setOutput([100, 100])\n    : () => {};\n\n  const webGL2DrawRandom = webGL2\n    ? webGL2.createKernel(drawRandomFunction)\n      .setGraphical(true)\n      .setOutput([100, 100])\n    : () => {};\n\n  function draw() {\n    cpuDrawRandom();\n    webGLDrawRandom();\n    webGL2DrawRandom();\n\n    requestAnimationFrame(draw);\n  }\n\n  requestAnimationFrame(draw);\n</script>\n</html>\n"
  },
  {
    "path": "examples/raster-globe/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Raster projection with GPU.js</title>\n  <script src=\"../../dist/gpu-browser.min.js\"></script>\n</head>\n<body>\n<h1>Raster projection with GPU.js from <a href=\"https://observablehq.com/d/b70d084526a1a764\">https://observablehq.com/d/b70d084526a1a764</a></h1>\n<div id=\"log-fps\"></div>\n</body>\n<script>\n  function frac(n) {\n    return n - Math.floor(n);\n  }\n  function applyRotation(rotatex, rotatey, rotatez, lambda, phi) {\n    const degrees = 57.29577951308232;\n\n    lambda = lambda / degrees;\n    phi = phi / degrees;\n\n    const cosphi = Math.cos(phi),\n      x = Math.cos(lambda) * cosphi,\n      y = Math.sin(lambda) * cosphi,\n      z = Math.sin(phi);\n\n    // inverse rotation\n    const deltaLambda = rotatex / degrees; // rotate[0]\n    const deltaPhi = -rotatey / degrees;   // rotate[1]\n    const deltaGamma = -rotatez / degrees; // rotate[2]\n\n    const cosDeltaPhi = Math.cos(deltaPhi),\n      sinDeltaPhi = Math.sin(deltaPhi),\n      cosDeltaGamma = Math.cos(deltaGamma),\n      sinDeltaGamma = Math.sin(deltaGamma);\n\n    let k = z * cosDeltaGamma - y * sinDeltaGamma;\n\n    lambda = Math.atan2(\n      y * cosDeltaGamma + z * sinDeltaGamma,\n      x * cosDeltaPhi + k * sinDeltaPhi\n    ) - deltaLambda;\n    k = k * cosDeltaPhi - x * sinDeltaPhi;\n\n    phi = Math.asin(k);\n\n    lambda *= degrees;\n    phi *= degrees;\n    return [lambda, phi];\n  }\n  // the kernel runs for each pixel, with:\n  // - this.thread.x = horizontal position in pixels from the left edge\n  // - this.thread.y = vertical position in pixels from the bottom edge (*opposite of canvas*)\n  const kernel = function(pixels, rotate0, rotate1, rotate2, scale) {\n\n    // azimuthal equal area\n    function radius(rho) {\n      return 2.0 * Math.asin(rho / 2.0);\n    }\n    // orthographic\n    function __radius(rho) {\n      return Math.asin(rho);\n    }\n\n    // equirectangular projection (reads the (lon,lat) color from the base image)\n    function pixelx(lon, srcw) {\n      lon = frac((lon + 180) / 360);\n      return Math.floor(lon * srcw);\n    }\n    function pixely(lat, srch) {\n      lat = frac((lat + 90) / 180);\n      return Math.floor(lat * srch);\n    }\n\n    const x = (this.thread.x / this.constants.w - 1 / 2) / scale,\n      y = ((this.thread.y - this.constants.h / 2) / this.constants.w) / scale;\n\n    // inverse projection\n    const rho = Math.sqrt(x * x + y * y) + 1e-12;\n\n    const c = radius(rho),\n      sinc = Math.sin(c),\n      cosc = Math.cos(c);\n\n    // x, y :  pixel coordinates if rotation was null\n    const lambda = Math.atan2(x * sinc, rho * cosc) * 57.29577951308232;\n    const z = y * sinc / rho;\n    if (Math.abs(z) < 1) {\n      const phi = Math.asin(z) * 57.29577951308232;\n\n      // apply rotation\n      const rotation = applyRotation(rotate0, rotate1, rotate2, lambda, phi);\n      const lambdan = rotation[0];\n      const phin = rotation[1];\n      //var n = n0(lambda, phi, this.constants.srcw, this.constants.srch);\n      //this.color(pixels[n]/256, pixels[n+1]/256,pixels[n+2]/256,1);\n      const pixel = pixels[pixely(phin, this.constants.srch)][pixelx(lambdan, this.constants.srcw)];\n      this.color(pixel[0], pixel[1], pixel[2], 1);\n    }\n  };\n  const gpu = new GPU();\n  const logFps = document.querySelector('#log-fps');\n  const image = new Image();\n  image.src = './earth-map.jpg';\n  image.onload = () => {\n    const w = 975;\n    const h = 975;\n    const render = gpu\n      .createKernel(kernel, { functions: [applyRotation, frac] })\n      .setConstants({ w, h, srcw: image.width, srch: image.height  })\n      .setOutput([w, h])\n      .setTactic('precision')\n      .setGraphical(true);\n    const canvas = render.canvas;\n    document.body.appendChild(canvas);\n    let lastCalledTime;\n    let fps;\n    function callRender() {\n      let r0 = (-Date.now() / 30) % 360,\n        r1 = 35 * Math.sin((-Date.now() / 1030) % 360),\n        r2 = 0,\n        scale = 0.49;\n      render(image, r0, r1, r2, scale);\n      delta = (Date.now() - lastCalledTime)/1000;\n      lastCalledTime = Date.now();\n      fps = 1/delta;\n      logFps.innerHTML = fps.toFixed(0) + ' FPS';\n      window.requestAnimationFrame(() => {\n        callRender();\n      });\n    }\n    callRender();\n  };\n</script>\n</html>\n"
  },
  {
    "path": "examples/raytracer.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width initial-scale=1\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <title>GPU.js Raytracer</title>\n  <meta name=\"author\" content=\"Stacey Tay\">\n  <meta name=\"description\" content=\"A simple raytracer built with GPU.js.\">\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\n    html, body {\n      font-family: 'Open Sans', sans-serif;\n    }\n    canvas {\n      display: block;\n      margin: auto;\n    }\n    .center {\n      text-align: center;\n    }\n    #container {\n      margin: auto;\n      max-width: 600px;\n    }\n  </style>\n</head>\n<body>\n<div id=\"container\">\n  <div class=\"center\">\n    <p>\n      From https://staceytay.com/raytracer/. A simple ray tracer with Lambertian and specular reflection,\n      built with <a href=\"http://gpu.rocks/\">GPU.js</a>. Read more\n      about ray tracing and GPU.js in\n      my <a href=\"http://staceytay.com/2016/04/20/a-parallelized-ray-tracer-in-the-browser.html\">blog\n      post</a>. Code available\n      on <a href=\"https://github.com/staceytay/raytracer/\">GitHub</a>.\n    </p>\n  </div>\n  <div class=\"center\">\n    <label for=\"cpu\"><input type=\"radio\" name=\"mode\" value=\"cpu\" id=\"cpu\">CPU</label>\n    <label for=\"gpu\"><input type=\"radio\" name=\"mode\" value=\"gpu\" id=\"gpu\">GPU</label>\n  </div>\n  <div class=\"center\">\n    <label for=\"lambert\"><input checked=\"checked\" type=\"checkbox\" id=\"lambert\" value=\"lambert\">Lambertian reflectance</label><br>\n    <label for=\"specular\"><input checked=\"checked\" type=\"checkbox\" id=\"specular\" value=\"specular\">Specular reflection</label>\n  </div>\n  <div class=\"center\">\n    <input type=\"button\" value=\"Pause\" onclick=\"togglePause ()\" id=\"pause\" />\n  </div>\n  <div class=\"center\">\n    <span id=\"fps\"></span><span> fps</span>\n  </div>\n  <div id=\"canvas-holder\"></div>\n</div>\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  class Vector {\n    constructor(x, y, z) {\n      this.x = x;\n      this.y = y;\n      this.z = z;\n    }\n    toArray() {\n      return [this.x, this.y, this.z];\n    }\n    static times(k, v) {\n      return new Vector(k * v.x, k * v.y, k * v.z);\n    }\n    static minus(v1, v2) {\n      return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);\n    }\n    static magnitude(v) {\n      return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);\n    }\n    static norm(v) {\n      const mag = Vector.magnitude(v);\n      const div = (mag === 0) ? Infinity : 1.0 / mag;\n      return Vector.times(div, v);\n    }\n    static cross(v1, v2) {\n      return new Vector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);\n    }\n  }\n  const stringOfMode = function (mode) {\n    switch (mode) {\n      case 1: return 'cpu';\n      case 0: return 'gpu';\n    }\n  };\n  const canvasHeight = (window.innerWidth < 600) ? (window.innerWidth - 20) : 600;\n  const canvasWidth = canvasHeight;\n  const initialMode = 1;\n  const camera = [\n    0, 0, 16, 0, 0, 1, 45\n  ];\n  const lights = [\n    [-16, 16, 8],\n    [16, 16, 8],\n  ];\n  const things = [\n    [0, 13,\n      1.0, 0.0, 0.0,\n      0.3, 0.7, 0.2, 1.0,\n      -2, 2, -2, 1],\n    [0, 13,\n      0.0, 1.0, 0.0,\n      0.3, 0.7, 0.2, 1.0,\n      0, 2, 0, 1],\n    [0, 13,\n      0.0, 0.0, 1.0,\n      0.3, 0.7, 0.2, 1.0,\n      2, 2, 2, 1],\n    [0, 13,\n      0.0, 1.0, 1.0,\n      0.3, 0.7, 0.2, 1.0,\n      -2, -2, 2, 1],\n    [0, 13,\n      1.0, 0.0, 1.0,\n      0.3, 0.7, 0.2, 1.0,\n      0, -2, 0, 1],\n    [0, 13,\n      1.0, 1.0, 0.0,\n      0.3, 0.7, 0.2, 1.0,\n      2, -2, -2, 1],\n  ];\n  const constants = {\n    LIGHTSCOUNT: lights.length,\n    THINGSCOUNT: things.length\n  };\n  const opt = function (mode) {\n    return {\n      constants: constants,\n      graphical: true,\n      mode: stringOfMode(mode),\n      output: [canvasWidth, canvasHeight],\n      tactic: 'precision',\n    };\n  };\n  const gpu = new GPU();\n  const cpu = new GPU({ mode: 'cpu'});\n  function vectorDotProduct(V1x, V1y, V1z, V2x, V2y, V2z) {\n    return (V1x * V2x) + (V1y * V2y) + (V1z * V2z);\n  }\n  function unitVectorX(Vx, Vy, Vz) {\n    const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));\n    const div = 1.0 / magnitude;\n    return div * Vx;\n  }\n  function unitVectorY(Vx, Vy, Vz) {\n    const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));\n    const div = 1.0 / magnitude;\n    return div * Vy;\n  }\n  function unitVectorZ(Vx, Vy, Vz) {\n    const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));\n    const div = 1.0 / magnitude;\n    return div * Vz;\n  }\n  function sphereNormalX(Sx, Sy, Sz, radius, Px, Py, Pz) {\n    const SPx = Px - Sx;\n    const SPy = Py - Sy;\n    const SPz = Pz - Sz;\n    const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);\n    let div = Infinity;\n    if (magnitude > 0) {\n      div = 1.0 / magnitude;\n    }\n    return div * SPx;\n  }\n  function sphereNormalY(Sx, Sy, Sz, radius, Px, Py, Pz) {\n    const SPx = Px - Sx;\n    const SPy = Py - Sy;\n    const SPz = Pz - Sz;\n    const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);\n    let div = Infinity;\n    if (magnitude > 0) {\n      div = 1.0 / magnitude;\n    }\n    return div * SPy;\n  }\n  function sphereNormalZ(Sx, Sy, Sz, radius, Px, Py, Pz) {\n    const SPx = Px - Sx;\n    const SPy = Py - Sy;\n    const SPz = Pz - Sz;\n    const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);\n    let div = Infinity;\n    if (magnitude > 0) {\n      div = 1.0 / magnitude;\n    }\n    return div * SPz;\n  }\n  function vectorReflectX(Vx, Vy, Vz, Nx, Ny, Nz) {\n    const V1x = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Nx;\n    return (V1x * 2) - Vx;\n  }\n  function vectorReflectY(Vx, Vy, Vz, Nx, Ny, Nz) {\n    const V1y = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Ny;\n    return (V1y * 2) - Vy;\n  }\n  function vectorReflectZ(Vx, Vy, Vz, Nx, Ny, Nz) {\n    const V1z = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Nz;\n    return (V1z * 2) - Vz;\n  }\n  function sphereIntersectionDistance(Sx, Sy, Sz, radius, Ex, Ey, Ez, Vx, Vy, Vz) {\n    const EOx = Sx - Ex;\n    const EOy = Sy - Ey;\n    const EOz = Sz - Ez;\n    const v = (EOx * Vx) + (EOy * Vy) + (EOz * Vz);\n    const discriminant = (radius * radius)\n      - ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))\n      + (v * v);\n    if (discriminant < 0) {\n      return Infinity;\n    }\n    else {\n      return v - Math.sqrt(discriminant);\n    }\n  }\n  const kernelFunctions = [\n    vectorDotProduct,\n    unitVectorX, unitVectorY, unitVectorZ,\n    sphereNormalX, sphereNormalY, sphereNormalZ,\n    vectorReflectX, vectorReflectY, vectorReflectZ,\n    sphereIntersectionDistance\n  ];\n  kernelFunctions.forEach(function (f) {\n    cpu.addFunction(f);\n    gpu.addFunction(f);\n  });\n  const createKernel = function (mode) {\n    return (mode === 0 ? gpu : cpu).createKernel(function (camera, lights, things, eyeV, rightV, upV, halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection) {\n      const x = this.thread.x;\n      const y = this.thread.y;\n      const rayPx = camera[0];\n      const rayPy = camera[1];\n      const rayPz = camera[2];\n      const xCompVx = ((x * pixelWidth) - halfWidth) * rightV[0];\n      const xCompVy = ((x * pixelWidth) - halfWidth) * rightV[1];\n      const xCompVz = ((x * pixelWidth) - halfWidth) * rightV[2];\n      const yCompVx = ((y * pixelHeight) - halfHeight) * upV[0];\n      const yCompVy = ((y * pixelHeight) - halfHeight) * upV[1];\n      const yCompVz = ((y * pixelHeight) - halfHeight) * upV[2];\n      const sumVx = eyeV[0] + xCompVx + yCompVx;\n      const sumVy = eyeV[1] + xCompVy + yCompVy;\n      const sumVz = eyeV[2] + xCompVz + yCompVz;\n      const rayVx = unitVectorX(sumVx, sumVy, sumVz);\n      const rayVy = unitVectorY(sumVx, sumVy, sumVz);\n      const rayVz = unitVectorZ(sumVx, sumVy, sumVz);\n      let closest = this.constants.THINGSCOUNT;\n      let closestDistance = Infinity;\n      for (let i = 0; i < this.constants.THINGSCOUNT; i++) {\n        const distance = sphereIntersectionDistance(things[i][9], things[i][10], things[i][11], things[i][12], rayPx, rayPy, rayPz, rayVx, rayVy, rayVz);\n        if (distance < closestDistance) {\n          closest = i;\n          closestDistance = distance;\n        }\n      }\n      if (closestDistance < Infinity) {\n        const px = rayPx + rayVx * closestDistance;\n        const py = rayPy + rayVy * closestDistance;\n        const pz = rayPz + rayVz * closestDistance;\n        const sx = things[closest][9];\n        const sy = things[closest][10];\n        const sz = things[closest][11];\n        const sRadius = things[closest][12];\n        const snVx = sphereNormalX(sx, sy, sz, sRadius, px, py, pz);\n        const snVy = sphereNormalY(sx, sy, sz, sRadius, px, py, pz);\n        const snVz = sphereNormalZ(sx, sy, sz, sRadius, px, py, pz);\n        const sRed = things[closest][2];\n        const sGreen = things[closest][3];\n        const sBlue = things[closest][4];\n        const ambient = things[closest][7];\n        const lambert = things[closest][6];\n        let lambertAmount = 0;\n        if (lambertianReflectance > 0 && lambert > 0) {\n          for (let i = 0; i < this.constants.LIGHTSCOUNT; i++) {\n            const LPx = px - lights[i][0];\n            const LPy = py - lights[i][1];\n            const LPz = pz - lights[i][2];\n            const uLPx = unitVectorX(LPx, LPy, LPz);\n            const uLPy = unitVectorY(LPx, LPy, LPz);\n            const uLPz = unitVectorZ(LPx, LPy, LPz);\n            let closestDistance_1 = Infinity;\n            for (let j = 0; j < this.constants.THINGSCOUNT; j++) {\n              let distance = Infinity;\n              const EOx = things[j][9] - px;\n              const EOy = things[j][10] - py;\n              const EOz = things[j][11] - pz;\n              const v = (EOx * uLPx) + (EOy * uLPy) + (EOz * uLPz);\n              const radius = things[j][12];\n              const discriminant = (radius * radius)\n                - ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))\n                + (v * v);\n              if (discriminant >= 0) {\n                distance = v - Math.sqrt(discriminant);\n              }\n              if (distance < closestDistance_1) {\n                closestDistance_1 = distance;\n              }\n            }\n            if (closestDistance_1 > -0.005) {\n              const PLx = -LPx;\n              const PLy = -LPy;\n              const PLz = -LPz;\n              const uPLx = unitVectorX(PLx, PLy, PLz);\n              const uPLy = unitVectorY(PLx, PLy, PLz);\n              const uPLz = unitVectorZ(PLx, PLy, PLz);\n              const contribution = vectorDotProduct(uPLx, uPLy, uPLz, snVx, snVy, snVz);\n              if (contribution > 0) {\n                lambertAmount += contribution;\n              }\n            }\n          }\n        }\n        if (lambertianReflectance > 0) {\n          lambertAmount = Math.min(1, lambertAmount);\n        }\n        const specular = things[closest][5];\n        let cVx = 0;\n        let cVy = 0;\n        let cVz = 0;\n        if (specularReflection > 0 && specular > 0) {\n          const rRayPx = px;\n          const rRayPy = py;\n          const rRayPz = pz;\n          const rRayVx = vectorReflectX(rayVx, rayVy, rayVz, snVx, snVy, snVz);\n          const rRayVy = vectorReflectY(rayVx, rayVy, rayVz, snVx, snVy, snVz);\n          const rRayVz = vectorReflectZ(rayVx, rayVy, rayVz, snVx, snVy, snVz);\n          let closest_1 = this.constants.THINGSCOUNT;\n          let closestDistance_2 = Infinity;\n          for (let i = 0; i < this.constants.THINGSCOUNT; i++) {\n            const distance = sphereIntersectionDistance(things[i][9], things[i][10], things[i][11], things[i][12], rRayPx, rRayPy, rRayPz, rRayVx, rRayVy, rRayVz);\n            if (distance < closestDistance_2) {\n              closest_1 = i;\n              closestDistance_2 = distance;\n            }\n          }\n          let reflectedRed = 1;\n          let reflectedGreen = 1;\n          let reflectedBlue = 1;\n          if (closestDistance_2 < Infinity) {\n            const px_1 = rRayPx + rRayVx * closestDistance_2;\n            const py_1 = rRayPy + rRayVy * closestDistance_2;\n            const pz_1 = rRayPz + rRayVz * closestDistance_2;\n            const sx_1 = things[closest_1][9];\n            const sy_1 = things[closest_1][10];\n            const sz_1 = things[closest_1][11];\n            const sRadius_1 = things[closest_1][12];\n            const snVx_1 = sphereNormalX(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);\n            const snVy_1 = sphereNormalY(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);\n            const snVz_1 = sphereNormalZ(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);\n            const rsRed = things[closest_1][2];\n            const rsGreen = things[closest_1][3];\n            const rsBlue = things[closest_1][4];\n            const rambient = things[closest_1][7];\n            const rlambert = things[closest_1][6];\n            let rlambertAmount = 0;\n            if (lambertianReflectance > 0 && rlambert > 0) {\n              for (let i = 0; i < this.constants.LIGHTSCOUNT; i++) {\n                const LPx = px_1 - lights[i][0];\n                const LPy = py_1 - lights[i][1];\n                const LPz = pz_1 - lights[i][2];\n                const uLPx = unitVectorX(LPx, LPy, LPz);\n                const uLPy = unitVectorY(LPx, LPy, LPz);\n                const uLPz = unitVectorZ(LPx, LPy, LPz);\n                let closestDistance_3 = Infinity;\n                for (let j = 0; j < this.constants.THINGSCOUNT; j++) {\n                  let distance = Infinity;\n                  const EOx = things[j][9] - px_1;\n                  const EOy = things[j][10] - py_1;\n                  const EOz = things[j][11] - pz_1;\n                  const v = (EOx * uLPx) + (EOy * uLPy) + (EOz * uLPz);\n                  const radius = things[j][12];\n                  const discriminant = (radius * radius)\n                    - ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))\n                    + (v * v);\n                  if (discriminant >= 0) {\n                    distance = v - Math.sqrt(discriminant);\n                  }\n                  if (distance < closestDistance_3) {\n                    closestDistance_3 = distance;\n                  }\n                }\n                if (closestDistance_3 > -0.005) {\n                  const PLx = -LPx;\n                  const PLy = -LPy;\n                  const PLz = -LPz;\n                  const uPLx = unitVectorX(PLx, PLy, PLz);\n                  const uPLy = unitVectorY(PLx, PLy, PLz);\n                  const uPLz = unitVectorZ(PLx, PLy, PLz);\n                  const contribution = vectorDotProduct(uPLx, uPLy, uPLz, snVx_1, snVy_1, snVz_1);\n                  if (contribution > 0) {\n                    rlambertAmount += contribution;\n                  }\n                }\n              }\n            }\n            if (lambertianReflectance > 0)\n              rlambertAmount = Math.min(1, rlambertAmount);\n            reflectedRed = (rsRed * rlambertAmount * rlambert) + (rsRed * rambient);\n            reflectedGreen = (rsGreen * rlambertAmount * rlambert) + (rsGreen * rambient);\n            reflectedBlue = (rsBlue * rlambertAmount * rlambert) + (rsBlue * rambient);\n            cVx = cVx + (specular * reflectedRed);\n            cVy = cVy + (specular * reflectedGreen);\n            cVz = cVz + (specular * reflectedBlue);\n          }\n        }\n        const red = cVx + (sRed * lambertAmount * lambert) + (sRed * ambient);\n        const green = cVy + (sGreen * lambertAmount * lambert) + (sGreen * ambient);\n        const blue = cVz + (sBlue * lambertAmount * lambert) + (sBlue * ambient);\n        this.color(red, green, blue);\n      }\n      else {\n        this.color(0.95, 0.95, 0.95);\n      }\n    }, opt(mode));\n  };\n  const fps = {\n    startTime: 0,\n    frameNumber: 0,\n    getFPS: function () {\n      this.frameNumber++;\n      const d = new Date().getTime();\n      const currentTime = (d - this.startTime) / 1000;\n      const result = Math.floor(this.frameNumber / currentTime);\n      if (currentTime > 1) {\n        this.startTime = new Date().getTime();\n        this.frameNumber = 0;\n      }\n      return result;\n    }\n  };\n  const cameraPoint = new Vector(camera[0], camera[1], camera[2]);\n  const cameraVector = new Vector(camera[3], camera[4], camera[5]);\n  const eyeVector = Vector.norm(Vector.minus(cameraVector, cameraPoint));\n  const vpRight = Vector.norm(Vector.cross(eyeVector, new Vector(0, 1, 0)));\n  const vpUp = Vector.norm(Vector.cross(vpRight, eyeVector));\n  const fovRadians = Math.PI * (camera[6] / 2) / 180;\n  const heightWidthRatio = canvasHeight / canvasWidth;\n  const halfWidth = Math.tan(fovRadians);\n  const halfHeight = heightWidthRatio * halfWidth;\n  const cameraWidth = halfWidth * 2;\n  const cameraHeight = halfHeight * 2;\n  const pixelWidth = cameraWidth / (canvasWidth - 1);\n  const pixelHeight = cameraHeight / (canvasHeight - 1);\n  const gpuKernel = createKernel(0);\n  const cpuKernel = createKernel(1);\n  const gpuCanvas = gpuKernel.canvas;\n  const cpuCanvas = cpuKernel.canvas;\n  document.getElementById(stringOfMode(initialMode)).checked = true;\n  const canvasContainer = document.getElementById('canvas-holder');\n  canvasContainer.appendChild(gpuCanvas);\n  canvasContainer.appendChild(cpuCanvas);\n  let canvas;\n  if (initialMode === 1) {\n    showCPUCanvas();\n  } else {\n    showGPUCanvas();\n  }\n  let requestId = null;\n  function togglePause() {\n    if (requestId) {\n      if (document.getElementById('pause').value === 'Pause') {\n        cancelAnimationFrame(requestId);\n        document.getElementById('pause').value = 'Play';\n      }\n      else {\n        requestId = requestAnimationFrame(renderLoop);\n        document.getElementById('pause').value = 'Pause';\n      }\n    }\n  };\n  const f = document.getElementById('fps');\n  const lambert = document.getElementById('lambert');\n  const specular = document.getElementById('specular');\n  const cpuProcessor = document.getElementById('cpu');\n  function renderLoop() {\n    f.innerHTML = fps.getFPS().toString();\n    const lambertianReflectance = lambert.checked ? 1 : 0;\n    const specularReflection = specular.checked ? 1 : 0;\n    if (cpuProcessor.checked) {\n      cpuKernel(camera, lights, things, eyeVector.toArray(), vpRight.toArray(), vpUp.toArray(), halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection);\n      if (canvas !== cpuKernel.canvas) {\n        showCPUCanvas();\n      }\n    } else {\n\n      gpuKernel(camera, lights, things, eyeVector.toArray(), vpRight.toArray(), vpUp.toArray(), halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection);\n      if (canvas !== gpuKernel.canvas) {\n        showGPUCanvas();\n      }\n    }\n    for (let i = 0; i < things.length; i++) {\n      const thing = things[i];\n      const height = canvasHeight / (halfHeight * 2 * 100);\n      if (thing[10] < height) {\n        thing[10] = (thing[10] + 0.02) % (height + 1);\n      } else {\n        thing[10] = -1 * height;\n      }\n    }\n    requestId = requestAnimationFrame(renderLoop);\n  }\n  window.onload = renderLoop;\n\n  function showCPUCanvas() {\n    cpuCanvas.style.display = '';\n    gpuCanvas.style.display = 'none';\n    canvas = cpuCanvas;\n  }\n\n  function showGPUCanvas() {\n    cpuCanvas.style.display = 'none';\n    gpuCanvas.style.display = '';\n    canvas = gpuCanvas;\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/simple-javascript.js",
    "content": "const { GPU } = require('../src');\n\nconst gpu = new GPU({ mode: 'gpu' });\n\n// Look ma! I can javascript on my GPU!\nfunction kernelFunction(anInt, anArray, aNestedArray) {\n  const x = .25 + anInt + anArray[this.thread.x] + aNestedArray[this.thread.x][this.thread.y];\n  return x;\n}\n\nconst kernel = gpu.createKernel(kernelFunction, {\n  output: [1]\n});\n\nconst result = kernel(1, [.25], [[1.5]]);\n\nconsole.log(result[0]); // 3\n"
  },
  {
    "path": "examples/simple-typescript.ts",
    "content": "import { GPU, KernelFunction, IKernelRunShortcut } from '../src';\n\nconst gpu = new GPU({ mode: 'gpu' });\n\n// Look ma! I can typescript on my GPU!\nconst kernelFunction: KernelFunction = function(anInt: number, anArray: number[], aNestedArray: number[][]) {\n  const x = .25 + anInt + anArray[this.thread.x] + aNestedArray[this.thread.x][this.thread.y];\n  return x;\n};\n\nconst kernel: IKernelRunShortcut = gpu.createKernel(kernelFunction)\n  .setOutput([1]);\n\nconst result = kernel(1, [.25], [[1.5]]);\n\nconsole.log(result[0]); // 3\n"
  },
  {
    "path": "examples/slow-fade.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Slow Fade</title>\n</head>\n<body>\n  <h1>Slow Fade</h1>\n  <div>\n    <canvas id=\"c\"></canvas>\n  </div>\n</body>\n<script src=\"../dist/gpu-browser.min.js\"></script>\n<script>\n  const canvas = document.getElementById('c');\n  const gpu = new GPU({\n    canvas: canvas,\n    mode: 'gpu'\n  });\n  const dim = 512;\n  const kernel = gpu.createKernel(\n    function(x) {\n      this.color(\n        (x * (this.thread.y + this.thread.x)) / 1024,\n        (x * (this.thread.y * this.thread.x)) / (1024 * 1024),\n        (x * (this.thread.y * 2 * this.thread.x)) / (1024 * 2),\n        1\n      );\n    },\n    {\n      useLegacyEncoder: true,\n      output: [dim, dim],\n      graphical: true\n    }\n  );\n\n  let param = 0;\n  const doDraw = () => {\n    kernel(param);\n    param += 0.001;\n    window.requestAnimationFrame(doDraw);\n  };\n\n  window.requestAnimationFrame(doDraw);\n</script>\n</html>\n"
  },
  {
    "path": "examples/video/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Video input with GPU.js</title>\n  <script src=\"../../dist/gpu-browser.min.js\"></script>\n</head>\n<body>\n<h1>Video input (and output) with GPU.js</h1>\n<!-- ty https://commons.wikimedia.org/wiki/File:Jellyfish_in_Vr%C3%A5ngo.webm -->\n<div style=\"text-align: center\">\n  <label><input type=\"checkbox\" id=\"flip-xy\" /> Flip XY</label><br />\n  <label><input type=\"checkbox\" id=\"alter-colors\" /> Alter colors</label><br />\n  <label><input type=\"checkbox\" id=\"gpu-enabled\" checked /> GPU enabled</label><br />\n  <label><input type=\"checkbox\" id=\"pause-gpu\" /> Pause GPU</label><br />\n</div>\n<style>\n  .grid-container {\n    display: grid;\n    grid-template-columns: auto auto;\n  }\n  .grid-item {\n    padding: 20px;\n    text-align: center;\n  }\n</style>\n<div class=\"grid-container\">\n<div class=\"grid-item\">\n<h3>Video</h3>\n<video src=\"../../test/jellyfish.webm\" controls width=\"337\" height=\"599\" loop></video>\n</div>\n<div id=\"canvas-parent\" class=\"grid-item\">\n  <h3>GPU.js Graphical Output <span id=\"fps-number\">0</span><span> fps</span></h3>\n</div>\n</div>\n<script src=\"dist/gpu-browser.js\"></script>\n<script>\n  const canvasParent = document.getElementById('canvas-parent');\n  const flipXY = document.getElementById('flip-xy');\n  const alterColors = document.getElementById('alter-colors');\n  const gpuEnabled = document.getElementById('gpu-enabled');\n  const pauseGPU = document.getElementById('pause-gpu');\n  const fpsNumber = document.getElementById('fps-number');\n  let lastCalledTime = Date.now();\n  let fps;\n  let delta;\n  let dispose = setup();\n  gpuEnabled.onchange = () => {\n    if (dispose) dispose();\n    dispose = setup();\n  };\n  function setup() {\n    let disposed = false;\n    const gpu = new GPU({ mode: gpuEnabled.checked ? 'gpu' : 'cpu' });\n\n    // THIS IS THE IMPORTANT STUFF\n    const kernel = gpu.createKernel(function (frame, flipXY, alterColors) {\n      // NOTE: better to do alterColors and flipXY out of the kernel, but showing here for brevity\n      const pixel = flipXY\n        ? frame[this.output.y - 1 - this.thread.y][this.output.x - 1 - this.thread.x]\n        : frame[this.thread.y][this.thread.x];\n\n      if (alterColors) {\n        this.color(pixel.b, pixel.g, pixel.r, pixel.a);\n      } else {\n        this.color(pixel.r, pixel.g, pixel.b, pixel.a);\n      }\n    }, {\n      output: [337, 599],\n      graphical: true,\n      tactic: 'precision'\n    });\n    canvasParent.appendChild(kernel.canvas);\n    const videoElement = document.querySelector('video');\n    function render() {\n      if (disposed) {\n        return;\n      }\n      if (pauseGPU.checked) return setTimeout(render, 100); // save a little resources when paused\n      kernel(videoElement, flipXY.checked, alterColors.checked);\n      window.requestAnimationFrame(render);\n      calcFPS();\n    }\n\n    render();\n    return () => {\n      canvasParent.removeChild(kernel.canvas);\n      gpu.destroy();\n      disposed = true;\n    };\n  }\n\n  function calcFPS() {\n    delta = (Date.now() - lastCalledTime) / 1000;\n    lastCalledTime = Date.now();\n    fps = 1 / delta;\n    fpsNumber.innerHTML = fps.toFixed(0);\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "gulpfile.js",
    "content": "const fs = require('fs');\nconst gulp = require('gulp');\nconst rename = require('gulp-rename');\nconst header = require('gulp-header');\nconst browserSync = require('browser-sync');\nconst browserify = require('browserify');\nconst replace = require('gulp-replace');\nconst source = require('vinyl-source-stream');\nconst buffer = require('vinyl-buffer');\nconst path = require('path');\nconst uglify = require('gulp-uglify-es').default;\nconst pkg = require('./package.json');\nconst jsprettify = require('gulp-jsbeautifier');\nconst stripComments = require('gulp-strip-comments');\nconst merge = require('ordered-read-streams');\nconst { readDirDeepSync } = require('read-dir-deep');\n\ngulp.task('build', () => {\n  const gpu = browserify('./src/browser.js', { standalone: 'GPU', browserField: false })\n    .ignore('gl')\n    .bundle()\n    .pipe(source('gpu-browser.js'))\n    .pipe(buffer())\n    .pipe(stripComments())\n    .pipe(header(fs.readFileSync('./src/browser-header.txt', 'utf8'), { pkg : pkg }))\n    .pipe(gulp.dest('./dist'))\n    .on('error', console.error);\n\n  const gpuCore = browserify('./src/browser.js', { standalone: 'GPU', browserField: false })\n    .ignore('gl')\n    .ignore('acorn')\n    .bundle()\n    .pipe(source('gpu-browser-core.js'))\n    .pipe(buffer())\n    .pipe(stripComments())\n    .pipe(header(fs.readFileSync('./src/browser-header.txt', 'utf8'), { pkg : pkg }))\n    .pipe(gulp.dest('./dist'))\n    .on('error', console.error);\n\n  return merge(gpu, gpuCore);\n});\n\n/// Minify the build script, after building it\ngulp.task('minify', () => {\n  const gpu = gulp.src('dist/gpu-browser.js')\n    .pipe(uglify())\n    .pipe(rename('gpu-browser.min.js'))\n    .pipe(header(fs.readFileSync('./src/browser-header.txt', 'utf8'), { pkg : pkg }))\n    .pipe(gulp.dest('./dist'))\n    .on('error', console.error);\n\n  const gpuCore = gulp.src('dist/gpu-browser-core.js')\n    .pipe(uglify())\n    .pipe(rename('gpu-browser-core.min.js'))\n    .pipe(header(fs.readFileSync('./src/browser-header.txt', 'utf8'), { pkg : pkg }))\n    .pipe(gulp.dest('./dist'))\n    .on('error', console.error);\n\n  return merge(gpu, gpuCore);\n});\n\n/// The browser sync prototyping\ngulp.task('bsync', () => {\n  // Syncs browser\n  browserSync.init({\n    server: {\n      baseDir: './'\n    },\n    open: true,\n    startPath: \"./test/html/test-all.html\",\n    // Makes it easier to test on external mobile devices\n    host: \"0.0.0.0\",\n    tunnel: true\n  });\n\n  // Detect change -> rebuild TS\n  gulp.watch(['src/**.js'], gulp.series('minify'));\n});\n\n/// Auto rebuild and host\ngulp.task('default', gulp.series('minify','bsync'));\n\n/// Beautify source code\n/// Use before merge request\ngulp.task('beautify', () => {\n  return gulp.src(['src/**/*.js'])\n    .pipe(jsprettify({\n      indent_size: 2,\n      indent_char: ' ',\n      indent_with_tabs: false,\n      eol: '\\n',\n      brace_style: 'preserve-inline'\n    }))\n    .pipe(gulp.dest('src'));\n});\n\ngulp.task('build-tests', () => {\n  const folder = 'test';\n  const testFile = 'all.html';\n  try {\n    fs.unlinkSync(`${folder}/${testFile}`);\n  } catch (e) {}\n  const rootPath = path.resolve(process.cwd(), folder);\n  const warning = '<!-- the following list of javascript files is built automatically -->\\n';\n  const files = readDirDeepSync(rootPath, {\n    patterns: [\n      '**/*.js'\n    ],\n    ignore: [\n      '*.js'\n    ]\n  })\n    .map(file => file.replace(/^test\\//, ''));\n  return gulp.src(`${folder}/all-template.html`)\n    .pipe(replace('{{test-files}}', warning + files.map(file => `<script type=\"module\" src=\"${file}\"></script>`).join('\\n')))\n    .pipe(rename(testFile))\n    .pipe(gulp.dest(folder));\n});\n\ngulp.task('make', gulp.series('build', 'beautify', 'minify', 'build-tests'));\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"gpu.js\",\n  \"version\": \"2.16.0\",\n  \"description\": \"GPU Accelerated JavaScript\",\n  \"engines\": {\n    \"node\": \">=8.0.0\"\n  },\n  \"main\": \"./src/index.js\",\n  \"files\": [\n    \"src\",\n    \"dist\"\n  ],\n  \"unpkg\": \"./dist/gpu-browser.js\",\n  \"jsdelivr\": \"./dist/gpu-browser.js\",\n  \"browser\": \"./dist/gpu-browser.js\",\n  \"directories\": {\n    \"doc\": \"doc\",\n    \"test\": \"test\"\n  },\n  \"dependencies\": {\n    \"acorn\": \"8.14.0\",\n    \"gl\": \"8.1.6\",\n    \"gl-wiretap\": \"0.6.2\",\n    \"gpu-mock.js\": \"github:weagle08/gpu-mock.js\",\n    \"ordered-read-streams\": \"^2.0.0\",\n    \"webgpu\": \"0.2.9\"\n  },\n  \"devDependencies\": {\n    \"benchmark\": \"2.1.4\",\n    \"browser-sync\": \"3.0.2\",\n    \"browserify\": \"17.0.0\",\n    \"c8\": \"10.1.2\",\n    \"gulp\": \"5.0.0\",\n    \"gulp-concat\": \"2.6.1\",\n    \"gulp-header\": \"2.0.9\",\n    \"gulp-jsbeautifier\": \"3.0.1\",\n    \"gulp-rename\": \"2.0.0\",\n    \"gulp-replace\": \"1.1.4\",\n    \"gulp-strip-comments\": \"2.6.0\",\n    \"gulp-uglify-es\": \"3.0.0\",\n    \"merge-stream\": \"2.0.0\",\n    \"qunit\": \"2.22.0\",\n    \"read-dir-deep\": \"8.0.0\",\n    \"sinon\": \"18.0.0\",\n    \"vinyl-buffer\": \"1.0.1\",\n    \"vinyl-source-stream\": \"2.0.0\"\n  },\n  \"scripts\": {\n    \"test\": \"qunit test/issues test/internal test/features\",\n    \"coverage\": \"c8 qunit test/issues test/internal test/features\",\n    \"setup\": \"npm i -g gulp-cli\",\n    \"make\": \"gulp make\",\n    \"build\": \"gulp build\",\n    \"docs\": \"doxdox ./src --layout bootstrap --output docs.html\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/gpujs/gpu.js.git\"\n  },\n  \"keywords\": [\n    \"gpgpu\",\n    \"webgl\"\n  ],\n  \"author\": \"The gpu.js Team\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/gpujs/gpu.js/issues\"\n  },\n  \"homepage\": \"http://gpu.rocks/\",\n  \"typings\": \"./src/index.d.ts\"\n}\n"
  },
  {
    "path": "src/alias.js",
    "content": "const { utils } = require('./utils');\n\n/**\n *\n * @param name\n * @param source\n * @returns {Function}\n */\nfunction alias(name, source) {\n  const fnString = source.toString();\n  return new Function(`return function ${ name } (${ utils.getArgumentNamesFromString(fnString).join(', ') }) {\n  ${ utils.getFunctionBodyFromString(fnString) }\n}`)();\n}\n\nmodule.exports = {\n  alias\n};"
  },
  {
    "path": "src/backend/cpu/function-node.js",
    "content": "const { FunctionNode } = require('../function-node');\n\n/**\n * @desc [INTERNAL] Represents a single function, inside JS\n *\n * <p>This handles all the raw state, converted state, etc. Of a single function.</p>\n */\nclass CPUFunctionNode extends FunctionNode {\n  /**\n   * @desc Parses the abstract syntax tree for to its *named function*\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astFunction(ast, retArr) {\n\n    // Setup function return type and name\n    if (!this.isRootKernel) {\n      retArr.push('function');\n      retArr.push(' ');\n      retArr.push(this.name);\n      retArr.push('(');\n\n      // Arguments handling\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        retArr.push('user_');\n        retArr.push(argumentName);\n      }\n\n      // Function opening\n      retArr.push(') {\\n');\n    }\n\n    // Body statement iteration\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    if (!this.isRootKernel) {\n      // Function closing\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for to *return* statement\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astReturnStatement(ast, retArr) {\n    const type = this.returnType || this.getType(ast.argument);\n\n    if (!this.returnType) {\n      this.returnType = type;\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(this.leadingReturnStatement);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';\\n');\n      retArr.push(this.followingReturnStatement);\n      retArr.push('continue;\\n');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = `);\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push('return ');\n      this.astGeneric(ast.argument, retArr);\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *literal value*\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astLiteral(ast, retArr) {\n\n    // Reject non numeric literals\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    retArr.push(ast.value);\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *binary* expression\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astBinaryExpression(ast, retArr) {\n    retArr.push('(');\n    this.astGeneric(ast.left, retArr);\n    retArr.push(ast.operator);\n    this.astGeneric(ast.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *identifier* expression\n   * @param {Object} idtNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    switch (idtNode.name) {\n      case 'Infinity':\n        retArr.push('Infinity');\n        break;\n      default:\n        if (this.constants && this.constants.hasOwnProperty(idtNode.name)) {\n          retArr.push('constants_' + idtNode.name);\n        } else {\n          retArr.push('user_' + idtNode.name);\n        }\n    }\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *for-loop* expression\n   * @param {Object} forNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed webgl string\n   */\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      this.pushState('in-for-loop-init');\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < initArr.length; i++) {\n        if (initArr[i].includes && initArr[i].includes(',')) {\n          isSafe = false;\n        }\n      }\n      this.popState('in-for-loop-init');\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    // have all parts, now make them safe\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), ';\\n');\n      }\n      retArr.push(`for (let ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *while* loop\n   * @param {Object} whileNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed javascript string\n   */\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        whileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    retArr.push('if (');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') {\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('} else {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *do while* loop\n   * @param {Object} doWhileNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed webgl string\n   */\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput(\n        'Invalid while statement',\n        doWhileNode\n      );\n    }\n\n    retArr.push('for (let i = 0; i < LOOP_MAX; i++) {');\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') {\\n');\n    retArr.push('break;\\n');\n    retArr.push('}\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Assignment* Expression\n   * @param {Object} assNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astAssignmentExpression(assNode, retArr) {\n    const declaration = this.getDeclaration(assNode.left);\n    if (declaration && !declaration.assignable) {\n      throw this.astErrorOutput(`Variable ${assNode.left.name} is not assignable here`, assNode);\n    }\n    this.astGeneric(assNode.left, retArr);\n    retArr.push(assNode.operator);\n    this.astGeneric(assNode.right, retArr);\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Block* statement\n   * @param {Object} bNode - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); // this prevents recursive removal of braces\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Variable Declaration*\n   * @param {Object} varDecNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astVariableDeclaration(varDecNode, retArr) {\n    retArr.push(`${varDecNode.kind} `);\n    const { declarations } = varDecNode;\n    for (let i = 0; i < declarations.length; i++) {\n      if (i > 0) {\n        retArr.push(',');\n      }\n      const declaration = declarations[i];\n      const info = this.getDeclaration(declaration.id);\n      if (!info.valueType) {\n        info.valueType = this.getType(declaration.init);\n      }\n      this.astGeneric(declaration, retArr);\n    }\n    if (!this.isState('in-for-loop-init')) {\n      retArr.push(';');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *If* Statement\n   * @param {Object} ifNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n\n  }\n\n  astSwitchStatement(ast, retArr) {\n    const { discriminant, cases } = ast;\n    retArr.push('switch (');\n    this.astGeneric(discriminant, retArr);\n    retArr.push(') {\\n');\n    for (let i = 0; i < cases.length; i++) {\n      if (cases[i].test === null) {\n        retArr.push('default:\\n');\n        this.astGeneric(cases[i].consequent, retArr);\n        if (cases[i].consequent && cases[i].consequent.length > 0) {\n          retArr.push('break;\\n');\n        }\n        continue;\n      }\n      retArr.push('case ');\n      this.astGeneric(cases[i].test, retArr);\n      retArr.push(':\\n');\n      if (cases[i].consequent && cases[i].consequent.length > 0) {\n        this.astGeneric(cases[i].consequent, retArr);\n        retArr.push('break;\\n');\n      }\n    }\n    retArr.push('\\n}');\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *This* expression\n   * @param {Object} tNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astThisExpression(tNode, retArr) {\n    retArr.push('_this');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Member* Expression\n   * @param {Object} mNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astMemberExpression(mNode, retArr) {\n    const {\n      signature,\n      type,\n      property,\n      xProperty,\n      yProperty,\n      zProperty,\n      name,\n      origin\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'this.thread.value':\n        retArr.push(`_this.thread.${ name }`);\n        return retArr;\n      case 'this.output.value':\n        switch (name) {\n          case 'x':\n            retArr.push('outputX');\n            break;\n          case 'y':\n            retArr.push('outputY');\n            break;\n          case 'z':\n            retArr.push('outputZ');\n            break;\n          default:\n            throw this.astErrorOutput('Unexpected expression', mNode);\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ name }[0]`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ name }[1]`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ name }[2]`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ name }[3]`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n      case 'this.constants.value[]':\n      case 'this.constants.value[][]':\n      case 'this.constants.value[][][]':\n        break;\n      case 'fn()[]':\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      case 'fn()[][]':\n        this.astGeneric(mNode.object.object, retArr);\n        retArr.push('[');\n        this.astGeneric(mNode.object.property, retArr);\n        retArr.push(']');\n        retArr.push('[');\n        this.astGeneric(mNode.property, retArr);\n        retArr.push(']');\n        return retArr;\n      default:\n        throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (!mNode.computed) {\n      // handle simple types\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${name}`);\n          return retArr;\n      }\n    }\n\n    // handle more complex types\n    // argument may have come from a parent\n    const markupName = `${origin}_${name}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'HTMLImageArray':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n      case 'HTMLImage':\n      default:\n        let size;\n        let isInput;\n        if (origin === 'constants') {\n          const constant = this.constants[name];\n          isInput = this.constantTypes[name] === 'Input';\n          size = isInput ? constant.size : null;\n        } else {\n          isInput = this.isInput(name);\n          size = isInput ? this.argumentSizes[this.argumentNames.indexOf(name)] : null;\n        }\n        retArr.push(`${ markupName }`);\n        if (zProperty && yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? '(outputY * outputX)' : size[1] * size[0] })+(`);\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(zProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (yProperty) {\n          if (isInput) {\n            retArr.push('[(');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(`*${ this.dynamicArguments ? 'outputX' : size[0] })+`);\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          } else {\n            retArr.push('[');\n            this.astGeneric(yProperty, retArr);\n            retArr.push(']');\n            retArr.push('[');\n            this.astGeneric(xProperty, retArr);\n            retArr.push(']');\n          }\n        } else if (typeof xProperty !== 'undefined') {\n          retArr.push('[');\n          this.astGeneric(xProperty, retArr);\n          retArr.push(']');\n        }\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *call* expression\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns  {Array} the append retArr\n   */\n  astCallExpression(ast, retArr) {\n    if (ast.type !== 'CallExpression') {\n      // Failure, unknown expression\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n    // Get the full function call, unrolled\n    let functionName = this.astMemberExpressionUnroll(ast.callee);\n\n    // Register the function into the called registry\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    // track the function was called\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    // Call the function\n    retArr.push(functionName);\n\n    // Open arguments space\n    retArr.push('(');\n    const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n    // Add the arguments\n    for (let i = 0; i < ast.arguments.length; ++i) {\n      const argument = ast.arguments[i];\n\n      // in order to track return type, even though this is CPU\n      let argumentType = this.getType(argument);\n      if (!targetTypes[i]) {\n        this.triggerImplyArgumentType(functionName, i, argumentType, this);\n      }\n\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      this.astGeneric(argument, retArr);\n    }\n    // Close arguments space\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Array* Expression\n   * @param {Object} arrNode - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n    const arrLen = arrNode.elements.length;\n    const elements = [];\n    for (let i = 0; i < arrLen; ++i) {\n      const element = [];\n      this.astGeneric(arrNode.elements[i], element);\n      elements.push(element.join(''));\n    }\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`[${elements.join(', ')}]`);\n        break;\n      default:\n        retArr.push(`new Float32Array([${elements.join(', ')}])`);\n    }\n    return retArr;\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    retArr.push('debugger;');\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  CPUFunctionNode\n};"
  },
  {
    "path": "src/backend/cpu/kernel-string.js",
    "content": "const { utils } = require('../../utils');\n\nfunction constantsToString(constants, types) {\n  const results = [];\n  for (const name in types) {\n    if (!types.hasOwnProperty(name)) continue;\n    const type = types[name];\n    const constant = constants[name];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n        results.push(`${name}:${constant}`);\n        break;\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        results.push(`${name}:new ${constant.constructor.name}(${JSON.stringify(Array.from(constant))})`);\n        break;\n    }\n  }\n  return `{ ${ results.join() } }`;\n}\n\nfunction cpuKernelString(cpuKernel, name) {\n  const header = [];\n  const thisProperties = [];\n  const beforeReturn = [];\n\n  const useFunctionKeyword = !/^function/.test(cpuKernel.color.toString());\n\n  header.push(\n    '  const { context, canvas, constants: incomingConstants } = settings;',\n    `  const output = new Int32Array(${JSON.stringify(Array.from(cpuKernel.output))});`,\n    `  const _constantTypes = ${JSON.stringify(cpuKernel.constantTypes)};`,\n    `  const _constants = ${constantsToString(cpuKernel.constants, cpuKernel.constantTypes)};`\n  );\n\n  thisProperties.push(\n    '    constants: _constants,',\n    '    context,',\n    '    output,',\n    '    thread: {x: 0, y: 0, z: 0},'\n  );\n\n  if (cpuKernel.graphical) {\n    header.push(`  const _imageData = context.createImageData(${cpuKernel.output[0]}, ${cpuKernel.output[1]});`);\n    header.push(`  const _colorData = new Uint8ClampedArray(${cpuKernel.output[0]} * ${cpuKernel.output[1]} * 4);`);\n\n    const colorFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.color.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: (object, name) => {\n        return null;\n      }\n    });\n\n    const getPixelsFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.getPixels.toString(), {\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case '_colorData':\n            return '_colorData';\n          case '_imageData':\n            return '_imageData';\n          case 'output':\n            return 'output';\n          case 'thread':\n            return 'this.thread';\n        }\n        return JSON.stringify(cpuKernel[propertyName]);\n      },\n      findDependency: () => {\n        return null;\n      }\n    });\n\n    thisProperties.push(\n      '    _imageData,',\n      '    _colorData,',\n      `    color: ${colorFn},`\n    );\n\n    beforeReturn.push(\n      `  kernel.getPixels = ${getPixelsFn};`\n    );\n  }\n\n  const constantTypes = [];\n  const constantKeys = Object.keys(cpuKernel.constantTypes);\n  for (let i = 0; i < constantKeys.length; i++) {\n    constantTypes.push(cpuKernel.constantTypes[constantKeys]);\n  }\n  if (cpuKernel.argumentTypes.indexOf('HTMLImageArray') !== -1 || constantTypes.indexOf('HTMLImageArray') !== -1) {\n    const flattenedImageTo3DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._imageTo3DArray.toString(), {\n      doNotDefine: ['canvas'],\n      findDependency: (object, name) => {\n        if (object === 'this') {\n          return (useFunctionKeyword ? 'function ' : '') + cpuKernel[name].toString();\n        }\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return;\n          case 'context':\n            return 'context';\n        }\n      }\n    });\n    beforeReturn.push(flattenedImageTo3DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n    thisProperties.push(`    _imageTo3DArray,`);\n  } else if (cpuKernel.argumentTypes.indexOf('HTMLImage') !== -1 || constantTypes.indexOf('HTMLImage') !== -1) {\n    const flattenedImageTo2DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._mediaTo2DArray.toString(), {\n      findDependency: (object, name) => {\n        return null;\n      },\n      thisLookup: (propertyName) => {\n        switch (propertyName) {\n          case 'canvas':\n            return 'settings.canvas';\n          case 'context':\n            return 'settings.context';\n        }\n        throw new Error('unhandled thisLookup');\n      }\n    });\n    beforeReturn.push(flattenedImageTo2DArray);\n    thisProperties.push(`    _mediaTo2DArray,`);\n  }\n\n  return `function(settings) {\n${ header.join('\\n') }\n  for (const p in _constantTypes) {\n    if (!_constantTypes.hasOwnProperty(p)) continue;\n    const type = _constantTypes[p];\n    switch (type) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n      case 'Boolean':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        if (incomingConstants.hasOwnProperty(p)) {\n          console.warn('constant ' + p + ' of type ' + type + ' cannot be resigned');\n        }\n        continue;\n    }\n    if (!incomingConstants.hasOwnProperty(p)) {\n      throw new Error('constant ' + p + ' not found');\n    }\n    _constants[p] = incomingConstants[p];\n  }\n  const kernel = (function() {\n${cpuKernel._kernelString}\n  })\n    .apply({ ${thisProperties.join('\\n')} });\n  ${ beforeReturn.join('\\n') }\n  return kernel;\n}`;\n}\n\nmodule.exports = {\n  cpuKernelString\n};"
  },
  {
    "path": "src/backend/cpu/kernel.js",
    "content": "const { Kernel } = require('../kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { CPUFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst { cpuKernelString } = require('./kernel-string');\n\n/**\n * @desc Kernel Implementation for CPU.\n * <p>Instantiates properties to the CPU Kernel.</p>\n */\nclass CPUKernel extends Kernel {\n  static getFeatures() {\n    return this.features;\n  }\n  static get features() {\n    return Object.freeze({\n      kernelMap: true,\n      isIntegerDivisionAccurate: true\n    });\n  }\n  static get isSupported() {\n    return true;\n  }\n  static isContextMatch(context) {\n    return false;\n  }\n  /**\n   * @desc The current mode in which gpu.js is executing.\n   */\n  static get mode() {\n    return 'cpu';\n  }\n\n  static nativeFunctionArguments() {\n    return null;\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`Looking up native function return type not supported on ${this.name}`);\n  }\n\n  static combineKernels(combinedKernel) {\n    return combinedKernel;\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return 'cpu' + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.mergeSettings(source.settings || settings);\n\n    this._imageData = null;\n    this._colorData = null;\n    this._kernelString = null;\n    this._prependedString = [];\n    this.thread = {\n      x: 0,\n      y: 0,\n      z: 0\n    };\n    this.translatedSources = null;\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      return document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  initContext() {\n    if (!this.canvas) return null;\n    return this.canvas.getContext('2d');\n  }\n\n  initPlugins(settings) {\n    return [];\n  }\n\n  /**\n   * @desc Validate settings related to Kernel, such as dimensions size, and auto output support.\n   * @param {IArguments} args\n   */\n  validateSettings(args) {\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      if (argType === 'Array') {\n        this.output = utils.getDimensions(argType);\n      } else if (argType === 'NumberTexture' || argType === 'ArrayTexture(4)') {\n        this.output = args[0].output;\n      } else {\n        throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n    }\n\n    this.checkOutput();\n  }\n\n  translateSource() {\n    this.leadingReturnStatement = this.output.length > 1 ? 'resultX[x] = ' : 'result[x] = ';\n    if (this.subKernels) {\n      const followingReturnStatement = [];\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const {\n          name\n        } = this.subKernels[i];\n        followingReturnStatement.push(this.output.length > 1 ? `resultX_${ name }[x] = subKernelResult_${ name };\\n` : `result_${ name }[x] = subKernelResult_${ name };\\n`);\n      }\n      this.followingReturnStatement = followingReturnStatement.join('');\n    }\n    const functionBuilder = FunctionBuilder.fromKernel(this, CPUFunctionNode);\n    this.translatedSources = functionBuilder.getPrototypes('kernel');\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n  }\n\n  /**\n   * @desc Builds the Kernel, by generating the kernel\n   * string using thread dimensions, and arguments\n   * supplied to the kernel.\n   *\n   * <p>If the graphical flag is enabled, canvas is used.</p>\n   */\n  build() {\n    if (this.built) return;\n    this.setupConstants();\n    this.setupArguments(arguments);\n    this.validateSettings(arguments);\n    this.translateSource();\n\n    if (this.graphical) {\n      const {\n        canvas,\n        output\n      } = this;\n      if (!canvas) {\n        throw new Error('no canvas available for using graphical output');\n      }\n      const width = output[0];\n      const height = output[1] || 1;\n      canvas.width = width;\n      canvas.height = height;\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n\n    const kernelString = this.getKernelString();\n    this.kernelString = kernelString;\n\n    if (this.debug) {\n      console.log('Function output:');\n      console.log(kernelString);\n    }\n\n    try {\n      this.run = new Function([], kernelString).bind(this)();\n    } catch (e) {\n      console.error('An error occurred compiling the javascript: ', e);\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  color(r, g, b, a) {\n    if (typeof a === 'undefined') {\n      a = 1;\n    }\n\n    r = Math.floor(r * 255);\n    g = Math.floor(g * 255);\n    b = Math.floor(b * 255);\n    a = Math.floor(a * 255);\n\n    const width = this.output[0];\n    const height = this.output[1];\n\n    const x = this.thread.x;\n    const y = height - this.thread.y - 1;\n\n    const index = x + y * width;\n\n    this._colorData[index * 4 + 0] = r;\n    this._colorData[index * 4 + 1] = g;\n    this._colorData[index * 4 + 2] = b;\n    this._colorData[index * 4 + 3] = a;\n  }\n\n  /**\n   * @desc Generates kernel string for this kernel program.\n   *\n   * <p>If sub-kernels are supplied, they are also factored in.\n   * This string can be saved by calling the `toString` method\n   * and then can be reused later.</p>\n   *\n   * @returns {String} result\n   *\n   */\n  getKernelString() {\n    if (this._kernelString !== null) return this._kernelString;\n\n    let kernelThreadString = null;\n    let {\n      translatedSources\n    } = this;\n    if (translatedSources.length > 1) {\n      translatedSources = translatedSources.filter(fn => {\n        if (/^function/.test(fn)) return fn;\n        kernelThreadString = fn;\n        return false;\n      });\n    } else {\n      kernelThreadString = translatedSources.shift();\n    }\n    return this._kernelString = `  const LOOP_MAX = ${ this._getLoopMaxString() };\n  ${ this.injectedNative || '' }\n  const _this = this;\n  ${ this._resultKernelHeader() }\n  ${ this._processConstants() }\n  return (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) => {\n    ${ this._prependedString.join('') }\n    ${ this._earlyThrows() }\n    ${ this._processArguments() }\n    ${ this.graphical ? this._graphicalKernelBody(kernelThreadString) : this._resultKernelBody(kernelThreadString) }\n    ${ translatedSources.length > 0 ? translatedSources.join('\\n') : '' }\n  };`;\n  }\n\n  /**\n   * @desc Returns the *pre-compiled* Kernel as a JS Object String, that can be reused.\n   */\n  toString() {\n    return cpuKernelString(this);\n  }\n\n  /**\n   * @desc Get the maximum loop size String.\n   * @returns {String} result\n   */\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${ parseInt(this.loopMaxIterations) };` :\n      ' 1000;'\n    );\n  }\n\n  _processConstants() {\n    if (!this.constants) return '';\n\n    const result = [];\n    for (let p in this.constants) {\n      const type = this.constantTypes[p];\n      switch (type) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    const constants_${p} = this._mediaTo2DArray(this.constants.${p});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    const constants_${p} = this._imageTo3DArray(this.constants.${p});\\n`);\n          break;\n        case 'Input':\n          result.push(`    const constants_${p} = this.constants.${p}.value;\\n`);\n          break;\n        default:\n          result.push(`    const constants_${p} = this.constants.${p};\\n`);\n      }\n    }\n    return result.join('');\n  }\n\n  _earlyThrows() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    const arrayArguments = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      if (this.argumentTypes[i] === 'Array') {\n        arrayArguments.push(this.argumentNames[i]);\n      }\n    }\n    if (arrayArguments.length === 0) return '';\n    const checks = [];\n    for (let i = 0; i < arrayArguments.length; i++) {\n      const argumentName = arrayArguments[i];\n      const checkSubKernels = this._mapSubKernels(subKernel => `user_${argumentName} === result_${subKernel.name}`).join(' || ');\n      checks.push(`user_${argumentName} === result${checkSubKernels ? ` || ${checkSubKernels}` : ''}`);\n    }\n    return `if (${checks.join(' || ')}) throw new Error('Source and destination arrays are the same.  Use immutable = true');`;\n  }\n\n  _processArguments() {\n    const result = [];\n    for (let i = 0; i < this.argumentTypes.length; i++) {\n      const variableName = `user_${this.argumentNames[i]}`;\n      switch (this.argumentTypes[i]) {\n        case 'HTMLCanvas':\n        case 'OffscreenCanvas':\n        case 'HTMLImage':\n        case 'ImageBitmap':\n        case 'ImageData':\n        case 'HTMLVideo':\n          result.push(`    ${variableName} = this._mediaTo2DArray(${variableName});\\n`);\n          break;\n        case 'HTMLImageArray':\n          result.push(`    ${variableName} = this._imageTo3DArray(${variableName});\\n`);\n          break;\n        case 'Input':\n          result.push(`    ${variableName} = ${variableName}.value;\\n`);\n          break;\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n          result.push(`\n    if (${variableName}.toArray) {\n      if (!_this.textureCache) {\n        _this.textureCache = [];\n        _this.arrayCache = [];\n      }\n      const textureIndex = _this.textureCache.indexOf(${variableName});\n      if (textureIndex !== -1) {\n        ${variableName} = _this.arrayCache[textureIndex];\n      } else {\n        _this.textureCache.push(${variableName});\n        ${variableName} = ${variableName}.toArray();\n        _this.arrayCache.push(${variableName});\n      }\n    }`);\n          break;\n      }\n    }\n    return result.join('');\n  }\n\n  _mediaTo2DArray(media) {\n    const canvas = this.canvas;\n    const width = media.width > 0 ? media.width : media.videoWidth;\n    const height = media.height > 0 ? media.height : media.videoHeight;\n    if (canvas.width < width) {\n      canvas.width = width;\n    }\n    if (canvas.height < height) {\n      canvas.height = height;\n    }\n    const ctx = this.context;\n    let pixelsData;\n    if (media.constructor === ImageData) {\n      pixelsData = media.data;\n    } else {\n      ctx.drawImage(media, 0, 0, width, height);\n      pixelsData = ctx.getImageData(0, 0, width, height).data;\n    }\n    const imageArray = new Array(height);\n    let index = 0;\n    for (let y = height - 1; y >= 0; y--) {\n      const row = imageArray[y] = new Array(width);\n      for (let x = 0; x < width; x++) {\n        const pixel = new Float32Array(4);\n        pixel[0] = pixelsData[index++] / 255; // r\n        pixel[1] = pixelsData[index++] / 255; // g\n        pixel[2] = pixelsData[index++] / 255; // b\n        pixel[3] = pixelsData[index++] / 255; // a\n        row[x] = pixel;\n      }\n    }\n    return imageArray;\n  }\n\n  /**\n   *\n   * @param flip\n   * @return {Uint8ClampedArray}\n   */\n  getPixels(flip) {\n    const [width, height] = this.output;\n    // cpu is not flipped by default\n    return flip ? utils.flipPixels(this._imageData.data, width, height) : this._imageData.data.slice(0);\n  }\n\n  _imageTo3DArray(images) {\n    const imagesArray = new Array(images.length);\n    for (let i = 0; i < images.length; i++) {\n      imagesArray[i] = this._mediaTo2DArray(images[i]);\n    }\n    return imagesArray;\n  }\n\n  _resultKernelHeader() {\n    if (this.graphical) return '';\n    if (this.immutable) return '';\n    if (!this.pipeline) return '';\n    switch (this.output.length) {\n      case 1:\n        return this._mutableKernel1DResults();\n      case 2:\n        return this._mutableKernel2DResults();\n      case 3:\n        return this._mutableKernel3DResults();\n    }\n  }\n\n  _resultKernelBody(kernelString) {\n    switch (this.output.length) {\n      case 1:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel1DLoop(kernelString) : this._resultImmutableKernel1DLoop(kernelString)) + this._kernelOutput();\n      case 2:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel2DLoop(kernelString) : this._resultImmutableKernel2DLoop(kernelString)) + this._kernelOutput();\n      case 3:\n        return (!this.immutable && this.pipeline ? this._resultMutableKernel3DLoop(kernelString) : this._resultImmutableKernel3DLoop(kernelString)) + this._kernelOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalKernelBody(kernelThreadString) {\n    switch (this.output.length) {\n      case 2:\n        return this._graphicalKernel2DLoop(kernelThreadString) + this._graphicalOutput();\n      default:\n        throw new Error('unsupported size kernel');\n    }\n  }\n\n  _graphicalOutput() {\n    return `\n    this._imageData.data.set(this._colorData);\n    this.context.putImageData(this._imageData, 0, 0);\n    return;`\n  }\n\n  _getKernelResultTypeConstructorString() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return 'Float32Array';\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        return 'Array';\n      default:\n        if (this.graphical) {\n          return 'Float32Array';\n        }\n        throw new Error(`unhandled returnType ${ this.returnType }`);\n    }\n  }\n\n  _resultImmutableKernel1DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _mutableKernel1DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const result = new ${constructorString}(outputX);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(outputX);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }`;\n  }\n\n  _resultMutableKernel1DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    for (let x = 0; x < outputX; x++) {\n      this.thread.x = x;\n      this.thread.y = 0;\n      this.thread.z = 0;\n      ${ kernelString }\n    }`;\n  }\n\n  _resultImmutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _mutableKernel2DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const result = new Array(outputY);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputY);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let y = 0; y < outputY; y++) {\n      const resultX = result[y] = new ${constructorString}(outputX);\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n    }`;\n  }\n\n  _resultMutableKernel2DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      const resultX = result[y];\n      ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('') }\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _graphicalKernel2DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    for (let y = 0; y < outputY; y++) {\n      this.thread.z = 0;\n      this.thread.y = y;\n      for (let x = 0; x < outputX; x++) {\n        this.thread.x = x;\n        ${ kernelString }\n      }\n    }`;\n  }\n\n  _resultImmutableKernel3DLoop(kernelString) {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _mutableKernel3DResults() {\n    const constructorString = this._getKernelResultTypeConstructorString();\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    const result = new Array(outputZ);\n    ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(outputZ);\\n`).join('    ') }\n    ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\\n`).join('    ') }\n    for (let z = 0; z < outputZ; z++) {\n      const resultY = result[z] = new Array(outputY);\n      ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(outputY);\\n`).join('      ') }\n      for (let y = 0; y < outputY; y++) {\n        const resultX = resultY[y] = new ${constructorString}(outputX);\n        ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(outputX);\\n`).join('        ') }\n      }\n    }`;\n  }\n\n  _resultMutableKernel3DLoop(kernelString) {\n    return `  const outputX = _this.output[0];\n    const outputY = _this.output[1];\n    const outputZ = _this.output[2];\n    for (let z = 0; z < outputZ; z++) {\n      this.thread.z = z;\n      const resultY = result[z];\n      for (let y = 0; y < outputY; y++) {\n        this.thread.y = y;\n        const resultX = resultY[y];\n        for (let x = 0; x < outputX; x++) {\n          this.thread.x = x;\n          ${ kernelString }\n        }\n      }\n    }`;\n  }\n\n  _kernelOutput() {\n    if (!this.subKernels) {\n      return '\\n    return result;';\n    }\n    return `\\n    return {\n      result: result,\n      ${ this.subKernels.map(subKernel => `${ subKernel.property }: result_${ subKernel.name }`).join(',\\n      ') }\n    };`;\n  }\n\n  _mapSubKernels(fn) {\n    return this.subKernels === null ? [''] :\n      this.subKernels.map(fn);\n  }\n\n  destroy(removeCanvasReference) {\n    if (removeCanvasReference) {\n      delete this.canvas;\n    }\n  }\n\n  static destroyContext(context) {}\n\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, CPUFunctionNode).toJSON();\n    return json;\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    const [width, height] = this.output;\n    if (this.graphical) {\n      this._imageData = this.context.createImageData(width, height);\n      this._colorData = new Uint8ClampedArray(width * height * 4);\n    }\n  }\n\n  prependString(value) {\n    if (this._kernelString) throw new Error('Kernel already built');\n    this._prependedString.push(value);\n  }\n\n  hasPrependString(value) {\n    return this._prependedString.indexOf(value) > -1;\n  }\n}\n\nmodule.exports = {\n  CPUKernel\n};"
  },
  {
    "path": "src/backend/function-builder.js",
    "content": "/**\n * @desc This handles all the raw state, converted state, etc. of a single function.\n * [INTERNAL] A collection of functionNodes.\n * @class\n */\nclass FunctionBuilder {\n  /**\n   *\n   * @param {Kernel} kernel\n   * @param {FunctionNode} FunctionNode\n   * @param {object} [extraNodeOptions]\n   * @returns {FunctionBuilder}\n   * @static\n   */\n  static fromKernel(kernel, FunctionNode, extraNodeOptions) {\n    const {\n      kernelArguments,\n      kernelConstants,\n      argumentNames,\n      argumentSizes,\n      argumentBitRatios,\n      constants,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      nativeFunctions,\n      output,\n      optimizeFloatMemory,\n      precision,\n      plugins,\n      source,\n      subKernels,\n      functions,\n      leadingReturnStatement,\n      followingReturnStatement,\n      dynamicArguments,\n      dynamicOutput,\n    } = kernel;\n\n    const argumentTypes = new Array(kernelArguments.length);\n    const constantTypes = {};\n\n    for (let i = 0; i < kernelArguments.length; i++) {\n      argumentTypes[i] = kernelArguments[i].type;\n    }\n\n    for (let i = 0; i < kernelConstants.length; i++) {\n      const kernelConstant = kernelConstants[i];\n      constantTypes[kernelConstant.name] = kernelConstant.type;\n    }\n\n    const needsArgumentType = (functionName, index) => {\n      return functionBuilder.needsArgumentType(functionName, index);\n    };\n\n    const assignArgumentType = (functionName, index, type) => {\n      functionBuilder.assignArgumentType(functionName, index, type);\n    };\n\n    const lookupReturnType = (functionName, ast, requestingNode) => {\n      return functionBuilder.lookupReturnType(functionName, ast, requestingNode);\n    };\n\n    const lookupFunctionArgumentTypes = (functionName) => {\n      return functionBuilder.lookupFunctionArgumentTypes(functionName);\n    };\n\n    const lookupFunctionArgumentName = (functionName, argumentIndex) => {\n      return functionBuilder.lookupFunctionArgumentName(functionName, argumentIndex);\n    };\n\n    const lookupFunctionArgumentBitRatio = (functionName, argumentName) => {\n      return functionBuilder.lookupFunctionArgumentBitRatio(functionName, argumentName);\n    };\n\n    const triggerImplyArgumentType = (functionName, i, argumentType, requestingNode) => {\n      functionBuilder.assignArgumentType(functionName, i, argumentType, requestingNode);\n    };\n\n    const triggerImplyArgumentBitRatio = (functionName, argumentName, calleeFunctionName, argumentIndex) => {\n      functionBuilder.assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex);\n    };\n\n    const onFunctionCall = (functionName, calleeFunctionName, args) => {\n      functionBuilder.trackFunctionCall(functionName, calleeFunctionName, args);\n    };\n\n    const onNestedFunction = (ast, source) => {\n      const argumentNames = [];\n      for (let i = 0; i < ast.params.length; i++) {\n        argumentNames.push(ast.params[i].name);\n      }\n      const nestedFunction = new FunctionNode(source, Object.assign({}, nodeOptions, {\n        returnType: null,\n        ast,\n        name: ast.id.name,\n        argumentNames,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n      }));\n      nestedFunction.traceFunctionAST(ast);\n      functionBuilder.addFunctionNode(nestedFunction);\n    };\n\n    const nodeOptions = Object.assign({\n      isRootKernel: false,\n      onNestedFunction,\n      lookupReturnType,\n      lookupFunctionArgumentTypes,\n      lookupFunctionArgumentName,\n      lookupFunctionArgumentBitRatio,\n      needsArgumentType,\n      assignArgumentType,\n      triggerImplyArgumentType,\n      triggerImplyArgumentBitRatio,\n      onFunctionCall,\n      optimizeFloatMemory,\n      precision,\n      constants,\n      constantTypes,\n      constantBitRatios,\n      debug,\n      loopMaxIterations,\n      output,\n      plugins,\n      dynamicArguments,\n      dynamicOutput,\n    }, extraNodeOptions || {});\n\n    const rootNodeOptions = Object.assign({}, nodeOptions, {\n      isRootKernel: true,\n      name: 'kernel',\n      argumentNames,\n      argumentTypes,\n      argumentSizes,\n      argumentBitRatios,\n      leadingReturnStatement,\n      followingReturnStatement,\n    });\n\n    if (typeof source === 'object' && source.functionNodes) {\n      return new FunctionBuilder().fromJSON(source.functionNodes, FunctionNode);\n    }\n\n    const rootNode = new FunctionNode(source, rootNodeOptions);\n\n    let functionNodes = null;\n    if (functions) {\n      functionNodes = functions.map((fn) => new FunctionNode(fn.source, {\n        returnType: fn.returnType,\n        argumentTypes: fn.argumentTypes,\n        output,\n        plugins,\n        constants,\n        constantTypes,\n        constantBitRatios,\n        optimizeFloatMemory,\n        precision,\n        lookupReturnType,\n        lookupFunctionArgumentTypes,\n        lookupFunctionArgumentName,\n        lookupFunctionArgumentBitRatio,\n        needsArgumentType,\n        assignArgumentType,\n        triggerImplyArgumentType,\n        triggerImplyArgumentBitRatio,\n        onFunctionCall,\n        onNestedFunction,\n      }));\n    }\n\n    let subKernelNodes = null;\n    if (subKernels) {\n      subKernelNodes = subKernels.map((subKernel) => {\n        const { name, source } = subKernel;\n        return new FunctionNode(source, Object.assign({}, nodeOptions, {\n          name,\n          isSubKernel: true,\n          isRootKernel: false,\n        }));\n      });\n    }\n\n    const functionBuilder = new FunctionBuilder({\n      kernel,\n      rootNode,\n      functionNodes,\n      nativeFunctions,\n      subKernelNodes\n    });\n\n    return functionBuilder;\n  }\n\n  /**\n   *\n   * @param {IFunctionBuilderSettings} [settings]\n   */\n  constructor(settings) {\n    settings = settings || {};\n    this.kernel = settings.kernel;\n    this.rootNode = settings.rootNode;\n    this.functionNodes = settings.functionNodes || [];\n    this.subKernelNodes = settings.subKernelNodes || [];\n    this.nativeFunctions = settings.nativeFunctions || [];\n    this.functionMap = {};\n    this.nativeFunctionNames = [];\n    this.lookupChain = [];\n    this.functionNodeDependencies = {};\n    this.functionCalls = {};\n\n    if (this.rootNode) {\n      this.functionMap['kernel'] = this.rootNode;\n    }\n\n    if (this.functionNodes) {\n      for (let i = 0; i < this.functionNodes.length; i++) {\n        this.functionMap[this.functionNodes[i].name] = this.functionNodes[i];\n      }\n    }\n\n    if (this.subKernelNodes) {\n      for (let i = 0; i < this.subKernelNodes.length; i++) {\n        this.functionMap[this.subKernelNodes[i].name] = this.subKernelNodes[i];\n      }\n    }\n\n    if (this.nativeFunctions) {\n      for (let i = 0; i < this.nativeFunctions.length; i++) {\n        const nativeFunction = this.nativeFunctions[i];\n        this.nativeFunctionNames.push(nativeFunction.name);\n      }\n    }\n  }\n\n  /**\n   * @desc Add the function node directly\n   *\n   * @param {FunctionNode} functionNode - functionNode to add\n   *\n   */\n  addFunctionNode(functionNode) {\n    if (!functionNode.name) throw new Error('functionNode.name needs set');\n    this.functionMap[functionNode.name] = functionNode;\n    if (functionNode.isRootKernel) {\n      this.rootNode = functionNode;\n    }\n  }\n\n  /**\n   * @desc Trace all the depending functions being called, from a single function\n   *\n   * This allow for 'unneeded' functions to be automatically optimized out.\n   * Note that the 0-index, is the starting function trace.\n   *\n   * @param {String} functionName - Function name to trace from, default to 'kernel'\n   * @param {String[]} [retList] - Returning list of function names that is traced. Including itself.\n   *\n   * @returns {String[]}  Returning list of function names that is traced. Including itself.\n   */\n  traceFunctionCalls(functionName, retList) {\n    functionName = functionName || 'kernel';\n    retList = retList || [];\n\n    if (this.nativeFunctionNames.indexOf(functionName) > -1) {\n      const nativeFunctionIndex = retList.indexOf(functionName);\n      if (nativeFunctionIndex === -1) {\n        retList.push(functionName);\n      } else {\n        /**\n         * https://github.com/gpujs/gpu.js/issues/207\n         * if dependent function is already in the list, because a function depends on it, and because it has\n         * already been traced, we know that we must move the dependent function to the end of the the retList.\n         * */\n        const dependantNativeFunctionName = retList.splice(nativeFunctionIndex, 1)[0];\n        retList.push(dependantNativeFunctionName);\n      }\n      return retList;\n    }\n\n    const functionNode = this.functionMap[functionName];\n    if (functionNode) {\n      // Check if function already exists\n      const functionIndex = retList.indexOf(functionName);\n      if (functionIndex === -1) {\n        retList.push(functionName);\n        functionNode.toString(); //ensure JS trace is done\n        for (let i = 0; i < functionNode.calledFunctions.length; ++i) {\n          this.traceFunctionCalls(functionNode.calledFunctions[i], retList);\n        }\n      } else {\n        /**\n         * https://github.com/gpujs/gpu.js/issues/207\n         * if dependent function is already in the list, because a function depends on it, and because it has\n         * already been traced, we know that we must move the dependent function to the end of the the retList.\n         * */\n        const dependantFunctionName = retList.splice(functionIndex, 1)[0];\n        retList.push(dependantFunctionName);\n      }\n    }\n\n    return retList;\n  }\n\n  /**\n   * @desc Return the string for a function\n   * @param {String} functionName - Function name to trace from. If null, it returns the WHOLE builder stack\n   * @returns {String} The full string, of all the various functions. Trace optimized if functionName given\n   */\n  getPrototypeString(functionName) {\n    return this.getPrototypes(functionName).join('\\n');\n  }\n\n  /**\n   * @desc Return the string for a function\n   * @param {String} [functionName] - Function name to trace from. If null, it returns the WHOLE builder stack\n   * @returns {Array} The full string, of all the various functions. Trace optimized if functionName given\n   */\n  getPrototypes(functionName) {\n    if (this.rootNode) {\n      this.rootNode.toString();\n    }\n    if (functionName) {\n      return this.getPrototypesFromFunctionNames(this.traceFunctionCalls(functionName, []).reverse());\n    }\n    return this.getPrototypesFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  /**\n   * @desc Get string from function names\n   * @param {String[]} functionList - List of function to build string\n   * @returns {String} The string, of all the various functions. Trace optimized if functionName given\n   */\n  getStringFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const node = this.functionMap[functionList[i]];\n      if (node) {\n        ret.push(this.functionMap[functionList[i]].toString());\n      }\n    }\n    return ret.join('\\n');\n  }\n\n  /**\n   * @desc Return string of all functions converted\n   * @param {String[]} functionList - List of function names to build the string.\n   * @returns {Array} Prototypes of all functions converted\n   */\n  getPrototypesFromFunctionNames(functionList) {\n    const ret = [];\n    for (let i = 0; i < functionList.length; ++i) {\n      const functionName = functionList[i];\n      const functionIndex = this.nativeFunctionNames.indexOf(functionName);\n      if (functionIndex > -1) {\n        ret.push(this.nativeFunctions[functionIndex].source);\n        continue;\n      }\n      const node = this.functionMap[functionName];\n      if (node) {\n        ret.push(node.toString());\n      }\n    }\n    return ret;\n  }\n\n  toJSON() {\n    return this.traceFunctionCalls(this.rootNode.name).reverse().map(name => {\n      const nativeIndex = this.nativeFunctions.indexOf(name);\n      if (nativeIndex > -1) {\n        return {\n          name,\n          source: this.nativeFunctions[nativeIndex].source\n        };\n      } else if (this.functionMap[name]) {\n        return this.functionMap[name].toJSON();\n      } else {\n        throw new Error(`function ${ name } not found`);\n      }\n    });\n  }\n\n  fromJSON(jsonFunctionNodes, FunctionNode) {\n    this.functionMap = {};\n    for (let i = 0; i < jsonFunctionNodes.length; i++) {\n      const jsonFunctionNode = jsonFunctionNodes[i];\n      this.functionMap[jsonFunctionNode.settings.name] = new FunctionNode(jsonFunctionNode.ast, jsonFunctionNode.settings);\n    }\n    return this;\n  }\n\n  /**\n   * @desc Get string for a particular function name\n   * @param {String} functionName - Function name to trace from. If null, it returns the WHOLE builder stack\n   * @returns {String} settings - The string, of all the various functions. Trace optimized if functionName given\n   */\n  getString(functionName) {\n    if (functionName) {\n      return this.getStringFromFunctionNames(this.traceFunctionCalls(functionName).reverse());\n    }\n    return this.getStringFromFunctionNames(Object.keys(this.functionMap));\n  }\n\n  lookupReturnType(functionName, ast, requestingNode) {\n    if (ast.type !== 'CallExpression') {\n      throw new Error(`expected ast type of \"CallExpression\", but is ${ ast.type }`);\n    }\n    if (this._isNativeFunction(functionName)) {\n      return this._lookupNativeFunctionReturnType(functionName);\n    } else if (this._isFunction(functionName)) {\n      const node = this._getFunction(functionName);\n      if (node.returnType) {\n        return node.returnType;\n      } else {\n        for (let i = 0; i < this.lookupChain.length; i++) {\n          // detect circlical logic\n          if (this.lookupChain[i].ast === ast) {\n            // detect if arguments have not resolved, preventing a return type\n            // if so, go ahead and resolve them, so we can resolve the return type\n            if (node.argumentTypes.length === 0 && ast.arguments.length > 0) {\n              const args = ast.arguments;\n              for (let j = 0; j < args.length; j++) {\n                this.lookupChain.push({\n                  name: requestingNode.name,\n                  ast: args[i],\n                  requestingNode\n                });\n                node.argumentTypes[j] = requestingNode.getType(args[j]);\n                this.lookupChain.pop();\n              }\n              return node.returnType = node.getType(node.getJsAST());\n            }\n\n            throw new Error('circlical logic detected!');\n          }\n        }\n        // get ready for a ride!\n        this.lookupChain.push({\n          name: requestingNode.name,\n          ast,\n          requestingNode\n        });\n        const type = node.getType(node.getJsAST());\n        this.lookupChain.pop();\n        return node.returnType = type;\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   *\n   * @param {String} functionName\n   * @return {FunctionNode}\n   * @private\n   */\n  _getFunction(functionName) {\n    if (!this._isFunction(functionName)) {\n      new Error(`Function ${functionName} not found`);\n    }\n    return this.functionMap[functionName];\n  }\n\n  _isFunction(functionName) {\n    return Boolean(this.functionMap[functionName]);\n  }\n\n  _getNativeFunction(functionName) {\n    for (let i = 0; i < this.nativeFunctions.length; i++) {\n      if (this.nativeFunctions[i].name === functionName) return this.nativeFunctions[i];\n    }\n    return null;\n  }\n\n  _isNativeFunction(functionName) {\n    return Boolean(this._getNativeFunction(functionName));\n  }\n\n  _lookupNativeFunctionReturnType(functionName) {\n    let nativeFunction = this._getNativeFunction(functionName);\n    if (nativeFunction) {\n      return nativeFunction.returnType;\n    }\n    throw new Error(`Native function ${ functionName } not found`);\n  }\n\n  lookupFunctionArgumentTypes(functionName) {\n    if (this._isNativeFunction(functionName)) {\n      return this._getNativeFunction(functionName).argumentTypes;\n    } else if (this._isFunction(functionName)) {\n      return this._getFunction(functionName).argumentTypes;\n    }\n    return null;\n  }\n\n  lookupFunctionArgumentName(functionName, argumentIndex) {\n    return this._getFunction(functionName).argumentNames[argumentIndex];\n  }\n\n  /**\n   *\n   * @param {string} functionName\n   * @param {string} argumentName\n   * @return {number}\n   */\n  lookupFunctionArgumentBitRatio(functionName, argumentName) {\n    if (!this._isFunction(functionName)) {\n      throw new Error('function not found');\n    }\n    if (this.rootNode.name === functionName) {\n      const i = this.rootNode.argumentNames.indexOf(argumentName);\n      if (i !== -1) {\n        return this.rootNode.argumentBitRatios[i];\n      }\n    }\n    const node = this._getFunction(functionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error('argument not found');\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error('argument bit ratio not found');\n    }\n    return bitRatio;\n  }\n\n  needsArgumentType(functionName, i) {\n    if (!this._isFunction(functionName)) return false;\n    const fnNode = this._getFunction(functionName);\n    return !fnNode.argumentTypes[i];\n  }\n\n  assignArgumentType(functionName, i, argumentType, requestingNode) {\n    if (!this._isFunction(functionName)) return;\n    const fnNode = this._getFunction(functionName);\n    if (!fnNode.argumentTypes[i]) {\n      fnNode.argumentTypes[i] = argumentType;\n    }\n  }\n\n  /**\n   * @param {string} functionName\n   * @param {string} argumentName\n   * @param {string} calleeFunctionName\n   * @param {number} argumentIndex\n   * @return {number|null}\n   */\n  assignArgumentBitRatio(functionName, argumentName, calleeFunctionName, argumentIndex) {\n    const node = this._getFunction(functionName);\n    if (this._isNativeFunction(calleeFunctionName)) return null;\n    const calleeNode = this._getFunction(calleeFunctionName);\n    const i = node.argumentNames.indexOf(argumentName);\n    if (i === -1) {\n      throw new Error(`Argument ${argumentName} not found in arguments from function ${functionName}`);\n    }\n    const bitRatio = node.argumentBitRatios[i];\n    if (typeof bitRatio !== 'number') {\n      throw new Error(`Bit ratio for argument ${argumentName} not found in function ${functionName}`);\n    }\n    if (!calleeNode.argumentBitRatios) {\n      calleeNode.argumentBitRatios = new Array(calleeNode.argumentNames.length);\n    }\n    const calleeBitRatio = calleeNode.argumentBitRatios[i];\n    if (typeof calleeBitRatio === 'number') {\n      if (calleeBitRatio !== bitRatio) {\n        throw new Error(`Incompatible bit ratio found at function ${functionName} at argument ${argumentName}`);\n      }\n      return calleeBitRatio;\n    }\n    calleeNode.argumentBitRatios[i] = bitRatio;\n    return bitRatio;\n  }\n\n  trackFunctionCall(functionName, calleeFunctionName, args) {\n    if (!this.functionNodeDependencies[functionName]) {\n      this.functionNodeDependencies[functionName] = new Set();\n      this.functionCalls[functionName] = [];\n    }\n    this.functionNodeDependencies[functionName].add(calleeFunctionName);\n    this.functionCalls[functionName].push(args);\n  }\n\n  getKernelResultType() {\n    return this.rootNode.returnType || this.rootNode.getType(this.rootNode.ast);\n  }\n\n  getSubKernelResultType(index) {\n    const subKernelNode = this.subKernelNodes[index];\n    let called = false;\n    for (let functionCallIndex = 0; functionCallIndex < this.rootNode.functionCalls.length; functionCallIndex++) {\n      const functionCall = this.rootNode.functionCalls[functionCallIndex];\n      if (functionCall.ast.callee.name === subKernelNode.name) {\n        called = true;\n      }\n    }\n    if (!called) {\n      throw new Error(`SubKernel ${ subKernelNode.name } never called by kernel`);\n    }\n    return subKernelNode.returnType || subKernelNode.getType(subKernelNode.getJsAST());\n  }\n\n  getReturnTypes() {\n    const result = {\n      [this.rootNode.name]: this.rootNode.getType(this.rootNode.ast),\n    };\n    const list = this.traceFunctionCalls(this.rootNode.name);\n    for (let i = 0; i < list.length; i++) {\n      const functionName = list[i];\n      const functionNode = this.functionMap[functionName];\n      result[functionName] = functionNode.getType(functionNode.ast);\n    }\n    return result;\n  }\n}\n\nmodule.exports = {\n  FunctionBuilder\n};"
  },
  {
    "path": "src/backend/function-node.js",
    "content": "const acorn = require('acorn');\nconst { utils } = require('../utils');\nconst { FunctionTracer } = require('./function-tracer');\n\n/**\n *\n * @desc Represents a single function, inside JS, webGL, or openGL.\n * <p>This handles all the raw state, converted state, etc. Of a single function.</p>\n */\nclass FunctionNode {\n  /**\n   *\n   * @param {string|object} source\n   * @param {IFunctionSettings} [settings]\n   */\n  constructor(source, settings) {\n    if (!source && !settings.ast) {\n      throw new Error('source parameter is missing');\n    }\n    settings = settings || {};\n    this.source = source;\n    this.ast = null;\n    this.name = typeof source === 'string' ? settings.isRootKernel ?\n      'kernel' :\n      (settings.name || utils.getFunctionNameFromString(source)) : null;\n    this.calledFunctions = [];\n    this.constants = {};\n    this.constantTypes = {};\n    this.constantBitRatios = {};\n    this.isRootKernel = false;\n    this.isSubKernel = false;\n    this.debug = null;\n    this.functions = null;\n    this.identifiers = null;\n    this.contexts = null;\n    this.functionCalls = null;\n    this.states = [];\n    this.needsArgumentType = null;\n    this.assignArgumentType = null;\n    this.lookupReturnType = null;\n    this.lookupFunctionArgumentTypes = null;\n    this.lookupFunctionArgumentBitRatio = null;\n    this.triggerImplyArgumentType = null;\n    this.triggerImplyArgumentBitRatio = null;\n    this.onNestedFunction = null;\n    this.onFunctionCall = null;\n    this.optimizeFloatMemory = null;\n    this.precision = null;\n    this.loopMaxIterations = null;\n    this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null);\n    this.argumentTypes = [];\n    this.argumentSizes = [];\n    this.argumentBitRatios = null;\n    this.returnType = null;\n    this.output = [];\n    this.plugins = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.dynamicOutput = null;\n    this.dynamicArguments = null;\n    this.strictTypingChecking = false;\n    this.fixIntegerDivisionAccuracy = null;\n\n    if (settings) {\n      for (const p in settings) {\n        if (!settings.hasOwnProperty(p)) continue;\n        if (!this.hasOwnProperty(p)) continue;\n        this[p] = settings[p];\n      }\n    }\n\n    this.literalTypes = {};\n\n    this.validate();\n    this._string = null;\n    this._internalVariableNames = {};\n  }\n\n  validate() {\n    if (typeof this.source !== 'string' && !this.ast) {\n      throw new Error('this.source not a string');\n    }\n\n    if (!this.ast && !utils.isFunctionString(this.source)) {\n      throw new Error('this.source not a function string');\n    }\n\n    if (!this.name) {\n      throw new Error('this.name could not be set');\n    }\n\n    if (this.argumentTypes.length > 0 && this.argumentTypes.length !== this.argumentNames.length) {\n      throw new Error(`argumentTypes count of ${ this.argumentTypes.length } exceeds ${ this.argumentNames.length }`);\n    }\n\n    if (this.output.length < 1) {\n      throw new Error('this.output is not big enough');\n    }\n  }\n\n  /**\n   * @param {String} name\n   * @returns {boolean}\n   */\n  isIdentifierConstant(name) {\n    if (!this.constants) return false;\n    return this.constants.hasOwnProperty(name);\n  }\n\n  isInput(argumentName) {\n    return this.argumentTypes[this.argumentNames.indexOf(argumentName)] === 'Input';\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.state !== state) {\n      throw new Error(`Cannot popState ${ state } when in ${ this.state }`);\n    }\n    this.states.pop();\n  }\n\n  isState(state) {\n    return this.state === state;\n  }\n\n  get state() {\n    return this.states[this.states.length - 1];\n  }\n\n  /**\n   * @function\n   * @name astMemberExpressionUnroll\n   * @desc Parses the abstract syntax tree for binary expression.\n   *\n   * <p>Utility function for astCallExpression.</p>\n   *\n   * @param {Object} ast - the AST object to parse\n   *\n   * @returns {String} the function namespace call, unrolled\n   */\n  astMemberExpressionUnroll(ast) {\n    if (ast.type === 'Identifier') {\n      return ast.name;\n    } else if (ast.type === 'ThisExpression') {\n      return 'this';\n    }\n\n    if (ast.type === 'MemberExpression') {\n      if (ast.object && ast.property) {\n        //babel sniffing\n        if (ast.object.hasOwnProperty('name') && ast.object.name !== 'Math') {\n          return this.astMemberExpressionUnroll(ast.property);\n        }\n\n        return (\n          this.astMemberExpressionUnroll(ast.object) +\n          '.' +\n          this.astMemberExpressionUnroll(ast.property)\n        );\n      }\n    }\n\n    //babel sniffing\n    if (ast.hasOwnProperty('expressions')) {\n      const firstExpression = ast.expressions[0];\n      if (firstExpression.type === 'Literal' && firstExpression.value === 0 && ast.expressions.length === 2) {\n        return this.astMemberExpressionUnroll(ast.expressions[1]);\n      }\n    }\n\n    // Failure, unknown expression\n    throw this.astErrorOutput('Unknown astMemberExpressionUnroll', ast);\n  }\n\n  /**\n   * @desc Parses the class function JS, and returns its Abstract Syntax Tree object.\n   * This is used internally to convert to shader code\n   *\n   * @param {Object} [inParser] - Parser to use, assumes in scope 'parser' if null or undefined\n   *\n   * @returns {Object} The function AST Object, note that result is cached under this.ast;\n   */\n  getJsAST(inParser) {\n    if (this.ast) {\n      return this.ast;\n    }\n    if (typeof this.source === 'object') {\n      this.traceFunctionAST(this.source);\n      return this.ast = this.source;\n    }\n\n    inParser = inParser || acorn;\n    if (inParser === null) {\n      throw new Error('Missing JS to AST parser');\n    }\n\n    const ast = Object.freeze(inParser.parse(`const parser_${ this.name } = ${ this.source };`, {\n      locations: true\n    }));\n    // take out the function object, outside the var declarations\n    const functionAST = ast.body[0].declarations[0].init;\n    this.traceFunctionAST(functionAST);\n\n    if (!ast) {\n      throw new Error('Failed to parse JS code');\n    }\n\n    return this.ast = functionAST;\n  }\n\n  traceFunctionAST(ast) {\n    const { contexts, declarations, functions, identifiers, functionCalls } = new FunctionTracer(ast);\n    this.contexts = contexts;\n    this.identifiers = identifiers;\n    this.functionCalls = functionCalls;\n    this.functions = functions;\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const { ast, inForLoopInit, inForLoopTest } = declaration;\n      const { init } = ast;\n      const dependencies = this.getDependencies(init);\n      let valueType = null;\n\n      if (inForLoopInit && inForLoopTest) {\n        valueType = 'Integer';\n      } else {\n        if (init) {\n          const realType = this.getType(init);\n          switch (realType) {\n            case 'Integer':\n            case 'Float':\n            case 'Number':\n              if (init.type === 'MemberExpression') {\n                valueType = realType;\n              } else {\n                valueType = 'Number';\n              }\n              break;\n            case 'LiteralInteger':\n              valueType = 'Number';\n              break;\n            default:\n              valueType = realType;\n          }\n        }\n      }\n      declaration.valueType = valueType;\n      declaration.dependencies = dependencies;\n      declaration.isSafe = this.isSafeDependencies(dependencies);\n    }\n\n    for (let i = 0; i < functions.length; i++) {\n      this.onNestedFunction(functions[i], this.source);\n    }\n  }\n\n  getDeclaration(ast) {\n    for (let i = 0; i < this.identifiers.length; i++) {\n      const identifier = this.identifiers[i];\n      if (ast === identifier.ast) {\n        return identifier.declaration;\n      }\n    }\n    return null;\n  }\n\n  /**\n   * @desc Return the type of parameter sent to subKernel/Kernel.\n   * @param {Object} ast - Identifier\n   * @returns {String} Type of the parameter\n   */\n  getVariableType(ast) {\n    if (ast.type !== 'Identifier') {\n      throw new Error(`ast of ${ast.type} not \"Identifier\"`);\n    }\n    let type = null;\n    const argumentIndex = this.argumentNames.indexOf(ast.name);\n    if (argumentIndex === -1) {\n      const declaration = this.getDeclaration(ast);\n      if (declaration) {\n        return declaration.valueType;\n      }\n    } else {\n      const argumentType = this.argumentTypes[argumentIndex];\n      if (argumentType) {\n        type = argumentType;\n      }\n    }\n    if (!type && this.strictTypingChecking) {\n      throw new Error(`Declaration of ${name} not found`);\n    }\n    return type;\n  }\n\n  /**\n   * Generally used to lookup the value type returned from a member expressions\n   * @param {String} type\n   * @return {String}\n   */\n  getLookupType(type) {\n    if (!typeLookupMap.hasOwnProperty(type)) {\n      throw new Error(`unknown typeLookupMap ${ type }`);\n    }\n    return typeLookupMap[type];\n  }\n\n  getConstantType(constantName) {\n    if (this.constantTypes[constantName]) {\n      const type = this.constantTypes[constantName];\n      if (type === 'Float') {\n        return 'Number';\n      } else {\n        return type;\n      }\n    }\n    throw new Error(`Type for constant \"${ constantName }\" not declared`);\n  }\n\n  toString() {\n    if (this._string) return this._string;\n    return this._string = this.astGeneric(this.getJsAST(), []).join('').trim();\n  }\n\n  toJSON() {\n    const settings = {\n      source: this.source,\n      name: this.name,\n      constants: this.constants,\n      constantTypes: this.constantTypes,\n      isRootKernel: this.isRootKernel,\n      isSubKernel: this.isSubKernel,\n      debug: this.debug,\n      output: this.output,\n      loopMaxIterations: this.loopMaxIterations,\n      argumentNames: this.argumentNames,\n      argumentTypes: this.argumentTypes,\n      argumentSizes: this.argumentSizes,\n      returnType: this.returnType,\n      leadingReturnStatement: this.leadingReturnStatement,\n      followingReturnStatement: this.followingReturnStatement,\n    };\n\n    return {\n      ast: this.ast,\n      settings\n    };\n  }\n\n  /**\n   * Recursively looks up type for ast expression until it's found\n   * @param ast\n   * @returns {String|null}\n   */\n  getType(ast) {\n    if (Array.isArray(ast)) {\n      return this.getType(ast[ast.length - 1]);\n    }\n    switch (ast.type) {\n      case 'BlockStatement':\n        return this.getType(ast.body);\n      case 'ArrayExpression':\n        const childType = this.getType(ast.elements[0]);\n        switch (childType) {\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            return `Matrix(${ast.elements.length})`;\n        }\n        return `Array(${ ast.elements.length })`;\n      case 'Literal':\n        const literalKey = this.astKey(ast);\n        if (this.literalTypes[literalKey]) {\n          return this.literalTypes[literalKey];\n        }\n        if (Number.isInteger(ast.value)) {\n          return 'LiteralInteger';\n        } else if (ast.value === true || ast.value === false) {\n          return 'Boolean';\n        } else {\n          return 'Number';\n        }\n      case 'AssignmentExpression':\n        return this.getType(ast.left);\n      case 'CallExpression':\n        if (this.isAstMathFunction(ast)) {\n          return 'Number';\n        }\n        if (!ast.callee || !ast.callee.name) {\n          if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) {\n            const functionName = ast.callee.expressions[ast.callee.expressions.length - 1].property.name;\n            this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n            return this.lookupReturnType(functionName, ast, this);\n          }\n          if (this.getVariableSignature(ast.callee, true) === 'this.color') {\n            return null;\n          }\n          if (ast.callee.type === 'MemberExpression' && ast.callee.object && ast.callee.property && ast.callee.property.name && ast.arguments) {\n            const functionName = ast.callee.property.name;\n            this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n            return this.lookupReturnType(functionName, ast, this);\n          }\n          throw this.astErrorOutput('Unknown call expression', ast);\n        }\n        if (ast.callee && ast.callee.name) {\n          const functionName = ast.callee.name;\n          this.inferArgumentTypesIfNeeded(functionName, ast.arguments);\n          return this.lookupReturnType(functionName, ast, this);\n        }\n        throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n      case 'LogicalExpression':\n        return 'Boolean';\n      case 'BinaryExpression':\n        // modulos is Number\n        switch (ast.operator) {\n          case '%':\n          case '/':\n            if (this.fixIntegerDivisionAccuracy) {\n              return 'Number';\n            } else {\n              break;\n            }\n          case '>':\n          case '<':\n            return 'Boolean';\n          case '&':\n          case '|':\n          case '^':\n          case '<<':\n          case '>>':\n          case '>>>':\n            return 'Integer';\n        }\n        const type = this.getType(ast.left);\n        if (this.isState('skip-literal-correction')) return type;\n        if (type === 'LiteralInteger') {\n          const rightType = this.getType(ast.right);\n          if (rightType === 'LiteralInteger') {\n            if (ast.left.value % 1 === 0) {\n              return 'Integer';\n            } else {\n              return 'Float';\n            }\n          }\n          return rightType;\n        }\n        return typeLookupMap[type] || type;\n      case 'UpdateExpression':\n        return this.getType(ast.argument);\n      case 'UnaryExpression':\n        if (ast.operator === '~') {\n          return 'Integer';\n        }\n        return this.getType(ast.argument);\n      case 'VariableDeclaration': {\n        const declarations = ast.declarations;\n        let lastType;\n        for (let i = 0; i < declarations.length; i++) {\n          const declaration = declarations[i];\n          lastType = this.getType(declaration);\n        }\n        if (!lastType) {\n          throw this.astErrorOutput(`Unable to find type for declaration`, ast);\n        }\n        return lastType;\n      }\n      case 'VariableDeclarator':\n        const declaration = this.getDeclaration(ast.id);\n        if (!declaration) {\n          throw this.astErrorOutput(`Unable to find declarator`, ast);\n        }\n\n        if (!declaration.valueType) {\n          throw this.astErrorOutput(`Unable to find declarator valueType`, ast);\n        }\n\n        return declaration.valueType;\n      case 'Identifier':\n        if (ast.name === 'Infinity') {\n          return 'Number';\n        }\n        if (this.isAstVariable(ast)) {\n          const signature = this.getVariableSignature(ast);\n          if (signature === 'value') {\n            return this.getCheckVariableType(ast);\n          }\n        }\n        const origin = this.findIdentifierOrigin(ast);\n        if (origin && origin.init) {\n          return this.getType(origin.init);\n        }\n        return null;\n      case 'ReturnStatement':\n        return this.getType(ast.argument);\n      case 'MemberExpression':\n        if (this.isAstMathFunction(ast)) {\n          switch (ast.property.name) {\n            case 'ceil':\n              return 'Integer';\n            case 'floor':\n              return 'Integer';\n            case 'round':\n              return 'Integer';\n          }\n          return 'Number';\n        }\n        if (this.isAstVariable(ast)) {\n          const variableSignature = this.getVariableSignature(ast);\n          switch (variableSignature) {\n            case 'value[]':\n              return this.getLookupType(this.getCheckVariableType(ast.object));\n            case 'value[][]':\n              return this.getLookupType(this.getCheckVariableType(ast.object.object));\n            case 'value[][][]':\n              return this.getLookupType(this.getCheckVariableType(ast.object.object.object));\n            case 'value[][][][]':\n              return this.getLookupType(this.getCheckVariableType(ast.object.object.object.object));\n            case 'value.thread.value':\n            case 'this.thread.value':\n              return 'Integer';\n            case 'this.output.value':\n              return this.dynamicOutput ? 'Integer' : 'LiteralInteger';\n            case 'this.constants.value':\n              return this.getConstantType(ast.property.name);\n            case 'this.constants.value[]':\n              return this.getLookupType(this.getConstantType(ast.object.property.name));\n            case 'this.constants.value[][]':\n              return this.getLookupType(this.getConstantType(ast.object.object.property.name));\n            case 'this.constants.value[][][]':\n              return this.getLookupType(this.getConstantType(ast.object.object.object.property.name));\n            case 'this.constants.value[][][][]':\n              return this.getLookupType(this.getConstantType(ast.object.object.object.object.property.name));\n            case 'fn()[]':\n            case 'fn()[][]':\n            case 'fn()[][][]':\n              return this.getLookupType(this.getType(ast.object));\n            case 'value.value':\n              if (this.isAstMathVariable(ast)) {\n                return 'Number';\n              }\n              switch (ast.property.name) {\n                case 'r':\n                case 'g':\n                case 'b':\n                case 'a':\n                  return this.getLookupType(this.getCheckVariableType(ast.object));\n              }\n            case '[][]':\n              return 'Number';\n          }\n          throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n        }\n        throw this.astErrorOutput('Unhandled getType MemberExpression', ast);\n      case 'ConditionalExpression':\n        return this.getType(ast.consequent);\n      case 'FunctionDeclaration':\n      case 'FunctionExpression':\n        const lastReturn = this.findLastReturn(ast.body);\n        if (lastReturn) {\n          return this.getType(lastReturn);\n        }\n        return null;\n      case 'IfStatement':\n        return this.getType(ast.consequent);\n      case 'SequenceExpression':\n        return this.getType(ast.expressions[ast.expressions.length - 1]);\n      default:\n        throw this.astErrorOutput(`Unhandled getType Type \"${ ast.type }\"`, ast);\n    }\n  }\n\n  getCheckVariableType(ast) {\n    const type = this.getVariableType(ast);\n    if (!type) {\n      throw this.astErrorOutput(`${ast.type} is not defined`, ast);\n    }\n    return type;\n  }\n\n  inferArgumentTypesIfNeeded(functionName, args) {\n    // ensure arguments are filled in, so when we lookup return type, we already can infer it\n    for (let i = 0; i < args.length; i++) {\n      if (!this.needsArgumentType(functionName, i)) continue;\n      const type = this.getType(args[i]);\n      if (!type) {\n        throw this.astErrorOutput(`Unable to infer argument ${i}`, args[i]);\n      }\n      this.assignArgumentType(functionName, i, type);\n    }\n  }\n\n  isAstMathVariable(ast) {\n    const mathProperties = [\n      'E',\n      'PI',\n      'SQRT2',\n      'SQRT1_2',\n      'LN2',\n      'LN10',\n      'LOG2E',\n      'LOG10E',\n    ];\n    return ast.type === 'MemberExpression' &&\n      ast.object && ast.object.type === 'Identifier' &&\n      ast.object.name === 'Math' &&\n      ast.property &&\n      ast.property.type === 'Identifier' &&\n      mathProperties.indexOf(ast.property.name) > -1;\n  }\n\n  isAstMathFunction(ast) {\n    const mathFunctions = [\n      'abs',\n      'acos',\n      'acosh',\n      'asin',\n      'asinh',\n      'atan',\n      'atan2',\n      'atanh',\n      'cbrt',\n      'ceil',\n      'clz32',\n      'cos',\n      'cosh',\n      'expm1',\n      'exp',\n      'floor',\n      'fround',\n      'imul',\n      'log',\n      'log2',\n      'log10',\n      'log1p',\n      'max',\n      'min',\n      'pow',\n      'random',\n      'round',\n      'sign',\n      'sin',\n      'sinh',\n      'sqrt',\n      'tan',\n      'tanh',\n      'trunc',\n    ];\n    return ast.type === 'CallExpression' &&\n      ast.callee &&\n      ast.callee.type === 'MemberExpression' &&\n      ast.callee.object &&\n      ast.callee.object.type === 'Identifier' &&\n      ast.callee.object.name === 'Math' &&\n      ast.callee.property &&\n      ast.callee.property.type === 'Identifier' &&\n      mathFunctions.indexOf(ast.callee.property.name) > -1;\n  }\n\n  isAstVariable(ast) {\n    return ast.type === 'Identifier' || ast.type === 'MemberExpression';\n  }\n\n  isSafe(ast) {\n    return this.isSafeDependencies(this.getDependencies(ast));\n  }\n\n  isSafeDependencies(dependencies) {\n    return dependencies && dependencies.every ? dependencies.every(dependency => dependency.isSafe) : true;\n  }\n\n  /**\n   *\n   * @param ast\n   * @param dependencies\n   * @param isNotSafe\n   * @return {Array}\n   */\n  getDependencies(ast, dependencies, isNotSafe) {\n    if (!dependencies) {\n      dependencies = [];\n    }\n    if (!ast) return null;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.getDependencies(ast[i], dependencies, isNotSafe);\n      }\n      return dependencies;\n    }\n    switch (ast.type) {\n      case 'AssignmentExpression':\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'ConditionalExpression':\n        this.getDependencies(ast.test, dependencies, isNotSafe);\n        this.getDependencies(ast.alternate, dependencies, isNotSafe);\n        this.getDependencies(ast.consequent, dependencies, isNotSafe);\n        return dependencies;\n      case 'Literal':\n        dependencies.push({\n          origin: 'literal',\n          value: ast.value,\n          isSafe: isNotSafe === true ? false : ast.value > -Infinity && ast.value < Infinity && !isNaN(ast.value)\n        });\n        break;\n      case 'VariableDeclarator':\n        return this.getDependencies(ast.init, dependencies, isNotSafe);\n      case 'Identifier':\n        const declaration = this.getDeclaration(ast);\n        if (declaration) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'declaration',\n            isSafe: isNotSafe ? false : this.isSafeDependencies(declaration.dependencies),\n          });\n        } else if (this.argumentNames.indexOf(ast.name) > -1) {\n          dependencies.push({\n            name: ast.name,\n            origin: 'argument',\n            isSafe: false,\n          });\n        } else if (this.strictTypingChecking) {\n          throw new Error(`Cannot find identifier origin \"${ast.name}\"`);\n        }\n        break;\n      case 'FunctionDeclaration':\n        return this.getDependencies(ast.body.body[ast.body.body.length - 1], dependencies, isNotSafe);\n      case 'ReturnStatement':\n        return this.getDependencies(ast.argument, dependencies);\n      case 'BinaryExpression':\n      case 'LogicalExpression':\n        isNotSafe = (ast.operator === '/' || ast.operator === '*');\n        this.getDependencies(ast.left, dependencies, isNotSafe);\n        this.getDependencies(ast.right, dependencies, isNotSafe);\n        return dependencies;\n      case 'UnaryExpression':\n      case 'UpdateExpression':\n        return this.getDependencies(ast.argument, dependencies, isNotSafe);\n      case 'VariableDeclaration':\n        return this.getDependencies(ast.declarations, dependencies, isNotSafe);\n      case 'ArrayExpression':\n        dependencies.push({\n          origin: 'declaration',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'CallExpression':\n        dependencies.push({\n          origin: 'function',\n          isSafe: true,\n        });\n        return dependencies;\n      case 'MemberExpression':\n        const details = this.getMemberExpressionDetails(ast);\n        switch (details.signature) {\n          case 'value[]':\n            this.getDependencies(ast.object, dependencies, isNotSafe);\n            break;\n          case 'value[][]':\n            this.getDependencies(ast.object.object, dependencies, isNotSafe);\n            break;\n          case 'value[][][]':\n            this.getDependencies(ast.object.object.object, dependencies, isNotSafe);\n            break;\n          case 'this.output.value':\n            if (this.dynamicOutput) {\n              dependencies.push({\n                name: details.name,\n                origin: 'output',\n                isSafe: false,\n              });\n            }\n            break;\n        }\n        if (details) {\n          if (details.property) {\n            this.getDependencies(details.property, dependencies, isNotSafe);\n          }\n          if (details.xProperty) {\n            this.getDependencies(details.xProperty, dependencies, isNotSafe);\n          }\n          if (details.yProperty) {\n            this.getDependencies(details.yProperty, dependencies, isNotSafe);\n          }\n          if (details.zProperty) {\n            this.getDependencies(details.zProperty, dependencies, isNotSafe);\n          }\n          return dependencies;\n        }\n      case 'SequenceExpression':\n        return this.getDependencies(ast.expressions, dependencies, isNotSafe);\n      default:\n        throw this.astErrorOutput(`Unhandled type ${ ast.type } in getDependencies`, ast);\n    }\n    return dependencies;\n  }\n\n  getVariableSignature(ast, returnRawValue) {\n    if (!this.isAstVariable(ast)) {\n      throw new Error(`ast of type \"${ ast.type }\" is not a variable signature`);\n    }\n    if (ast.type === 'Identifier') {\n      return 'value';\n    }\n    const signature = [];\n    while (true) {\n      if (!ast) break;\n      if (ast.computed) {\n        signature.push('[]');\n      } else if (ast.type === 'ThisExpression') {\n        signature.unshift('this');\n      } else if (ast.property && ast.property.name) {\n        if (\n          ast.property.name === 'x' ||\n          ast.property.name === 'y' ||\n          ast.property.name === 'z'\n        ) {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        } else if (\n          ast.property.name === 'constants' ||\n          ast.property.name === 'thread' ||\n          ast.property.name === 'output'\n        ) {\n          signature.unshift('.' + ast.property.name);\n        } else {\n          signature.unshift(returnRawValue ? '.' + ast.property.name : '.value');\n        }\n      } else if (ast.name) {\n        signature.unshift(returnRawValue ? ast.name : 'value');\n      } else if (ast.callee && ast.callee.name) {\n        signature.unshift(returnRawValue ? ast.callee.name + '()' : 'fn()');\n      } else if (ast.elements) {\n        signature.unshift('[]');\n      } else {\n        signature.unshift('unknown');\n      }\n      ast = ast.object;\n    }\n\n    const signatureString = signature.join('');\n    if (returnRawValue) {\n      return signatureString;\n    }\n\n    const allowedExpressions = [\n      'value',\n      'value[]',\n      'value[][]',\n      'value[][][]',\n      'value[][][][]',\n      'value.value',\n      'value.thread.value',\n      'this.thread.value',\n      'this.output.value',\n      'this.constants.value',\n      'this.constants.value[]',\n      'this.constants.value[][]',\n      'this.constants.value[][][]',\n      'this.constants.value[][][][]',\n      'fn()[]',\n      'fn()[][]',\n      'fn()[][][]',\n      '[][]',\n    ];\n    if (allowedExpressions.indexOf(signatureString) > -1) {\n      return signatureString;\n    }\n    return null;\n  }\n\n  build() {\n    return this.toString().length > 0;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for generically to its respective function\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed string array\n   */\n  astGeneric(ast, retArr) {\n    if (ast === null) {\n      throw this.astErrorOutput('NULL ast', ast);\n    } else {\n      if (Array.isArray(ast)) {\n        for (let i = 0; i < ast.length; i++) {\n          this.astGeneric(ast[i], retArr);\n        }\n        return retArr;\n      }\n\n      switch (ast.type) {\n        case 'FunctionDeclaration':\n          return this.astFunctionDeclaration(ast, retArr);\n        case 'FunctionExpression':\n          return this.astFunctionExpression(ast, retArr);\n        case 'ReturnStatement':\n          return this.astReturnStatement(ast, retArr);\n        case 'Literal':\n          return this.astLiteral(ast, retArr);\n        case 'BinaryExpression':\n          return this.astBinaryExpression(ast, retArr);\n        case 'Identifier':\n          return this.astIdentifierExpression(ast, retArr);\n        case 'AssignmentExpression':\n          return this.astAssignmentExpression(ast, retArr);\n        case 'ExpressionStatement':\n          return this.astExpressionStatement(ast, retArr);\n        case 'EmptyStatement':\n          return this.astEmptyStatement(ast, retArr);\n        case 'BlockStatement':\n          return this.astBlockStatement(ast, retArr);\n        case 'IfStatement':\n          return this.astIfStatement(ast, retArr);\n        case 'SwitchStatement':\n          return this.astSwitchStatement(ast, retArr);\n        case 'BreakStatement':\n          return this.astBreakStatement(ast, retArr);\n        case 'ContinueStatement':\n          return this.astContinueStatement(ast, retArr);\n        case 'ForStatement':\n          return this.astForStatement(ast, retArr);\n        case 'WhileStatement':\n          return this.astWhileStatement(ast, retArr);\n        case 'DoWhileStatement':\n          return this.astDoWhileStatement(ast, retArr);\n        case 'VariableDeclaration':\n          return this.astVariableDeclaration(ast, retArr);\n        case 'VariableDeclarator':\n          return this.astVariableDeclarator(ast, retArr);\n        case 'ThisExpression':\n          return this.astThisExpression(ast, retArr);\n        case 'SequenceExpression':\n          return this.astSequenceExpression(ast, retArr);\n        case 'UnaryExpression':\n          return this.astUnaryExpression(ast, retArr);\n        case 'UpdateExpression':\n          return this.astUpdateExpression(ast, retArr);\n        case 'LogicalExpression':\n          return this.astLogicalExpression(ast, retArr);\n        case 'MemberExpression':\n          return this.astMemberExpression(ast, retArr);\n        case 'CallExpression':\n          return this.astCallExpression(ast, retArr);\n        case 'ArrayExpression':\n          return this.astArrayExpression(ast, retArr);\n        case 'DebuggerStatement':\n          return this.astDebuggerStatement(ast, retArr);\n        case 'ConditionalExpression':\n          return this.astConditionalExpression(ast, retArr);\n      }\n\n      throw this.astErrorOutput('Unknown ast type : ' + ast.type, ast);\n    }\n  }\n  /**\n   * @desc To throw the AST error, with its location.\n   * @param {string} error - the error message output\n   * @param {Object} ast - the AST object where the error is\n   */\n  astErrorOutput(error, ast) {\n    if (typeof this.source !== 'string') {\n      return new Error(error);\n    }\n\n    const debugString = utils.getAstString(this.source, ast);\n    const leadingSource = this.source.substr(ast.start);\n    const splitLines = leadingSource.split(/\\n/);\n    const lineBefore = splitLines.length > 0 ? splitLines[splitLines.length - 1] : 0;\n    return new Error(`${error} on line ${ splitLines.length }, position ${ lineBefore.length }:\\n ${ debugString }`);\n  }\n\n  astDebuggerStatement(arrNode, retArr) {\n    return retArr;\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  /**\n   * @abstract\n   * @param {Object} ast\n   * @param {String[]} retArr\n   * @returns {String[]}\n   */\n  astFunction(ast, retArr) {\n    throw new Error(`\"astFunction\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for to its *named function declaration*\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astFunctionDeclaration(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  astFunctionExpression(ast, retArr) {\n    if (this.isChildFunction(ast)) {\n      return retArr;\n    }\n    return this.astFunction(ast, retArr);\n  }\n  isChildFunction(ast) {\n    for (let i = 0; i < this.functions.length; i++) {\n      if (this.functions[i] === ast) {\n        return true;\n      }\n    }\n    return false;\n  }\n  astReturnStatement(ast, retArr) {\n    return retArr;\n  }\n  astLiteral(ast, retArr) {\n    this.literalTypes[this.astKey(ast)] = 'Number';\n    return retArr;\n  }\n  astBinaryExpression(ast, retArr) {\n    return retArr;\n  }\n  astIdentifierExpression(ast, retArr) {\n    return retArr;\n  }\n  astAssignmentExpression(ast, retArr) {\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *generic expression* statement\n   * @param {Object} esNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astExpressionStatement(esNode, retArr) {\n    this.astGeneric(esNode.expression, retArr);\n    retArr.push(';');\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for an *Empty* Statement\n   * @param {Object} eNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astEmptyStatement(eNode, retArr) {\n    return retArr;\n  }\n  astBlockStatement(ast, retArr) {\n    return retArr;\n  }\n  astIfStatement(ast, retArr) {\n    return retArr;\n  }\n  astSwitchStatement(ast, retArr) {\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *Break* Statement\n   * @param {Object} brNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astBreakStatement(brNode, retArr) {\n    retArr.push('break;');\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *Continue* Statement\n   * @param {Object} crNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astContinueStatement(crNode, retArr) {\n    retArr.push('continue;\\n');\n    return retArr;\n  }\n  astForStatement(ast, retArr) {\n    return retArr;\n  }\n  astWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  astDoWhileStatement(ast, retArr) {\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *Variable Declarator*\n   * @param {Object} iVarDecNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astVariableDeclarator(iVarDecNode, retArr) {\n    this.astGeneric(iVarDecNode.id, retArr);\n    if (iVarDecNode.init !== null) {\n      retArr.push('=');\n      this.astGeneric(iVarDecNode.init, retArr);\n    }\n    return retArr;\n  }\n  astThisExpression(ast, retArr) {\n    return retArr;\n  }\n  astSequenceExpression(sNode, retArr) {\n    const { expressions } = sNode;\n    const sequenceResult = [];\n    for (let i = 0; i < expressions.length; i++) {\n      const expression = expressions[i];\n      const expressionResult = [];\n      this.astGeneric(expression, expressionResult);\n      sequenceResult.push(expressionResult.join(''));\n    }\n    if (sequenceResult.length > 1) {\n      retArr.push('(', sequenceResult.join(','), ')');\n    } else {\n      retArr.push(sequenceResult[0]);\n    }\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *Unary* Expression\n   * @param {Object} uNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astUnaryExpression(uNode, retArr) {\n    const unaryResult = this.checkAndUpconvertBitwiseUnary(uNode, retArr);\n    if (unaryResult) {\n      return retArr;\n    }\n\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(uNode, retArr) {}\n\n  /**\n   * @desc Parses the abstract syntax tree for *Update* Expression\n   * @param {Object} uNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astUpdateExpression(uNode, retArr) {\n    if (uNode.prefix) {\n      retArr.push(uNode.operator);\n      this.astGeneric(uNode.argument, retArr);\n    } else {\n      this.astGeneric(uNode.argument, retArr);\n      retArr.push(uNode.operator);\n    }\n\n    return retArr;\n  }\n  /**\n   * @desc Parses the abstract syntax tree for *Logical* Expression\n   * @param {Object} logNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astLogicalExpression(logNode, retArr) {\n    retArr.push('(');\n    this.astGeneric(logNode.left, retArr);\n    retArr.push(logNode.operator);\n    this.astGeneric(logNode.right, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n  astMemberExpression(ast, retArr) {\n    return retArr;\n  }\n  astCallExpression(ast, retArr) {\n    return retArr;\n  }\n  astArrayExpression(ast, retArr) {\n    return retArr;\n  }\n\n  /**\n   *\n   * @param ast\n   * @return {IFunctionNodeMemberExpressionDetails}\n   */\n  getMemberExpressionDetails(ast) {\n    if (ast.type !== 'MemberExpression') {\n      throw this.astErrorOutput(`Expression ${ ast.type } not a MemberExpression`, ast);\n    }\n    let name = null;\n    let type = null;\n    const variableSignature = this.getVariableSignature(ast);\n    switch (variableSignature) {\n      case 'value':\n        return null;\n      case 'value.thread.value':\n      case 'this.thread.value':\n      case 'this.output.value':\n        return {\n          signature: variableSignature,\n            type: 'Integer',\n            name: ast.property.name\n        };\n      case 'value[]':\n        if (typeof ast.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object),\n            xProperty: ast.property\n        };\n      case 'value[][]':\n        if (typeof ast.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object),\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][]':\n        if (typeof ast.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value[][][][]':\n        if (typeof ast.object.object.object.object.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.object.name;\n        return {\n          name,\n          origin: 'user',\n            signature: variableSignature,\n            type: this.getVariableType(ast.object.object.object.object),\n            zProperty: ast.object.object.property,\n            yProperty: ast.object.property,\n            xProperty: ast.property,\n        };\n      case 'value.value':\n        if (typeof ast.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        if (this.isAstMathVariable(ast)) {\n          name = ast.property.name;\n          return {\n            name,\n            origin: 'Math',\n            type: 'Number',\n            signature: variableSignature,\n          };\n        }\n        switch (ast.property.name) {\n          case 'r':\n          case 'g':\n          case 'b':\n          case 'a':\n            name = ast.object.name;\n            return {\n              name,\n              property: ast.property.name,\n                origin: 'user',\n                signature: variableSignature,\n                type: 'Number'\n            };\n          default:\n            throw this.astErrorOutput('Unexpected expression', ast);\n        }\n      case 'this.constants.value':\n        if (typeof ast.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.property.name;\n        type = this.getConstantType(name);\n        if (!type) {\n          throw this.astErrorOutput('Constant has no type', ast);\n        }\n        return {\n          name,\n          type,\n          origin: 'constants',\n            signature: variableSignature,\n        };\n      case 'this.constants.value[]':\n        if (typeof ast.object.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.property.name;\n        type = this.getConstantType(name);\n        if (!type) {\n          throw this.astErrorOutput('Constant has no type', ast);\n        }\n        return {\n          name,\n          type,\n          origin: 'constants',\n            signature: variableSignature,\n            xProperty: ast.property,\n        };\n      case 'this.constants.value[][]': {\n        if (typeof ast.object.object.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.property.name;\n        type = this.getConstantType(name);\n        if (!type) {\n          throw this.astErrorOutput('Constant has no type', ast);\n        }\n        return {\n          name,\n          type,\n          origin: 'constants',\n          signature: variableSignature,\n          yProperty: ast.object.property,\n          xProperty: ast.property,\n        };\n      }\n      case 'this.constants.value[][][]': {\n        if (typeof ast.object.object.object.property.name !== 'string') {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        name = ast.object.object.object.property.name;\n        type = this.getConstantType(name);\n        if (!type) {\n          throw this.astErrorOutput('Constant has no type', ast);\n        }\n        return {\n          name,\n          type,\n          origin: 'constants',\n          signature: variableSignature,\n          zProperty: ast.object.object.property,\n          yProperty: ast.object.property,\n          xProperty: ast.property,\n        };\n      }\n      case 'fn()[]':\n      case 'fn()[][]':\n      case '[][]':\n        return {\n          signature: variableSignature,\n            property: ast.property,\n        };\n      default:\n        throw this.astErrorOutput('Unexpected expression', ast);\n    }\n  }\n\n  findIdentifierOrigin(astToFind) {\n    const stack = [this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack[0];\n      if (atNode.type === 'VariableDeclarator' && atNode.id && atNode.id.name && atNode.id.name === astToFind.name) {\n        return atNode;\n      }\n      stack.shift();\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      }\n    }\n    return null;\n  }\n\n  findLastReturn(ast) {\n    const stack = [ast || this.ast];\n\n    while (stack.length > 0) {\n      const atNode = stack.pop();\n      if (atNode.type === 'ReturnStatement') {\n        return atNode;\n      }\n      if (atNode.type === 'FunctionDeclaration') {\n        continue;\n      }\n      if (atNode.argument) {\n        stack.push(atNode.argument);\n      } else if (atNode.body) {\n        stack.push(atNode.body);\n      } else if (atNode.declarations) {\n        stack.push(atNode.declarations);\n      } else if (Array.isArray(atNode)) {\n        for (let i = 0; i < atNode.length; i++) {\n          stack.push(atNode[i]);\n        }\n      } else if (atNode.consequent) {\n        stack.push(atNode.consequent);\n      } else if (atNode.cases) {\n        stack.push(atNode.cases);\n      }\n    }\n    return null;\n  }\n\n  getInternalVariableName(name) {\n    if (!this._internalVariableNames.hasOwnProperty(name)) {\n      this._internalVariableNames[name] = 0;\n    }\n    this._internalVariableNames[name]++;\n    if (this._internalVariableNames[name] === 1) {\n      return name;\n    }\n    return name + this._internalVariableNames[name];\n  }\n\n  astKey(ast, separator = ',') {\n    if (!ast.start || !ast.end) throw new Error('AST start and end needed');\n    return `${ast.start}${separator}${ast.end}`;\n  }\n}\n\nconst typeLookupMap = {\n  'Number': 'Number',\n  'Float': 'Float',\n  'Integer': 'Integer',\n  'Array': 'Number',\n  'Array(2)': 'Number',\n  'Array(3)': 'Number',\n  'Array(4)': 'Number',\n  'Matrix(2)': 'Number',\n  'Matrix(3)': 'Number',\n  'Matrix(4)': 'Number',\n  'Array2D': 'Number',\n  'Array3D': 'Number',\n  'Input': 'Number',\n  'HTMLCanvas': 'Array(4)',\n  'OffscreenCanvas': 'Array(4)',\n  'HTMLImage': 'Array(4)',\n  'ImageBitmap': 'Array(4)',\n  'ImageData': 'Array(4)',\n  'HTMLVideo': 'Array(4)',\n  'HTMLImageArray': 'Array(4)',\n  'NumberTexture': 'Number',\n  'MemoryOptimizedNumberTexture': 'Number',\n  'Array1D(2)': 'Array(2)',\n  'Array1D(3)': 'Array(3)',\n  'Array1D(4)': 'Array(4)',\n  'Array2D(2)': 'Array(2)',\n  'Array2D(3)': 'Array(3)',\n  'Array2D(4)': 'Array(4)',\n  'Array3D(2)': 'Array(2)',\n  'Array3D(3)': 'Array(3)',\n  'Array3D(4)': 'Array(4)',\n  'ArrayTexture(1)': 'Number',\n  'ArrayTexture(2)': 'Array(2)',\n  'ArrayTexture(3)': 'Array(3)',\n  'ArrayTexture(4)': 'Array(4)',\n};\n\nmodule.exports = {\n  FunctionNode\n};"
  },
  {
    "path": "src/backend/function-tracer.js",
    "content": "const { utils } = require('../utils');\n\nfunction last(array) {\n  return array.length > 0 ? array[array.length - 1] : null;\n}\n\nconst states = {\n  trackIdentifiers: 'trackIdentifiers',\n  memberExpression: 'memberExpression',\n  inForLoopInit: 'inForLoopInit'\n};\n\nclass FunctionTracer {\n  constructor(ast) {\n    this.runningContexts = [];\n    this.functionContexts = [];\n    this.contexts = [];\n    this.functionCalls = [];\n    /**\n     *\n     * @type {IDeclaration[]}\n     */\n    this.declarations = [];\n    this.identifiers = [];\n    this.functions = [];\n    this.returnStatements = [];\n    this.trackedIdentifiers = null;\n    this.states = [];\n    this.newFunctionContext();\n    this.scan(ast);\n  }\n\n  isState(state) {\n    return this.states[this.states.length - 1] === state;\n  }\n\n  hasState(state) {\n    return this.states.indexOf(state) > -1;\n  }\n\n  pushState(state) {\n    this.states.push(state);\n  }\n\n  popState(state) {\n    if (this.isState(state)) {\n      this.states.pop();\n    } else {\n      throw new Error(`Cannot pop the non-active state \"${state}\"`);\n    }\n  }\n\n  get currentFunctionContext() {\n    return last(this.functionContexts);\n  }\n\n  get currentContext() {\n    return last(this.runningContexts);\n  }\n\n  newFunctionContext() {\n    const newContext = { '@contextType': 'function' };\n    this.contexts.push(newContext);\n    this.functionContexts.push(newContext);\n  }\n\n  newContext(run) {\n    const newContext = Object.assign({ '@contextType': 'const/let' }, this.currentContext);\n    this.contexts.push(newContext);\n    this.runningContexts.push(newContext);\n    run();\n    const { currentFunctionContext } = this;\n    for (const p in currentFunctionContext) {\n      if (!currentFunctionContext.hasOwnProperty(p) || newContext.hasOwnProperty(p)) continue;\n      newContext[p] = currentFunctionContext[p];\n    }\n    this.runningContexts.pop();\n    return newContext;\n  }\n\n  useFunctionContext(run) {\n    const functionContext = last(this.functionContexts);\n    this.runningContexts.push(functionContext);\n    run();\n    this.runningContexts.pop();\n  }\n\n  getIdentifiers(run) {\n    const trackedIdentifiers = this.trackedIdentifiers = [];\n    this.pushState(states.trackIdentifiers);\n    run();\n    this.trackedIdentifiers = null;\n    this.popState(states.trackIdentifiers);\n    return trackedIdentifiers;\n  }\n\n  /**\n   * @param {string} name\n   * @returns {IDeclaration}\n   */\n  getDeclaration(name) {\n    const { currentContext, currentFunctionContext, runningContexts } = this;\n    const declaration = currentContext[name] || currentFunctionContext[name] || null;\n\n    if (\n      !declaration &&\n      currentContext === currentFunctionContext &&\n      runningContexts.length > 0\n    ) {\n      const previousRunningContext = runningContexts[runningContexts.length - 2];\n      if (previousRunningContext[name]) {\n        return previousRunningContext[name];\n      }\n    }\n\n    return declaration;\n  }\n\n  /**\n   * Recursively scans AST for declarations and functions, and add them to their respective context\n   * @param ast\n   */\n  scan(ast) {\n    if (!ast) return;\n    if (Array.isArray(ast)) {\n      for (let i = 0; i < ast.length; i++) {\n        this.scan(ast[i]);\n      }\n      return;\n    }\n    switch (ast.type) {\n      case 'Program':\n        this.useFunctionContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'BlockStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n        });\n        break;\n      case 'AssignmentExpression':\n      case 'LogicalExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'BinaryExpression':\n        this.scan(ast.left);\n        this.scan(ast.right);\n        break;\n      case 'UpdateExpression':\n        if (ast.operator === '++') {\n          const declaration = this.getDeclaration(ast.argument.name);\n          if (declaration) {\n            declaration.suggestedType = 'Integer';\n          }\n        }\n        this.scan(ast.argument);\n        break;\n      case 'UnaryExpression':\n        this.scan(ast.argument);\n        break;\n      case 'VariableDeclaration':\n        if (ast.kind === 'var') {\n          this.useFunctionContext(() => {\n            ast.declarations = utils.normalizeDeclarations(ast);\n            this.scan(ast.declarations);\n          });\n        } else {\n          ast.declarations = utils.normalizeDeclarations(ast);\n          this.scan(ast.declarations);\n        }\n        break;\n      case 'VariableDeclarator': {\n        const { currentContext } = this;\n        const inForLoopInit = this.hasState(states.inForLoopInit);\n        const declaration = {\n          ast: ast,\n          context: currentContext,\n          name: ast.id.name,\n          origin: 'declaration',\n          inForLoopInit,\n          inForLoopTest: null,\n          assignable: currentContext === this.currentFunctionContext || (!inForLoopInit && !currentContext.hasOwnProperty(ast.id.name)),\n          suggestedType: null,\n          valueType: null,\n          dependencies: null,\n          isSafe: null,\n        };\n        if (!currentContext[ast.id.name]) {\n          currentContext[ast.id.name] = declaration;\n        }\n        this.declarations.push(declaration);\n        this.scan(ast.id);\n        this.scan(ast.init);\n        break;\n      }\n      case 'FunctionExpression':\n      case 'FunctionDeclaration':\n        if (this.runningContexts.length === 0) {\n          this.scan(ast.body);\n        } else {\n          this.functions.push(ast);\n        }\n        break;\n      case 'IfStatement':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        if (ast.alternate) this.scan(ast.alternate);\n        break;\n      case 'ForStatement': {\n        let testIdentifiers;\n        const context = this.newContext(() => {\n          this.pushState(states.inForLoopInit);\n          this.scan(ast.init);\n          this.popState(states.inForLoopInit);\n\n          testIdentifiers = this.getIdentifiers(() => {\n            this.scan(ast.test);\n          });\n\n          this.scan(ast.update);\n          this.newContext(() => {\n            this.scan(ast.body);\n          });\n        });\n\n        if (testIdentifiers) {\n          for (const p in context) {\n            if (p === '@contextType') continue;\n            if (testIdentifiers.indexOf(p) > -1) {\n              context[p].inForLoopTest = true;\n            }\n          }\n        }\n        break;\n      }\n      case 'DoWhileStatement':\n      case 'WhileStatement':\n        this.newContext(() => {\n          this.scan(ast.body);\n          this.scan(ast.test);\n        });\n        break;\n      case 'Identifier': {\n        if (this.isState(states.trackIdentifiers)) {\n          this.trackedIdentifiers.push(ast.name);\n        }\n        this.identifiers.push({\n          context: this.currentContext,\n          declaration: this.getDeclaration(ast.name),\n          ast,\n        });\n        break;\n      }\n      case 'ReturnStatement':\n        this.returnStatements.push(ast);\n        this.scan(ast.argument);\n        break;\n      case 'MemberExpression':\n        this.pushState(states.memberExpression);\n        this.scan(ast.object);\n        this.scan(ast.property);\n        this.popState(states.memberExpression);\n        break;\n      case 'ExpressionStatement':\n        this.scan(ast.expression);\n        break;\n      case 'SequenceExpression':\n        this.scan(ast.expressions);\n        break;\n      case 'CallExpression':\n        this.functionCalls.push({\n          context: this.currentContext,\n          ast,\n        });\n        this.scan(ast.arguments);\n        break;\n      case 'ArrayExpression':\n        this.scan(ast.elements);\n        break;\n      case 'ConditionalExpression':\n        this.scan(ast.test);\n        this.scan(ast.alternate);\n        this.scan(ast.consequent);\n        break;\n      case 'SwitchStatement':\n        this.scan(ast.discriminant);\n        this.scan(ast.cases);\n        break;\n      case 'SwitchCase':\n        this.scan(ast.test);\n        this.scan(ast.consequent);\n        break;\n\n      case 'ThisExpression':\n      case 'Literal':\n      case 'DebuggerStatement':\n      case 'EmptyStatement':\n      case 'BreakStatement':\n      case 'ContinueStatement':\n        break;\n      default:\n        throw new Error(`unhandled type \"${ast.type}\"`);\n    }\n  }\n}\n\nmodule.exports = {\n  FunctionTracer,\n};"
  },
  {
    "path": "src/backend/gl/kernel-string.js",
    "content": "const { glWiretap } = require('gl-wiretap');\nconst { utils } = require('../../utils');\n\nfunction toStringWithoutUtils(fn) {\n  return fn.toString()\n    .replace('=>', '')\n    .replace(/^function /, '')\n    .replace(/utils[.]/g, '/*utils.*/');\n}\n\n/**\n *\n * @param {GLKernel} Kernel\n * @param {KernelVariable[]} args\n * @param {Kernel} originKernel\n * @param {string} [setupContextString]\n * @param {string} [destroyContextString]\n * @returns {string}\n */\nfunction glKernelString(Kernel, args, originKernel, setupContextString, destroyContextString) {\n  if (!originKernel.built) {\n    originKernel.build.apply(originKernel, args);\n  }\n  args = args ? Array.from(args).map(arg => {\n    switch (typeof arg) {\n      case 'boolean':\n        return new Boolean(arg);\n      case 'number':\n        return new Number(arg);\n      default:\n        return arg;\n    }\n  }) : null;\n  const uploadedValues = [];\n  const postResult = [];\n  const context = glWiretap(originKernel.context, {\n    useTrackablePrimitives: true,\n    onReadPixels: (targetName) => {\n      if (kernel.subKernels) {\n        if (!subKernelsResultVariableSetup) {\n          postResult.push(`    const result = { result: ${getRenderString(targetName, kernel)} };`);\n          subKernelsResultVariableSetup = true;\n        } else {\n          const property = kernel.subKernels[subKernelsResultIndex++].property;\n          postResult.push(`    result${isNaN(property) ? '.' + property : `[${property}]`} = ${getRenderString(targetName, kernel)};`);\n        }\n        if (subKernelsResultIndex === kernel.subKernels.length) {\n          postResult.push('    return result;');\n        }\n        return;\n      }\n      if (targetName) {\n        postResult.push(`    return ${getRenderString(targetName, kernel)};`);\n      } else {\n        postResult.push(`    return null;`);\n      }\n    },\n    onUnrecognizedArgumentLookup: (argument) => {\n      const argumentName = findKernelValue(argument, kernel.kernelArguments, [], context, uploadedValues);\n      if (argumentName) {\n        return argumentName;\n      }\n      const constantName = findKernelValue(argument, kernel.kernelConstants, constants ? Object.keys(constants).map(key => constants[key]) : [], context, uploadedValues);\n      if (constantName) {\n        return constantName;\n      }\n      return null;\n    }\n  });\n  let subKernelsResultVariableSetup = false;\n  let subKernelsResultIndex = 0;\n  const {\n    source,\n    canvas,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    kernelArguments,\n    kernelConstants,\n    tactic,\n  } = originKernel;\n  const kernel = new Kernel(source, {\n    canvas,\n    context,\n    checkContext: false,\n    output,\n    pipeline,\n    graphical,\n    loopMaxIterations,\n    constants,\n    optimizeFloatMemory,\n    precision,\n    fixIntegerDivisionAccuracy,\n    functions,\n    nativeFunctions,\n    subKernels,\n    immutable,\n    argumentTypes,\n    constantTypes,\n    tactic,\n  });\n  let result = [];\n  context.setIndent(2);\n  kernel.build.apply(kernel, args);\n  result.push(context.toString());\n  context.reset();\n\n  kernel.kernelArguments.forEach((kernelArgument, i) => {\n    switch (kernelArgument.type) {\n      // primitives\n      case 'Integer':\n      case 'Boolean':\n      case 'Number':\n      case 'Float':\n        // non-primitives\n      case 'Array':\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n      case 'HTMLCanvas':\n      case 'HTMLImage':\n      case 'HTMLVideo':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'HTMLImageArray':\n        for (let imageIndex = 0; imageIndex < args[i].length; imageIndex++) {\n          const arg = args[i];\n          context.insertVariable(`uploadValue_${kernelArgument.name}[${imageIndex}]`, arg[imageIndex]);\n        }\n        break;\n      case 'Input':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue);\n        break;\n      case 'MemoryOptimizedNumberTexture':\n      case 'NumberTexture':\n      case 'Array1D(2)':\n      case 'Array1D(3)':\n      case 'Array1D(4)':\n      case 'Array2D(2)':\n      case 'Array2D(3)':\n      case 'Array2D(4)':\n      case 'Array3D(2)':\n      case 'Array3D(3)':\n      case 'Array3D(4)':\n      case 'ArrayTexture(1)':\n      case 'ArrayTexture(2)':\n      case 'ArrayTexture(3)':\n      case 'ArrayTexture(4)':\n        context.insertVariable(`uploadValue_${kernelArgument.name}`, args[i].texture);\n        break;\n      default:\n        throw new Error(`unhandled kernelArgumentType insertion for glWiretap of type ${kernelArgument.type}`);\n    }\n  });\n  result.push('/** start of injected functions **/');\n  result.push(`function ${toStringWithoutUtils(utils.flattenTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten2dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten3dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.flatten4dArrayTo)}`);\n  result.push(`function ${toStringWithoutUtils(utils.isArray)}`);\n  if (kernel.renderOutput !== kernel.renderTexture && kernel.formatValues) {\n    result.push(\n      `  const renderOutput = function ${toStringWithoutUtils(kernel.formatValues)};`\n    );\n  }\n  result.push('/** end of injected functions **/');\n  result.push(`  const innerKernel = function (${kernel.kernelArguments.map(kernelArgument => kernelArgument.varName).join(', ')}) {`);\n  context.setIndent(4);\n  kernel.run.apply(kernel, args);\n  if (kernel.renderKernels) {\n    kernel.renderKernels();\n  } else if (kernel.renderOutput) {\n    kernel.renderOutput();\n  }\n  result.push('    /** start setup uploads for kernel values **/');\n  kernel.kernelArguments.forEach(kernelArgument => {\n    result.push('    ' + kernelArgument.getStringValueHandler().split('\\n').join('\\n    '));\n  });\n  result.push('    /** end setup uploads for kernel values **/');\n  result.push(context.toString());\n  if (kernel.renderOutput === kernel.renderTexture) {\n    context.reset();\n    const framebufferName = context.getContextVariableName(kernel.framebuffer);\n    if (kernel.renderKernels) {\n      const results = kernel.renderKernels();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n      result: {\n        texture: ${ textureName },\n        type: '${ results.result.type }',\n        toArray: ${ getToArrayString(results.result, textureName, framebufferName) }\n      },`);\n      const { subKernels, mappedTextures } = kernel;\n      for (let i = 0; i < subKernels.length; i++) {\n        const texture = mappedTextures[i];\n        const subKernel = subKernels[i];\n        const subKernelResult = results[subKernel.property];\n        const subKernelTextureName = context.getContextVariableName(texture.texture);\n        result.push(`\n      ${subKernel.property}: {\n        texture: ${ subKernelTextureName },\n        type: '${ subKernelResult.type }',\n        toArray: ${ getToArrayString(subKernelResult, subKernelTextureName, framebufferName) }\n      },`);\n      }\n      result.push(`    };`);\n    } else {\n      const rendered = kernel.renderOutput();\n      const textureName = context.getContextVariableName(kernel.texture.texture);\n      result.push(`    return {\n        texture: ${ textureName },\n        type: '${ rendered.type }',\n        toArray: ${ getToArrayString(rendered, textureName, framebufferName) }\n      };`);\n    }\n  }\n  result.push(`    ${destroyContextString ? '\\n' + destroyContextString + '    ': ''}`);\n  result.push(postResult.join('\\n'));\n  result.push('  };');\n  if (kernel.graphical) {\n    result.push(getGetPixelsString(kernel));\n    result.push(`  innerKernel.getPixels = getPixels;`);\n  }\n  result.push('  return innerKernel;');\n\n  let constantsUpload = [];\n  kernelConstants.forEach((kernelConstant) => {\n    constantsUpload.push(`${kernelConstant.getStringValueHandler()}`);\n  });\n  return `function kernel(settings) {\n  const { context, constants } = settings;\n  ${constantsUpload.join('')}\n  ${setupContextString ? setupContextString : ''}\n${result.join('\\n')}\n}`;\n}\n\nfunction getRenderString(targetName, kernel) {\n  const readBackValue = kernel.precision === 'single' ? targetName : `new Float32Array(${targetName}.buffer)`;\n  if (kernel.output[2]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]}, ${kernel.output[2]})`;\n  }\n  if (kernel.output[1]) {\n    return `renderOutput(${readBackValue}, ${kernel.output[0]}, ${kernel.output[1]})`;\n  }\n\n  return `renderOutput(${readBackValue}, ${kernel.output[0]})`;\n}\n\nfunction getGetPixelsString(kernel) {\n  const getPixels = kernel.getPixels.toString();\n  const useFunctionKeyword = !/^function/.test(getPixels);\n  return utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ getPixels }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      }\n      return null;\n    },\n    thisLookup: (property) => {\n      if (property === 'context') {\n        return null;\n      }\n      if (kernel.hasOwnProperty(property)) {\n        return JSON.stringify(kernel[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n}\n\nfunction getToArrayString(kernelResult, textureName, framebufferName) {\n  const toArray = kernelResult.toArray.toString();\n  const useFunctionKeyword = !/^function/.test(toArray);\n  const flattenedFunctions = utils.flattenFunctionToString(`${useFunctionKeyword ? 'function ' : ''}${ toArray }`, {\n    findDependency: (object, name) => {\n      if (object === 'utils') {\n        return `const ${name} = ${utils[name].toString()};`;\n      } else if (object === 'this') {\n        if (name === 'framebuffer') {\n          return '';\n        }\n        return `${useFunctionKeyword ? 'function ' : ''}${kernelResult[name].toString()}`;\n      } else {\n        throw new Error('unhandled fromObject');\n      }\n    },\n    thisLookup: (property, isDeclaration) => {\n      if (property === 'texture') {\n        return textureName;\n      }\n      if (property === 'context') {\n        if (isDeclaration) return null;\n        return 'gl';\n      }\n      if (kernelResult.hasOwnProperty(property)) {\n        return JSON.stringify(kernelResult[property]);\n      }\n      throw new Error(`unhandled thisLookup ${ property }`);\n    }\n  });\n  return `() => {\n  function framebuffer() { return ${framebufferName}; };\n  ${flattenedFunctions}\n  return toArray();\n  }`;\n}\n\n/**\n *\n * @param {KernelVariable} argument\n * @param {KernelValue[]} kernelValues\n * @param {KernelVariable[]} values\n * @param context\n * @param {KernelVariable[]} uploadedValues\n * @return {string|null}\n */\nfunction findKernelValue(argument, kernelValues, values, context, uploadedValues) {\n  if (argument === null) return null;\n  if (kernelValues === null) return null;\n  switch (typeof argument) {\n    case 'boolean':\n    case 'number':\n      return null;\n  }\n  if (\n    typeof HTMLImageElement !== 'undefined' &&\n    argument instanceof HTMLImageElement\n  ) {\n    for (let i = 0; i < kernelValues.length; i++) {\n      const kernelValue = kernelValues[i];\n      if (kernelValue.type !== 'HTMLImageArray' && kernelValue) continue;\n      if (kernelValue.uploadValue !== argument) continue;\n      // TODO: if we send two of the same image, the parser could get confused, and short circuit to the first, handle that here\n      const variableIndex = values[i].indexOf(argument);\n      if (variableIndex === -1) continue;\n      const variableName = `uploadValue_${kernelValue.name}[${variableIndex}]`;\n      context.insertVariable(variableName, argument);\n      return variableName;\n    }\n  }\n\n  for (let i = 0; i < kernelValues.length; i++) {\n    const kernelValue = kernelValues[i];\n    if (argument !== kernelValue.uploadValue) continue;\n    const variable = `uploadValue_${kernelValue.name}`;\n    context.insertVariable(variable, kernelValue);\n    return variable;\n  }\n  return null;\n}\n\nmodule.exports = {\n  glKernelString\n};"
  },
  {
    "path": "src/backend/gl/kernel.js",
    "content": "const { Kernel } = require('../kernel');\nconst { utils } = require('../../utils');\nconst { GLTextureArray2Float } = require('./texture/array-2-float');\nconst { GLTextureArray2Float2D } = require('./texture/array-2-float-2d');\nconst { GLTextureArray2Float3D } = require('./texture/array-2-float-3d');\nconst { GLTextureArray3Float } = require('./texture/array-3-float');\nconst { GLTextureArray3Float2D } = require('./texture/array-3-float-2d');\nconst { GLTextureArray3Float3D } = require('./texture/array-3-float-3d');\nconst { GLTextureArray4Float } = require('./texture/array-4-float');\nconst { GLTextureArray4Float2D } = require('./texture/array-4-float-2d');\nconst { GLTextureArray4Float3D } = require('./texture/array-4-float-3d');\nconst { GLTextureFloat } = require('./texture/float');\nconst { GLTextureFloat2D } = require('./texture/float-2d');\nconst { GLTextureFloat3D } = require('./texture/float-3d');\nconst { GLTextureMemoryOptimized } = require('./texture/memory-optimized');\nconst { GLTextureMemoryOptimized2D } = require('./texture/memory-optimized-2d');\nconst { GLTextureMemoryOptimized3D } = require('./texture/memory-optimized-3d');\nconst { GLTextureUnsigned } = require('./texture/unsigned');\nconst { GLTextureUnsigned2D } = require('./texture/unsigned-2d');\nconst { GLTextureUnsigned3D } = require('./texture/unsigned-3d');\nconst { GLTextureGraphical } = require('./texture/graphical');\n\n/**\n * @abstract\n * @extends Kernel\n */\nclass GLKernel extends Kernel {\n  static get mode() {\n    return 'gpu';\n  }\n\n  static getIsFloatRead() {\n    const kernelString = `function kernelFunction() {\n      return 1;\n    }`;\n    const kernel = new this(kernelString, {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [1],\n      precision: 'single',\n      returnType: 'Number',\n      tactic: 'speed',\n    });\n    kernel.build();\n    kernel.run();\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return result[0] === 1;\n  }\n\n  static getIsIntegerDivisionAccurate() {\n    function kernelFunction(v1, v2) {\n      return v1[this.thread.x] / v2[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [2],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [6, 6030401],\n      [3, 3991]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    // have we not got whole numbers for 6/3 or 6030401/3991\n    // add more here if others see this problem\n    return result[0] === 2 && result[1] === 1511;\n  }\n\n  static getIsSpeedTacticSupported() {\n    function kernelFunction(value) {\n      return value[this.thread.x];\n    }\n    const kernel = new this(kernelFunction.toString(), {\n      context: this.testContext,\n      canvas: this.testCanvas,\n      validate: false,\n      output: [4],\n      returnType: 'Number',\n      precision: 'unsigned',\n      tactic: 'speed',\n    });\n    const args = [\n      [0, 1, 2, 3]\n    ];\n    kernel.build.apply(kernel, args);\n    kernel.run.apply(kernel, args);\n    const result = kernel.renderOutput();\n    kernel.destroy(true);\n    return Math.round(result[0]) === 0 && Math.round(result[1]) === 1 && Math.round(result[2]) === 2 && Math.round(result[3]) === 3;\n  }\n\n  /**\n   * @abstract\n   */\n  static get testCanvas() {\n    throw new Error(`\"testCanvas\" not defined on ${ this.name }`);\n  }\n\n  /**\n   * @abstract\n   */\n  static get testContext() {\n    throw new Error(`\"testContext\" not defined on ${ this.name }`);\n  }\n\n  static getFeatures() {\n    const gl = this.testContext;\n    const isDrawBuffers = this.getIsDrawBuffers();\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      isTextureFloat: this.getIsTextureFloat(),\n      isDrawBuffers,\n      kernelMap: isDrawBuffers,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  /**\n   * @abstract\n   */\n  static setupFeatureChecks() {\n    throw new Error(`\"setupFeatureChecks\" not defined on ${ this.name }`);\n  }\n\n  static getSignature(kernel, argumentTypes) {\n    return kernel.getVariablePrecisionString() + (argumentTypes.length > 0 ? ':' + argumentTypes.join(',') : '');\n  }\n\n  /**\n   * @desc Fix division by factor of 3 FP accuracy bug\n   * @param {Boolean} fix - should fix\n   */\n  setFixIntegerDivisionAccuracy(fix) {\n    this.fixIntegerDivisionAccuracy = fix;\n    return this;\n  }\n\n  /**\n   * @desc Toggle output mode\n   * @param {String} flag - 'single' or 'unsigned'\n   */\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  /**\n   * @desc Toggle texture output mode\n   * @param {Boolean} flag - true to enable floatTextures\n   * @deprecated\n   */\n  setFloatTextures(flag) {\n    utils.warnDeprecated('method', 'setFloatTextures', 'setOptimizeFloatMemory');\n    this.floatTextures = flag;\n    return this;\n  }\n\n  /**\n   * A highly readable very forgiving micro-parser for a glsl function that gets argument types\n   * @param {String} source\n   * @returns {{argumentTypes: String[], argumentNames: String[]}}\n   */\n  static nativeFunctionArguments(source) {\n    const argumentTypes = [];\n    const argumentNames = [];\n    const states = [];\n    const isStartingVariableName = /^[a-zA-Z_]/;\n    const isVariableChar = /[a-zA-Z_0-9]/;\n    let i = 0;\n    let argumentName = null;\n    let argumentType = null;\n    while (i < source.length) {\n      const char = source[i];\n      const nextChar = source[i + 1];\n      const state = states.length > 0 ? states[states.length - 1] : null;\n\n      // begin MULTI_LINE_COMMENT handling\n      if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '*') {\n        states.push('MULTI_LINE_COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'MULTI_LINE_COMMENT' && char === '*' && nextChar === '/') {\n        states.pop();\n        i += 2;\n        continue;\n      }\n      // end MULTI_LINE_COMMENT handling\n\n      // begin COMMENT handling\n      else if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '/') {\n        states.push('COMMENT');\n        i += 2;\n        continue;\n      } else if (state === 'COMMENT' && char === '\\n') {\n        states.pop();\n        i++;\n        continue;\n      }\n      // end COMMENT handling\n\n      // being FUNCTION_ARGUMENTS handling\n      else if (state === null && char === '(') {\n        states.push('FUNCTION_ARGUMENTS');\n        i++;\n        continue;\n      } else if (state === 'FUNCTION_ARGUMENTS') {\n        if (char === ')') {\n          states.pop();\n          break;\n        }\n        if (char === 'f' && nextChar === 'l' && source[i + 2] === 'o' && source[i + 3] === 'a' && source[i + 4] === 't' && source[i + 5] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'float';\n          argumentName = '';\n          i += 6;\n          continue;\n        } else if (char === 'i' && nextChar === 'n' && source[i + 2] === 't' && source[i + 3] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'int';\n          argumentName = '';\n          i += 4;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '2' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec2';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '3' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec3';\n          argumentName = '';\n          i += 5;\n          continue;\n        } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '4' && source[i + 4] === ' ') {\n          states.push('DECLARE_VARIABLE');\n          argumentType = 'vec4';\n          argumentName = '';\n          i += 5;\n          continue;\n        }\n      }\n      // end FUNCTION_ARGUMENTS handling\n\n      // begin DECLARE_VARIABLE handling\n      else if (state === 'DECLARE_VARIABLE') {\n        if (argumentName === '') {\n          if (char === ' ') {\n            i++;\n            continue;\n          }\n          if (!isStartingVariableName.test(char)) {\n            throw new Error('variable name is not expected string');\n          }\n        }\n        argumentName += char;\n        if (!isVariableChar.test(nextChar)) {\n          states.pop();\n          argumentNames.push(argumentName);\n          argumentTypes.push(typeMap[argumentType]);\n        }\n      }\n      // end DECLARE_VARIABLE handling\n\n      // Progress to next character\n      i++;\n    }\n    if (states.length > 0) {\n      throw new Error('GLSL function was not parsable');\n    }\n    return {\n      argumentNames,\n      argumentTypes,\n    };\n  }\n\n  static nativeFunctionReturnType(source) {\n    return typeMap[source.match(/int|float|vec[2-4]/)[0]];\n  }\n\n  static combineKernels(combinedKernel, lastKernel) {\n    combinedKernel.apply(null, arguments);\n    const {\n      texSize,\n      context,\n      threadDim\n    } = lastKernel.texSize;\n    let result;\n    if (lastKernel.precision === 'single') {\n      const w = texSize[0];\n      const h = Math.ceil(texSize[1] / 4);\n      result = new Float32Array(w * h * 4 * 4);\n      context.readPixels(0, 0, w, h * 4, context.RGBA, context.FLOAT, result);\n    } else {\n      const bytes = new Uint8Array(texSize[0] * texSize[1] * 4);\n      context.readPixels(0, 0, texSize[0], texSize[1], context.RGBA, context.UNSIGNED_BYTE, bytes);\n      result = new Float32Array(bytes.buffer);\n    }\n\n    result = result.subarray(0, threadDim[0] * threadDim[1] * threadDim[2]);\n\n    if (lastKernel.output.length === 1) {\n      return result;\n    } else if (lastKernel.output.length === 2) {\n      return utils.splitArray(result, lastKernel.output[0]);\n    } else if (lastKernel.output.length === 3) {\n      const cube = utils.splitArray(result, lastKernel.output[0] * lastKernel.output[1]);\n      return cube.map(function(x) {\n        return utils.splitArray(x, lastKernel.output[0]);\n      });\n    }\n  }\n\n  constructor(source, settings) {\n    super(source, settings);\n    this.transferValues = null;\n    this.formatValues = null;\n    /**\n     *\n     * @type {Texture}\n     */\n    this.TextureConstructor = null;\n    this.renderOutput = null;\n    this.renderRawOutput = null;\n    this.texSize = null;\n    this.translatedSource = null;\n    this.compiledFragmentShader = null;\n    this.compiledVertexShader = null;\n    this.switchingKernels = null;\n    this._textureSwitched = null;\n    this._mappedTextureSwitched = null;\n  }\n\n  checkTextureSize() {\n    const { features } = this.constructor;\n    if (this.texSize[0] > features.maxTextureSize || this.texSize[1] > features.maxTextureSize) {\n      throw new Error(`Texture size [${this.texSize[0]},${this.texSize[1]}] generated by kernel is larger than supported size [${features.maxTextureSize},${features.maxTextureSize}]`);\n    }\n  }\n\n  translateSource() {\n    throw new Error(`\"translateSource\" not defined on ${this.constructor.name}`);\n  }\n\n  /**\n   * Picks a render strategy for the now finally parsed kernel\n   * @param args\n   * @return {null|KernelOutput}\n   */\n  pickRenderStrategy(args) {\n    if (this.graphical) {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = (pixels) => pixels;\n      this.TextureConstructor = GLTextureGraphical;\n      return null;\n    }\n    if (this.precision === 'unsigned') {\n      this.renderRawOutput = this.readPackedPixelsToUint8Array;\n      this.transferValues = this.readPackedPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              return null;\n            }\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            return this.requestFallback(args);\n        }\n      } else {\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToArrays;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer':\n            this.renderOutput = this.renderValues;\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureUnsigned3D;\n              this.formatValues = utils.erect3DPackedFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureUnsigned2D;\n              this.formatValues = utils.erect2DPackedFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureUnsigned;\n              this.formatValues = utils.erectPackedFloat;\n              return null;\n            }\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            return this.requestFallback(args);\n        }\n      }\n    } else if (this.precision === 'single') {\n      this.renderRawOutput = this.readFloatPixelsToFloat32Array;\n      this.transferValues = this.readFloatPixelsToFloat32Array;\n      if (this.pipeline) {\n        this.renderOutput = this.renderTexture;\n        if (this.subKernels !== null) {\n          this.renderKernels = this.renderKernelsToTextures;\n        }\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.optimizeFloatMemory) {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureMemoryOptimized2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureMemoryOptimized;\n                return null;\n              }\n            } else {\n              if (this.output[2] > 0) {\n                this.TextureConstructor = GLTextureFloat3D;\n                return null;\n              } else if (this.output[1] > 0) {\n                this.TextureConstructor = GLTextureFloat2D;\n                return null;\n              } else {\n                this.TextureConstructor = GLTextureFloat;\n                return null;\n              }\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              return null;\n            }\n          }\n        }\n      }\n      this.renderOutput = this.renderValues;\n      if (this.subKernels !== null) {\n        this.renderKernels = this.renderKernelsToArrays;\n      }\n      if (this.optimizeFloatMemory) {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized3D;\n              this.formatValues = utils.erectMemoryOptimized3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureMemoryOptimized2D;\n              this.formatValues = utils.erectMemoryOptimized2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureMemoryOptimized;\n              this.formatValues = utils.erectMemoryOptimizedFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      } else {\n        switch (this.returnType) {\n          case 'LiteralInteger':\n          case 'Float':\n          case 'Number':\n          case 'Integer': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureFloat3D;\n              this.formatValues = utils.erect3DFloat;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureFloat2D;\n              this.formatValues = utils.erect2DFloat;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureFloat;\n              this.formatValues = utils.erectFloat;\n              return null;\n            }\n          }\n          case 'Array(2)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray2Float3D;\n              this.formatValues = utils.erect3DArray2;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray2Float2D;\n              this.formatValues = utils.erect2DArray2;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray2Float;\n              this.formatValues = utils.erectArray2;\n              return null;\n            }\n          }\n          case 'Array(3)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray3Float3D;\n              this.formatValues = utils.erect3DArray3;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray3Float2D;\n              this.formatValues = utils.erect2DArray3;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray3Float;\n              this.formatValues = utils.erectArray3;\n              return null;\n            }\n          }\n          case 'Array(4)': {\n            if (this.output[2] > 0) {\n              this.TextureConstructor = GLTextureArray4Float3D;\n              this.formatValues = utils.erect3DArray4;\n              return null;\n            } else if (this.output[1] > 0) {\n              this.TextureConstructor = GLTextureArray4Float2D;\n              this.formatValues = utils.erect2DArray4;\n              return null;\n            } else {\n              this.TextureConstructor = GLTextureArray4Float;\n              this.formatValues = utils.erectArray4;\n              return null;\n            }\n          }\n        }\n      }\n    } else {\n      throw new Error(`unhandled precision of \"${this.precision}\"`);\n    }\n\n    throw new Error(`unhandled return type \"${this.returnType}\"`);\n  }\n\n  /**\n   * @abstract\n   * @returns String\n   */\n  getKernelString() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultTexture() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Integer':\n      case 'Number':\n        return this.getMainResultNumberTexture();\n      case 'Array(2)':\n        return this.getMainResultArray2Texture();\n      case 'Array(3)':\n        return this.getMainResultArray3Texture();\n      case 'Array(4)':\n        return this.getMainResultArray4Texture();\n      default:\n        throw new Error(`unhandled returnType type ${ this.returnType }`);\n    }\n  }\n\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultSubKernelNumberTexture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultSubKernelArray2Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultSubKernelArray3Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultSubKernelArray4Texture() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultGraphical() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultMemoryOptimizedFloats() {\n    throw new Error(`abstract method call`);\n  }\n  /**\n   * @abstract\n   * @returns String[]\n   */\n  getMainResultPackedPixels() {\n    throw new Error(`abstract method call`);\n  }\n\n  getMainResultString() {\n    if (this.graphical) {\n      return this.getMainResultGraphical();\n    } else if (this.precision === 'single') {\n      if (this.optimizeFloatMemory) {\n        return this.getMainResultMemoryOptimizedFloats();\n      }\n      return this.getMainResultTexture();\n    } else {\n      return this.getMainResultPackedPixels();\n    }\n  }\n\n  getMainResultNumberTexture() {\n    return utils.linesToString(this.getMainResultKernelNumberTexture()) +\n      utils.linesToString(this.getMainResultSubKernelNumberTexture());\n  }\n\n  getMainResultArray2Texture() {\n    return utils.linesToString(this.getMainResultKernelArray2Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray2Texture());\n  }\n\n  getMainResultArray3Texture() {\n    return utils.linesToString(this.getMainResultKernelArray3Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray3Texture());\n  }\n\n  getMainResultArray4Texture() {\n    return utils.linesToString(this.getMainResultKernelArray4Texture()) +\n      utils.linesToString(this.getMainResultSubKernelArray4Texture());\n  }\n\n  /**\n   *\n   * @return {string}\n   */\n  getFloatTacticDeclaration() {\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    return `precision ${variablePrecision} float;\\n`;\n  }\n\n  /**\n   *\n   * @return {string}\n   */\n  getIntTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic, true)} int;\\n`;\n  }\n\n  /**\n   *\n   * @return {string}\n   */\n  getSampler2DTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2D;\\n`;\n  }\n\n  getSampler2DArrayTacticDeclaration() {\n    return `precision ${this.getVariablePrecisionString(this.texSize, this.tactic)} sampler2DArray;\\n`;\n  }\n\n  renderTexture() {\n    return this.immutable ? this.texture.clone() : this.texture;\n  }\n  readPackedPixelsToUint8Array() {\n    if (this.precision !== 'unsigned') throw new Error('Requires this.precision to be \"unsigned\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const result = new Uint8Array(texSize[0] * texSize[1] * 4);\n    gl.readPixels(0, 0, texSize[0], texSize[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n\n  readPackedPixelsToFloat32Array() {\n    return new Float32Array(this.readPackedPixelsToUint8Array().buffer);\n  }\n\n  readFloatPixelsToFloat32Array() {\n    if (this.precision !== 'single') throw new Error('Requires this.precision to be \"single\"');\n    const {\n      texSize,\n      context: gl\n    } = this;\n    const w = texSize[0];\n    const h = texSize[1];\n    const result = new Float32Array(w * h * 4);\n    gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n\n  /**\n   *\n   * @param {Boolean} [flip]\n   * @return {Uint8ClampedArray}\n   */\n  getPixels(flip) {\n    const {\n      context: gl,\n      output\n    } = this;\n    const [width, height] = output;\n    const pixels = new Uint8Array(width * height * 4);\n    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n    // flipped by default, so invert\n    return new Uint8ClampedArray((flip ? pixels : utils.flipPixels(pixels, width, height)).buffer);\n  }\n\n  renderKernelsToArrays() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    for (let i = 0; i < this.subKernels.length; i++) {\n      result[this.subKernels[i].property] = this.mappedTextures[i].toArray();\n    }\n    return result;\n  }\n\n  renderKernelsToTextures() {\n    const result = {\n      result: this.renderOutput(),\n    };\n    if (this.immutable) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i].clone();\n      }\n    } else {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        result[this.subKernels[i].property] = this.mappedTextures[i];\n      }\n    }\n    return result;\n  }\n\n  resetSwitchingKernels() {\n    const existingValue = this.switchingKernels;\n    this.switchingKernels = null;\n    return existingValue;\n  }\n\n  setOutput(output) {\n    const newOutput = this.toKernelOutput(output);\n    if (this.program) {\n      if (!this.dynamicOutput) {\n        throw new Error('Resizing a kernel with dynamicOutput: false is not possible');\n      }\n      const newThreadDim = [newOutput[0], newOutput[1] || 1, newOutput[2] || 1];\n      const newTexSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, newThreadDim);\n      const oldTexSize = this.texSize;\n      if (oldTexSize) {\n        const oldPrecision = this.getVariablePrecisionString(oldTexSize, this.tactic);\n        const newPrecision = this.getVariablePrecisionString(newTexSize, this.tactic);\n        if (oldPrecision !== newPrecision) {\n          if (this.debug) {\n            console.warn('Precision requirement changed, asking GPU instance to recompile');\n          }\n          this.switchKernels({\n            type: 'outputPrecisionMismatch',\n            precision: newPrecision,\n            needed: output\n          });\n          return;\n        }\n      }\n      this.output = newOutput;\n      this.threadDim = newThreadDim;\n      this.texSize = newTexSize;\n      const { context: gl } = this;\n      gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n      this.updateMaxTexSize();\n      this.framebuffer.width = this.texSize[0];\n      this.framebuffer.height = this.texSize[1];\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      this.canvas.width = this.maxTexSize[0];\n      this.canvas.height = this.maxTexSize[1];\n      if (this.texture) {\n        this.texture.delete();\n      }\n      this.texture = null;\n      this._setupOutputTexture();\n      if (this.mappedTextures && this.mappedTextures.length > 0) {\n        for (let i = 0; i < this.mappedTextures.length; i++) {\n          this.mappedTextures[i].delete();\n        }\n        this.mappedTextures = null;\n        this._setupSubOutputTextures();\n      }\n    } else {\n      this.output = newOutput;\n    }\n    return this;\n  }\n  renderValues() {\n    return this.formatValues(\n      this.transferValues(),\n      this.output[0],\n      this.output[1],\n      this.output[2]\n    );\n  }\n  switchKernels(reason) {\n    if (this.switchingKernels) {\n      this.switchingKernels.push(reason);\n    } else {\n      this.switchingKernels = [reason];\n    }\n  }\n  getVariablePrecisionString(textureSize = this.texSize, tactic = this.tactic, isInt = false) {\n    if (!tactic) {\n      if (!this.constructor.features.isSpeedTacticSupported) return 'highp';\n      const low = this.constructor.features[isInt ? 'lowIntPrecision' : 'lowFloatPrecision'];\n      const medium = this.constructor.features[isInt ? 'mediumIntPrecision' : 'mediumFloatPrecision'];\n      const high = this.constructor.features[isInt ? 'highIntPrecision' : 'highFloatPrecision'];\n      const requiredSize = Math.log2(textureSize[0] * textureSize[1]);\n      if (requiredSize <= low.rangeMax) {\n        return 'lowp';\n      } else if (requiredSize <= medium.rangeMax) {\n        return 'mediump';\n      } else if (requiredSize <= high.rangeMax) {\n        return 'highp';\n      } else {\n        throw new Error(`The required size exceeds that of the ability of your system`);\n      }\n    }\n    switch (tactic) {\n      case 'speed':\n        return 'lowp';\n      case 'balanced':\n        return 'mediump';\n      case 'precision':\n        return 'highp';\n      default:\n        throw new Error(`Unknown tactic \"${tactic}\" use \"speed\", \"balanced\", \"precision\", or empty for auto`);\n    }\n  }\n\n  /**\n   *\n   * @param {WebGLKernelValue} kernelValue\n   * @param {GLTexture} arg\n   */\n  updateTextureArgumentRefs(kernelValue, arg) {\n    if (!this.immutable) return;\n    if (this.texture.texture === arg.texture) {\n      const { prevArg } = kernelValue;\n      if (prevArg) {\n        if (prevArg.texture._refs === 1) {\n          this.texture.delete();\n          this.texture = prevArg.clone();\n          this._textureSwitched = true;\n        }\n        prevArg.delete();\n      }\n      kernelValue.prevArg = arg.clone();\n    } else if (this.mappedTextures && this.mappedTextures.length > 0) {\n      const { mappedTextures } = this;\n      for (let i = 0; i < mappedTextures.length; i++) {\n        const mappedTexture = mappedTextures[i];\n        if (mappedTexture.texture === arg.texture) {\n          const { prevArg } = kernelValue;\n          if (prevArg) {\n            if (prevArg.texture._refs === 1) {\n              mappedTexture.delete();\n              mappedTextures[i] = prevArg.clone();\n              this._mappedTextureSwitched[i] = true;\n            }\n            prevArg.delete();\n          }\n          kernelValue.prevArg = arg.clone();\n          return;\n        }\n      }\n    }\n  }\n\n  onActivate(previousKernel) {\n    this._textureSwitched = true;\n    this.texture = previousKernel.texture;\n    if (this.mappedTextures) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        this._mappedTextureSwitched[i] = true;\n      }\n      this.mappedTextures = previousKernel.mappedTextures;\n    }\n  }\n\n  initCanvas() {}\n}\n\nconst typeMap = {\n  int: 'Integer',\n  float: 'Number',\n  vec2: 'Array(2)',\n  vec3: 'Array(3)',\n  vec4: 'Array(4)',\n};\n\nmodule.exports = {\n  GLKernel\n};"
  },
  {
    "path": "src/backend/gl/texture/array-2-float-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect2DArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float2D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-2-float-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erect3DArray2(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float3D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-2-float.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray2Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(2)';\n  }\n  toArray() {\n    return utils.erectArray2(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray2Float\n};"
  },
  {
    "path": "src/backend/gl/texture/array-3-float-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect2DArray3(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float2D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-3-float-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erect3DArray3(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float3D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-3-float.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray3Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(3)';\n  }\n  toArray() {\n    return utils.erectArray3(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray3Float\n};"
  },
  {
    "path": "src/backend/gl/texture/array-4-float-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect2DArray4(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float2D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-4-float-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erect3DArray4(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float3D\n};"
  },
  {
    "path": "src/backend/gl/texture/array-4-float.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureArray4Float extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return utils.erectArray4(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureArray4Float\n};"
  },
  {
    "path": "src/backend/gl/texture/float-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat2D\n};"
  },
  {
    "path": "src/backend/gl/texture/float-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureFloat3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  toArray() {\n    return utils.erect3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat3D\n};"
  },
  {
    "path": "src/backend/gl/texture/float.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureFloat extends GLTexture {\n  get textureType() {\n    return this.context.FLOAT;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(1)';\n  }\n  renderRawOutput() {\n    const gl = this.context;\n    const size = this.size;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Float32Array(size[0] * size[1] * 4);\n    gl.readPixels(0, 0, size[0], size[1], gl.RGBA, gl.FLOAT, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return this.renderRawOutput();\n  }\n  toArray() {\n    return utils.erectFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureFloat\n};"
  },
  {
    "path": "src/backend/gl/texture/graphical.js",
    "content": "const { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureGraphical extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'ArrayTexture(4)';\n  }\n  toArray() {\n    return this.renderValues();\n  }\n}\n\nmodule.exports = {\n  GLTextureGraphical\n};"
  },
  {
    "path": "src/backend/gl/texture/index.js",
    "content": "const { Texture } = require('../../../texture');\n\n/**\n * @class\n * @property framebuffer\n * @extends Texture\n */\nclass GLTexture extends Texture {\n  /**\n   * @returns {Number}\n   * @abstract\n   */\n  get textureType() {\n    throw new Error(`\"textureType\" not implemented on ${ this.name }`);\n  }\n\n  clone() {\n    return new this.constructor(this);\n  }\n\n  /**\n   * @returns {Boolean}\n   */\n  beforeMutate() {\n    if (this.texture._refs > 1) {\n      this.newTexture();\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * @private\n   */\n  cloneTexture() {\n    this.texture._refs--;\n    const { context: gl, size, texture, kernel } = this;\n    if (kernel.debug) {\n      console.warn('cloning internal texture');\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, size[0], size[1]);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  /**\n   * @private\n   */\n  newTexture() {\n    this.texture._refs--;\n    const gl = this.context;\n    const size = this.size;\n    const kernel = this.kernel;\n    if (kernel.debug) {\n      console.warn('new internal texture');\n    }\n    const target = gl.createTexture();\n    selectTexture(gl, target);\n    gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    target._refs = 1;\n    this.texture = target;\n  }\n\n  clear() {\n    if (this.texture._refs) {\n      this.texture._refs--;\n      const gl = this.context;\n      const target = this.texture = gl.createTexture();\n      selectTexture(gl, target);\n      const size = this.size;\n      target._refs = 1;\n      gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, size[0], size[1], 0, this.textureFormat, this.textureType, null);\n    }\n    const { context: gl, texture } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    selectTexture(gl, texture);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    gl.clearColor(0, 0, 0, 0);\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n  }\n\n  delete() {\n    if (this._deleted) return;\n    this._deleted = true;\n    if (this.texture._refs) {\n      this.texture._refs--;\n      if (this.texture._refs) return;\n    }\n    this.context.deleteTexture(this.texture);\n    // TODO: Remove me\n    // if (this.texture._refs === 0 && this._framebuffer) {\n    //   this.context.deleteFramebuffer(this._framebuffer);\n    //   this._framebuffer = null;\n    // }\n  }\n\n  framebuffer() {\n    if (!this._framebuffer) {\n      this._framebuffer = this.kernel.getRawValueFramebuffer(this.size[0], this.size[1]);\n    }\n    return this._framebuffer;\n  }\n}\n\nfunction selectTexture(gl, texture) {\n  /* Maximum a texture can be, so that collision is highly unlikely\n   * basically gl.TEXTURE15 + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);\n   * Was gl.TEXTURE31, but safari didn't like it\n   * */\n  gl.activeTexture(gl.TEXTURE15);\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n}\n\nmodule.exports = { GLTexture };"
  },
  {
    "path": "src/backend/gl/texture/memory-optimized-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized2D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized2DFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized2D\n};"
  },
  {
    "path": "src/backend/gl/texture/memory-optimized-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized3D extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimized3DFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized3D\n};"
  },
  {
    "path": "src/backend/gl/texture/memory-optimized.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureFloat } = require('./float');\n\nclass GLTextureMemoryOptimized extends GLTextureFloat {\n  constructor(settings) {\n    super(settings);\n    this.type = 'MemoryOptimizedNumberTexture';\n  }\n  toArray() {\n    return utils.erectMemoryOptimizedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureMemoryOptimized\n};"
  },
  {
    "path": "src/backend/gl/texture/unsigned-2d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned2D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect2DPackedFloat(this.renderValues(), this.output[0], this.output[1]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned2D\n};"
  },
  {
    "path": "src/backend/gl/texture/unsigned-3d.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTextureUnsigned } = require('./unsigned');\n\nclass GLTextureUnsigned3D extends GLTextureUnsigned {\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  toArray() {\n    return utils.erect3DPackedFloat(this.renderValues(), this.output[0], this.output[1], this.output[2]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned3D\n};"
  },
  {
    "path": "src/backend/gl/texture/unsigned.js",
    "content": "const { utils } = require('../../../utils');\nconst { GLTexture } = require('./index');\n\nclass GLTextureUnsigned extends GLTexture {\n  get textureType() {\n    return this.context.UNSIGNED_BYTE;\n  }\n  constructor(settings) {\n    super(settings);\n    this.type = 'NumberTexture';\n  }\n  renderRawOutput() {\n    const { context: gl } = this;\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer());\n    gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      this.texture,\n      0\n    );\n    const result = new Uint8Array(this.size[0] * this.size[1] * 4);\n    gl.readPixels(0, 0, this.size[0], this.size[1], gl.RGBA, gl.UNSIGNED_BYTE, result);\n    return result;\n  }\n  renderValues() {\n    if (this._deleted) return null;\n    return new Float32Array(this.renderRawOutput().buffer);\n  }\n  toArray() {\n    return utils.erectPackedFloat(this.renderValues(), this.output[0]);\n  }\n}\n\nmodule.exports = {\n  GLTextureUnsigned\n};"
  },
  {
    "path": "src/backend/headless-gl/kernel.js",
    "content": "const getContext = require('gl');\nconst { WebGLKernel } = require('../web-gl/kernel');\nconst { glKernelString } = require('../gl/kernel-string');\n\nlet isSupported = null;\nlet testCanvas = null;\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nclass HeadlessGLKernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) return isSupported;\n    this.setupFeatureChecks();\n    isSupported = testContext !== null;\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    testCanvas = null;\n    testExtensions = null;\n    if (typeof getContext !== 'function') return;\n    try { // just in case, edge cases\n      testContext = getContext(2, 2, {\n        preserveDrawingBuffer: true\n      });\n      if (!testContext || !testContext.getExtension) return;\n      testExtensions = {\n        STACKGL_resize_drawingbuffer: testContext.getExtension('STACKGL_resize_drawingbuffer'),\n        STACKGL_destroy_context: testContext.getExtension('STACKGL_destroy_context'),\n        OES_texture_float: testContext.getExtension('OES_texture_float'),\n        OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n        OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n        WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n        WEBGL_color_buffer_float: testContext.getExtension('WEBGL_color_buffer_float'),\n      };\n      features = this.getFeatures();\n    } catch (e) {\n      console.warn(e);\n    }\n  }\n\n  static isContextMatch(context) {\n    try {\n      return context.getParameter(context.RENDERER) === 'ANGLE';\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  initCanvas() {\n    return {};\n  }\n\n  initContext() {\n    return getContext(2, 2, {\n      preserveDrawingBuffer: true\n    });\n  }\n\n  initExtensions() {\n    this.extensions = {\n      STACKGL_resize_drawingbuffer: this.context.getExtension('STACKGL_resize_drawingbuffer'),\n      STACKGL_destroy_context: this.context.getExtension('STACKGL_destroy_context'),\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n    };\n  }\n\n  build() {\n    super.build.apply(this, arguments);\n    if (!this.fallbackRequested) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n  }\n\n  destroyExtensions() {\n    this.extensions.STACKGL_resize_drawingbuffer = null;\n    this.extensions.STACKGL_destroy_context = null;\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('STACKGL_destroy_context');\n    if (extension && extension.destroy) {\n      extension.destroy();\n    }\n  }\n\n  /**\n   * @desc Returns the *pre-compiled* Kernel as a JS Object String, that can be reused.\n   */\n  toString() {\n    const setupContextString = `const gl = context || require('gl')(1, 1);\\n`;\n    const destroyContextString = `    if (!context) { gl.getExtension('STACKGL_destroy_context').destroy(); }\\n`;\n    return glKernelString(this.constructor, arguments, this, setupContextString, destroyContextString);\n  }\n\n  setOutput(output) {\n    super.setOutput(output);\n    if (this.graphical && this.extensions.STACKGL_resize_drawingbuffer) {\n      this.extensions.STACKGL_resize_drawingbuffer.resize(this.maxTexSize[0], this.maxTexSize[1]);\n    }\n    return this;\n  }\n}\n\nmodule.exports = {\n  HeadlessGLKernel\n};"
  },
  {
    "path": "src/backend/kernel-value.js",
    "content": "/**\n * @class KernelValue\n */\nclass KernelValue {\n  /**\n   * @param {KernelVariable} value\n   * @param {IKernelValueSettings} settings\n   */\n  constructor(value, settings) {\n    const {\n      name,\n      kernel,\n      context,\n      checkContext,\n      onRequestContextHandle,\n      onUpdateValueMismatch,\n      origin,\n      strictIntegers,\n      type,\n      tactic,\n    } = settings;\n    if (!name) {\n      throw new Error('name not set');\n    }\n    if (!type) {\n      throw new Error('type not set');\n    }\n    if (!origin) {\n      throw new Error('origin not set');\n    }\n    if (origin !== 'user' && origin !== 'constants') {\n      throw new Error(`origin must be \"user\" or \"constants\" value is \"${ origin }\"`);\n    }\n    if (!onRequestContextHandle) {\n      throw new Error('onRequestContextHandle is not set');\n    }\n    this.name = name;\n    this.origin = origin;\n    this.tactic = tactic;\n    this.varName = origin === 'constants' ? `constants.${name}` : name;\n    this.kernel = kernel;\n    this.strictIntegers = strictIntegers;\n    // handle textures\n    this.type = value.type || type;\n    this.size = value.size || null;\n    this.index = null;\n    this.context = context;\n    this.checkContext = checkContext !== null && checkContext !== undefined ? checkContext : true;\n    this.contextHandle = null;\n    this.onRequestContextHandle = onRequestContextHandle;\n    this.onUpdateValueMismatch = onUpdateValueMismatch;\n    this.forceUploadEachRun = null;\n  }\n\n  get id() {\n    return `${this.origin}_${name}`;\n  }\n\n  getSource() {\n    throw new Error(`\"getSource\" not defined on ${ this.constructor.name }`);\n  }\n\n  updateValue(value) {\n    throw new Error(`\"updateValue\" not defined on ${ this.constructor.name }`);\n  }\n}\n\nmodule.exports = {\n  KernelValue\n};"
  },
  {
    "path": "src/backend/kernel.js",
    "content": "const { utils } = require('../utils');\nconst { Input } = require('../input');\n\nclass Kernel {\n  /**\n   * @type {Boolean}\n   */\n  static get isSupported() {\n    throw new Error(`\"isSupported\" not implemented on ${ this.name }`);\n  }\n\n  /**\n   * @abstract\n   * @returns {Boolean}\n   */\n  static isContextMatch(context) {\n    throw new Error(`\"isContextMatch\" not implemented on ${ this.name }`);\n  }\n\n  /**\n   * @type {IKernelFeatures}\n   * Used internally to populate the kernel.feature, which is a getter for the output of this value\n   */\n  static getFeatures() {\n    throw new Error(`\"getFeatures\" not implemented on ${ this.name }`);\n  }\n\n  static destroyContext(context) {\n    throw new Error(`\"destroyContext\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionArguments() {\n    throw new Error(`\"nativeFunctionArguments\" called on ${ this.name }`);\n  }\n\n  static nativeFunctionReturnType() {\n    throw new Error(`\"nativeFunctionReturnType\" called on ${ this.name }`);\n  }\n\n  static combineKernels() {\n    throw new Error(`\"combineKernels\" called on ${ this.name }`);\n  }\n\n  /**\n   *\n   * @param {string|IKernelJSON} source\n   * @param [settings]\n   */\n  constructor(source, settings) {\n    if (typeof source !== 'object') {\n      if (typeof source !== 'string') {\n        throw new Error('source not a string');\n      }\n      if (!utils.isFunctionString(source)) {\n        throw new Error('source not a function string');\n      }\n    }\n    this.useLegacyEncoder = false;\n    this.fallbackRequested = false;\n    this.onRequestFallback = null;\n\n    /**\n     * Name of the arguments found from parsing source argument\n     * @type {String[]}\n     */\n    this.argumentNames = typeof source === 'string' ? utils.getArgumentNamesFromString(source) : null;\n    this.argumentTypes = null;\n    this.argumentSizes = null;\n    this.argumentBitRatios = null;\n    this.kernelArguments = null;\n    this.kernelConstants = null;\n    this.forceUploadKernelConstants = null;\n\n\n    /**\n     * The function source\n     * @type {String|IKernelJSON}\n     */\n    this.source = source;\n\n    /**\n     * The size of the kernel's output\n     * @type {Number[]}\n     */\n    this.output = null;\n\n    /**\n     * Debug mode\n     * @type {Boolean}\n     */\n    this.debug = false;\n\n    /**\n     * Graphical mode\n     * @type {Boolean}\n     */\n    this.graphical = false;\n\n    /**\n     * Maximum loops when using argument values to prevent infinity\n     * @type {Number}\n     */\n    this.loopMaxIterations = 0;\n\n    /**\n     * Constants used in kernel via `this.constants`\n     * @type {Object}\n     */\n    this.constants = null;\n\n    /**\n     *\n     * @type {Object.<string, string>}\n     */\n    this.constantTypes = null;\n\n    /**\n     *\n     * @type {Object.<string, number>}\n     */\n    this.constantBitRatios = null;\n\n    /**\n     *\n     * @type {boolean}\n     */\n    this.dynamicArguments = false;\n\n    /**\n     *\n     * @type {boolean}\n     */\n    this.dynamicOutput = false;\n\n    /**\n     *\n     * @type {Object}\n     */\n    this.canvas = null;\n\n    /**\n     *\n     * @type {Object}\n     */\n    this.context = null;\n\n    /**\n     *\n     * @type {Boolean}\n     */\n    this.checkContext = null;\n\n    /**\n     *\n     * @type {GPU}\n     */\n    this.gpu = null;\n\n    /**\n     *\n     * @type {IGPUFunction[]}\n     */\n    this.functions = null;\n\n    /**\n     *\n     * @type {IGPUNativeFunction[]}\n     */\n    this.nativeFunctions = null;\n\n    /**\n     *\n     * @type {String}\n     */\n    this.injectedNative = null;\n\n    /**\n     *\n     * @type {ISubKernel[]}\n     */\n    this.subKernels = null;\n\n    /**\n     *\n     * @type {Boolean}\n     */\n    this.validate = true;\n\n    /**\n     * Enforces kernel to write to a new array or texture on run\n     * @type {Boolean}\n     */\n    this.immutable = false;\n\n    /**\n     * Enforces kernel to write to a texture on run\n     * @type {Boolean}\n     */\n    this.pipeline = false;\n\n    /**\n     * Make GPU use single precision or unsigned.  Acceptable values: 'single' or 'unsigned'\n     * @type {String|null}\n     * @enum 'single' | 'unsigned'\n     */\n    this.precision = null;\n\n    /**\n     *\n     * @type {String|null}\n     * @enum 'speed' | 'balanced' | 'precision'\n     */\n    this.tactic = null;\n\n    this.plugins = null;\n\n    this.returnType = null;\n    this.leadingReturnStatement = null;\n    this.followingReturnStatement = null;\n    this.optimizeFloatMemory = null;\n    this.strictIntegers = false;\n    this.fixIntegerDivisionAccuracy = null;\n    this.built = false;\n    this.signature = null;\n  }\n\n  /**\n   *\n   * @param {IDirectKernelSettings|IJSONSettings} settings\n   */\n  mergeSettings(settings) {\n    for (let p in settings) {\n      if (!settings.hasOwnProperty(p) || !this.hasOwnProperty(p)) continue;\n      switch (p) {\n        case 'output':\n          if (!Array.isArray(settings.output)) {\n            this.setOutput(settings.output); // Flatten output object\n            continue;\n          }\n          break;\n        case 'functions':\n          this.functions = [];\n          for (let i = 0; i < settings.functions.length; i++) {\n            this.addFunction(settings.functions[i]);\n          }\n          continue;\n        case 'graphical':\n          if (settings[p] && !settings.hasOwnProperty('precision')) {\n            this.precision = 'unsigned';\n          }\n          this[p] = settings[p];\n          continue;\n        case 'nativeFunctions':\n          if (!settings.nativeFunctions) continue;\n          this.nativeFunctions = [];\n          for (let i = 0; i < settings.nativeFunctions.length; i++) {\n            const s = settings.nativeFunctions[i];\n            const { name, source } = s;\n            this.addNativeFunction(name, source, s);\n          }\n          continue;\n      }\n      this[p] = settings[p];\n    }\n\n    if (!this.canvas) this.canvas = this.initCanvas();\n    if (!this.context) this.context = this.initContext();\n    if (!this.plugins) this.plugins = this.initPlugins(settings);\n  }\n  /**\n   * @desc Builds the Kernel, by compiling Fragment and Vertical Shaders,\n   * and instantiates the program.\n   * @abstract\n   */\n  build() {\n    throw new Error(`\"build\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @desc Run the kernel program, and send the output to renderOutput\n   * <p> This method calls a helper method *renderOutput* to return the result. </p>\n   * @returns {Float32Array|Float32Array[]|Float32Array[][]|void} Result The final output of the program, as float, and as Textures for reuse.\n   * @abstract\n   */\n  run() {\n    throw new Error(`\"run\" not defined on ${ this.constructor.name }`)\n  }\n\n  /**\n   * @abstract\n   * @return {Object}\n   */\n  initCanvas() {\n    throw new Error(`\"initCanvas\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @abstract\n   * @return {Object}\n   */\n  initContext() {\n    throw new Error(`\"initContext\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @param {IDirectKernelSettings} settings\n   * @return {string[]};\n   * @abstract\n   */\n  initPlugins(settings) {\n    throw new Error(`\"initPlugins\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   *\n   * @param {KernelFunction|string|IGPUFunction} source\n   * @param {IFunctionSettings} [settings]\n   * @return {Kernel}\n   */\n  addFunction(source, settings = {}) {\n    if (source.name && source.source && source.argumentTypes && 'returnType' in source) {\n      this.functions.push(source);\n    } else if ('settings' in source && 'source' in source) {\n      this.functions.push(this.functionToIGPUFunction(source.source, source.settings));\n    } else if (typeof source === 'string' || typeof source === 'function') {\n      this.functions.push(this.functionToIGPUFunction(source, settings));\n    } else {\n      throw new Error(`function not properly defined`);\n    }\n    return this;\n  }\n\n  /**\n   *\n   * @param {string} name\n   * @param {string} source\n   * @param {IGPUFunctionSettings} [settings]\n   */\n  addNativeFunction(name, source, settings = {}) {\n    const { argumentTypes, argumentNames } = settings.argumentTypes ?\n      splitArgumentTypes(settings.argumentTypes) :\n      this.constructor.nativeFunctionArguments(source) || {};\n    this.nativeFunctions.push({\n      name,\n      source,\n      settings,\n      argumentTypes,\n      argumentNames,\n      returnType: settings.returnType || this.constructor.nativeFunctionReturnType(source)\n    });\n    return this;\n  }\n\n  /**\n   * @desc Setup the parameter types for the parameters\n   * supplied to the Kernel function\n   *\n   * @param {IArguments} args - The actual parameters sent to the Kernel\n   */\n  setupArguments(args) {\n    this.kernelArguments = [];\n    if (!this.argumentTypes) {\n      if (!this.argumentTypes) {\n        this.argumentTypes = [];\n        for (let i = 0; i < args.length; i++) {\n          const argType = utils.getVariableType(args[i], this.strictIntegers);\n          const type = argType === 'Integer' ? 'Number' : argType;\n          this.argumentTypes.push(type);\n          this.kernelArguments.push({\n            type\n          });\n        }\n      }\n    } else {\n      for (let i = 0; i < this.argumentTypes.length; i++) {\n        this.kernelArguments.push({\n          type: this.argumentTypes[i]\n        });\n      }\n    }\n\n    // setup sizes\n    this.argumentSizes = new Array(args.length);\n    this.argumentBitRatios = new Int32Array(args.length);\n\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      this.argumentSizes[i] = arg.constructor === Input ? arg.size : null;\n      this.argumentBitRatios[i] = this.getBitRatio(arg);\n    }\n\n    if (this.argumentNames.length !== args.length) {\n      throw new Error(`arguments are miss-aligned`);\n    }\n  }\n\n  /**\n   * Setup constants\n   */\n  setupConstants() {\n    this.kernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    if (this.constants) {\n      for (let name in this.constants) {\n        if (needsConstantTypes) {\n          const type = utils.getVariableType(this.constants[name], this.strictIntegers);\n          this.constantTypes[name] = type;\n          this.kernelConstants.push({\n            name,\n            type\n          });\n        } else {\n          this.kernelConstants.push({\n            name,\n            type: this.constantTypes[name]\n          });\n        }\n        this.constantBitRatios[name] = this.getBitRatio(this.constants[name]);\n      }\n    }\n  }\n\n  /**\n   *\n   * @param flag\n   * @return {this}\n   */\n  setOptimizeFloatMemory(flag) {\n    this.optimizeFloatMemory = flag;\n    return this;\n  }\n\n  /**\n   *\n   * @param {Array|Object} output\n   * @return {number[]}\n   */\n  toKernelOutput(output) {\n    if (output.hasOwnProperty('x')) {\n      if (output.hasOwnProperty('y')) {\n        if (output.hasOwnProperty('z')) {\n          return [output.x, output.y, output.z];\n        } else {\n          return [output.x, output.y];\n        }\n      } else {\n        return [output.x];\n      }\n    } else {\n      return output;\n    }\n  }\n\n  /**\n   * @desc Set output dimensions of the kernel function\n   * @param {Array|Object} output - The output array to set the kernel output size to\n   * @return {this}\n   */\n  setOutput(output) {\n    this.output = this.toKernelOutput(output);\n    return this;\n  }\n\n  /**\n   * @desc Toggle debug mode\n   * @param {Boolean} flag - true to enable debug\n   * @return {this}\n   */\n  setDebug(flag) {\n    this.debug = flag;\n    return this;\n  }\n\n  /**\n   * @desc Toggle graphical output mode\n   * @param {Boolean} flag - true to enable graphical output\n   * @return {this}\n   */\n  setGraphical(flag) {\n    this.graphical = flag;\n    this.precision = 'unsigned';\n    return this;\n  }\n\n  /**\n   * @desc Set the maximum number of loop iterations\n   * @param {number} max - iterations count\n   * @return {this}\n   */\n  setLoopMaxIterations(max) {\n    this.loopMaxIterations = max;\n    return this;\n  }\n\n  /**\n   * @desc Set Constants\n   * @return {this}\n   */\n  setConstants(constants) {\n    this.constants = constants;\n    return this;\n  }\n\n  /**\n   *\n   * @param {IKernelValueTypes} constantTypes\n   * @return {this}\n   */\n  setConstantTypes(constantTypes) {\n    this.constantTypes = constantTypes;\n    return this;\n  }\n\n  /**\n   *\n   * @param {IFunction[]|KernelFunction[]} functions\n   * @return {this}\n   */\n  setFunctions(functions) {\n    for (let i = 0; i < functions.length; i++) {\n      this.addFunction(functions[i]);\n    }\n    return this;\n  }\n\n  /**\n   *\n   * @param {IGPUNativeFunction[]} nativeFunctions\n   * @return {this}\n   */\n  setNativeFunctions(nativeFunctions) {\n    for (let i = 0; i < nativeFunctions.length; i++) {\n      const settings = nativeFunctions[i];\n      const { name, source } = settings;\n      this.addNativeFunction(name, source, settings);\n    }\n    return this;\n  }\n\n  /**\n   *\n   * @param {String} injectedNative\n   * @return {this}\n   */\n  setInjectedNative(injectedNative) {\n    this.injectedNative = injectedNative;\n    return this;\n  }\n\n  /**\n   * Set writing to texture on/off\n   * @param flag\n   * @return {this}\n   */\n  setPipeline(flag) {\n    this.pipeline = flag;\n    return this;\n  }\n\n  /**\n   * Set precision to 'unsigned' or 'single'\n   * @param {String} flag 'unsigned' or 'single'\n   * @return {this}\n   */\n  setPrecision(flag) {\n    this.precision = flag;\n    return this;\n  }\n\n  /**\n   * @param flag\n   * @return {Kernel}\n   * @deprecated\n   */\n  setDimensions(flag) {\n    utils.warnDeprecated('method', 'setDimensions', 'setOutput');\n    this.output = flag;\n    return this;\n  }\n\n  /**\n   * @param flag\n   * @return {this}\n   * @deprecated\n   */\n  setOutputToTexture(flag) {\n    utils.warnDeprecated('method', 'setOutputToTexture', 'setPipeline');\n    this.pipeline = flag;\n    return this;\n  }\n\n  /**\n   * Set to immutable\n   * @param flag\n   * @return {this}\n   */\n  setImmutable(flag) {\n    this.immutable = flag;\n    return this;\n  }\n\n  /**\n   * @desc Bind the canvas to kernel\n   * @param {Object} canvas\n   * @return {this}\n   */\n  setCanvas(canvas) {\n    this.canvas = canvas;\n    return this;\n  }\n\n  /**\n   * @param {Boolean} flag\n   * @return {this}\n   */\n  setStrictIntegers(flag) {\n    this.strictIntegers = flag;\n    return this;\n  }\n\n  /**\n   *\n   * @param flag\n   * @return {this}\n   */\n  setDynamicOutput(flag) {\n    this.dynamicOutput = flag;\n    return this;\n  }\n\n  /**\n   * @deprecated\n   * @param flag\n   * @return {this}\n   */\n  setHardcodeConstants(flag) {\n    utils.warnDeprecated('method', 'setHardcodeConstants');\n    this.setDynamicOutput(flag);\n    this.setDynamicArguments(flag);\n    return this;\n  }\n\n  /**\n   *\n   * @param flag\n   * @return {this}\n   */\n  setDynamicArguments(flag) {\n    this.dynamicArguments = flag;\n    return this;\n  }\n\n  /**\n   * @param {Boolean} flag\n   * @return {this}\n   */\n  setUseLegacyEncoder(flag) {\n    this.useLegacyEncoder = flag;\n    return this;\n  }\n\n  /**\n   *\n   * @param {Boolean} flag\n   * @return {this}\n   */\n  setWarnVarUsage(flag) {\n    utils.warnDeprecated('method', 'setWarnVarUsage');\n    return this;\n  }\n\n  /**\n   * @deprecated\n   * @returns {Object}\n   */\n  getCanvas() {\n    utils.warnDeprecated('method', 'getCanvas');\n    return this.canvas;\n  }\n\n  /**\n   * @deprecated\n   * @returns {Object}\n   */\n  getWebGl() {\n    utils.warnDeprecated('method', 'getWebGl');\n    return this.context;\n  }\n\n  /**\n   * @desc Bind the webGL instance to kernel\n   * @param {WebGLRenderingContext} context - webGl instance to bind\n   */\n  setContext(context) {\n    this.context = context;\n    return this;\n  }\n\n  /**\n   *\n   * @param {IKernelValueTypes|GPUVariableType[]} argumentTypes\n   * @return {this}\n   */\n  setArgumentTypes(argumentTypes) {\n    if (Array.isArray(argumentTypes)) {\n      this.argumentTypes = argumentTypes;\n    } else {\n      this.argumentTypes = [];\n      for (const p in argumentTypes) {\n        if (!argumentTypes.hasOwnProperty(p)) continue;\n        const argumentIndex = this.argumentNames.indexOf(p);\n        if (argumentIndex === -1) throw new Error(`unable to find argument ${ p }`);\n        this.argumentTypes[argumentIndex] = argumentTypes[p];\n      }\n    }\n    return this;\n  }\n\n  /**\n   *\n   * @param {Tactic} tactic\n   * @return {this}\n   */\n  setTactic(tactic) {\n    this.tactic = tactic;\n    return this;\n  }\n\n  requestFallback(args) {\n    if (!this.onRequestFallback) {\n      throw new Error(`\"onRequestFallback\" not defined on ${ this.constructor.name }`);\n    }\n    this.fallbackRequested = true;\n    return this.onRequestFallback(args);\n  }\n\n  /**\n   * @desc Validate settings\n   * @abstract\n   */\n  validateSettings() {\n    throw new Error(`\"validateSettings\" not defined on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @desc Add a sub kernel to the root kernel instance.\n   * This is what `createKernelMap` uses.\n   *\n   * @param {ISubKernel} subKernel - function (as a String) of the subKernel to add\n   */\n  addSubKernel(subKernel) {\n    if (this.subKernels === null) {\n      this.subKernels = [];\n    }\n    if (!subKernel.source) throw new Error('subKernel missing \"source\" property');\n    if (!subKernel.property && isNaN(subKernel.property)) throw new Error('subKernel missing \"property\" property');\n    if (!subKernel.name) throw new Error('subKernel missing \"name\" property');\n    this.subKernels.push(subKernel);\n    return this;\n  }\n\n  /**\n   * @desc Destroys all memory associated with this kernel\n   * @param {Boolean} [removeCanvasReferences] remove any associated canvas references\n   */\n  destroy(removeCanvasReferences) {\n    throw new Error(`\"destroy\" called on ${ this.constructor.name }`);\n  }\n\n  /**\n   * bit storage ratio of source to target 'buffer', i.e. if 8bit array -> 32bit tex = 4\n   * @param value\n   * @returns {number}\n   */\n  getBitRatio(value) {\n    if (this.precision === 'single') {\n      // 8 and 16 are up-converted to float32\n      return 4;\n    } else if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  /**\n   * @param {Boolean} [flip]\n   * @returns {Uint8ClampedArray}\n   */\n  getPixels(flip) {\n    throw new Error(`\"getPixels\" called on ${ this.constructor.name }`);\n  }\n\n  checkOutput() {\n    if (!this.output || !utils.isArray(this.output)) throw new Error('kernel.output not an array');\n    if (this.output.length < 1) throw new Error('kernel.output is empty, needs at least 1 value');\n    for (let i = 0; i < this.output.length; i++) {\n      if (isNaN(this.output[i]) || this.output[i] < 1) {\n        throw new Error(`${ this.constructor.name }.output[${ i }] incorrectly defined as \\`${ this.output[i] }\\`, needs to be numeric, and greater than 0`);\n      }\n    }\n  }\n\n  /**\n   *\n   * @param {String} value\n   */\n  prependString(value) {\n    throw new Error(`\"prependString\" called on ${ this.constructor.name }`);\n  }\n\n  /**\n   *\n   * @param {String} value\n   * @return Boolean\n   */\n  hasPrependString(value) {\n    throw new Error(`\"hasPrependString\" called on ${ this.constructor.name }`);\n  }\n\n  /**\n   * @return {IKernelJSON}\n   */\n  toJSON() {\n    return {\n      settings: {\n        output: this.output,\n        pipeline: this.pipeline,\n        argumentNames: this.argumentNames,\n        argumentsTypes: this.argumentTypes,\n        constants: this.constants,\n        pluginNames: this.plugins ? this.plugins.map(plugin => plugin.name) : null,\n        returnType: this.returnType,\n      }\n    };\n  }\n\n  /**\n   * @param {IArguments} args\n   */\n  buildSignature(args) {\n    const Constructor = this.constructor;\n    this.signature = Constructor.getSignature(this, Constructor.getArgumentTypes(this, args));\n  }\n\n  /**\n   * @param {Kernel} kernel\n   * @param {IArguments} args\n   * @returns GPUVariableType[]\n   */\n  static getArgumentTypes(kernel, args) {\n    const argumentTypes = new Array(args.length);\n    for (let i = 0; i < args.length; i++) {\n      const arg = args[i];\n      const type = kernel.argumentTypes[i];\n      if (arg.type) {\n        argumentTypes[i] = arg.type;\n      } else {\n        switch (type) {\n          case 'Number':\n          case 'Integer':\n          case 'Float':\n          case 'ArrayTexture(1)':\n            argumentTypes[i] = utils.getVariableType(arg);\n            break;\n          default:\n            argumentTypes[i] = type;\n        }\n      }\n    }\n    return argumentTypes;\n  }\n\n  /**\n   *\n   * @param {Kernel} kernel\n   * @param {GPUVariableType[]} argumentTypes\n   * @abstract\n   */\n  static getSignature(kernel, argumentTypes) {\n    throw new Error(`\"getSignature\" not implemented on ${ this.name }`);\n  }\n\n  /**\n   *\n   * @param {String|Function} source\n   * @param {IFunctionSettings} [settings]\n   * @returns {IGPUFunction}\n   */\n  functionToIGPUFunction(source, settings = {}) {\n    if (typeof source !== 'string' && typeof source !== 'function') throw new Error('source not a string or function');\n    const sourceString = typeof source === 'string' ? source : source.toString();\n    let argumentTypes = [];\n\n    if (Array.isArray(settings.argumentTypes)) {\n      argumentTypes = settings.argumentTypes;\n    } else if (typeof settings.argumentTypes === 'object') {\n      argumentTypes = utils.getArgumentNamesFromString(sourceString)\n        .map(name => settings.argumentTypes[name]) || [];\n    } else {\n      argumentTypes = settings.argumentTypes || [];\n    }\n\n    return {\n      name: utils.getFunctionNameFromString(sourceString) || null,\n      source: sourceString,\n      argumentTypes,\n      returnType: settings.returnType || null,\n    };\n  }\n\n  /**\n   *\n   * @param {Kernel} previousKernel\n   * @abstract\n   */\n  onActivate(previousKernel) {}\n}\n\nfunction splitArgumentTypes(argumentTypesObject) {\n  const argumentNames = Object.keys(argumentTypesObject);\n  const argumentTypes = [];\n  for (let i = 0; i < argumentNames.length; i++) {\n    const argumentName = argumentNames[i];\n    argumentTypes.push(argumentTypesObject[argumentName]);\n  }\n  return { argumentTypes, argumentNames };\n}\n\nmodule.exports = {\n  Kernel\n};"
  },
  {
    "path": "src/backend/web-gl/fragment-shader.js",
    "content": "// language=GLSL\nconst fragmentShader = `__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nvarying vec2 vTexCoord;\n\nfloat acosh(float x) {\n  return log(x + sqrt(x * x - 1.0));\n}\n\nfloat sinh(float x) {\n  return (pow(${Math.E}, x) - pow(${Math.E}, -x)) / 2.0;\n}\n\nfloat asinh(float x) {\n  return log(x + sqrt(x * x + 1.0));\n}\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat atanh(float x) {\n  x = (x + 1.0) / (x - 1.0);\n  if (x < 0.0) {\n    return 0.5 * log(-x);\n  }\n  return 0.5 * log(x);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat cosh(float x) {\n  return (pow(${Math.E}, x) + pow(${Math.E}, -x)) / 2.0; \n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat tanh(float x) {\n  float e = exp(2.0 * x);\n  return (e - 1.0) / (e + 1.0);\n}\n\nfloat trunc(float x) {\n  if (x >= 0.0) {\n    return floor(x); \n  } else {\n    return ceil(x);\n  }\n}\n\nvec4 _round(vec4 x) {\n  return floor(x + 0.5);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x / y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(_round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(_round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  if (channel == 0) return texel.r * 255.0 + texel.g * 65280.0;\n  if (channel == 1) return texel.b * 255.0 + texel.a * 65280.0;\n  return 0.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  if (channel == 0) return texel.r * 255.0;\n  if (channel == 1) return texel.g * 255.0;\n  if (channel == 2) return texel.b * 255.0;\n  if (channel == 3) return texel.a * 255.0;\n  return 0.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return texel.r;\n  if (channel == 1) return texel.g;\n  if (channel == 2) return texel.b;\n  if (channel == 3) return texel.a;\n  return 0.0;\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture2D(tex, st / vec2(texSize));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture2D(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n  \n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture2D(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture2D(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nvoid color(sampler2D image) {\n  actualColor = texture2D(image, vTexCoord);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};"
  },
  {
    "path": "src/backend/web-gl/function-node.js",
    "content": "const { utils } = require('../../utils');\nconst { FunctionNode } = require('../function-node');\n\n/**\n * @desc [INTERNAL] Takes in a function node, and does all the AST voodoo required to toString its respective WebGL code\n */\nclass WebGLFunctionNode extends FunctionNode {\n  constructor(source, settings) {\n    super(source, settings);\n    if (settings && settings.hasOwnProperty('fixIntegerDivisionAccuracy')) {\n      this.fixIntegerDivisionAccuracy = settings.fixIntegerDivisionAccuracy;\n    }\n  }\n\n  astConditionalExpression(ast, retArr) {\n    if (ast.type !== 'ConditionalExpression') {\n      throw this.astErrorOutput('Not a conditional expression', ast);\n    }\n    const consequentType = this.getType(ast.consequent);\n    const alternateType = this.getType(ast.alternate);\n    // minification handling if void\n    if (consequentType === null && alternateType === null) {\n      retArr.push('if (');\n      this.astGeneric(ast.test, retArr);\n      retArr.push(') {');\n      this.astGeneric(ast.consequent, retArr);\n      retArr.push(';');\n      retArr.push('} else {');\n      this.astGeneric(ast.alternate, retArr);\n      retArr.push(';');\n      retArr.push('}');\n      return retArr;\n    }\n    retArr.push('(');\n    this.astGeneric(ast.test, retArr);\n    retArr.push('?');\n    this.astGeneric(ast.consequent, retArr);\n    retArr.push(':');\n    this.astGeneric(ast.alternate, retArr);\n    retArr.push(')');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for to its *named function*\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astFunction(ast, retArr) {\n    // Setup function return type and name\n    if (this.isRootKernel) {\n      retArr.push('void');\n    } else {\n      // looking up return type, this is a little expensive, and can be avoided if returnType is set\n      if (!this.returnType) {\n        const lastReturn = this.findLastReturn();\n        if (lastReturn) {\n          this.returnType = this.getType(ast.body);\n          if (this.returnType === 'LiteralInteger') {\n            this.returnType = 'Number';\n          }\n        }\n      }\n\n      const { returnType } = this;\n      if (!returnType) {\n        retArr.push('void');\n      } else {\n        const type = typeMap[returnType];\n        if (!type) {\n          throw new Error(`unknown type ${returnType}`);\n        }\n        retArr.push(type);\n      }\n    }\n    retArr.push(' ');\n    retArr.push(this.name);\n    retArr.push('(');\n\n    if (!this.isRootKernel) {\n      // Arguments handling\n      for (let i = 0; i < this.argumentNames.length; ++i) {\n        const argumentName = this.argumentNames[i];\n\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        let argumentType = this.argumentTypes[this.argumentNames.indexOf(argumentName)];\n        // The type is too loose ended, here we decide to solidify a type, lets go with float\n        if (!argumentType) {\n          throw this.astErrorOutput(`Unknown argument ${argumentName} type`, ast);\n        }\n        if (argumentType === 'LiteralInteger') {\n          this.argumentTypes[i] = argumentType = 'Number';\n        }\n        const type = typeMap[argumentType];\n        if (!type) {\n          throw this.astErrorOutput('Unexpected expression', ast);\n        }\n        const name = utils.sanitizeName(argumentName);\n        if (type === 'sampler2D' || type === 'sampler2DArray') {\n          // mash needed arguments together, since now we have end to end inference\n          retArr.push(`${type} user_${name},ivec2 user_${name}Size,ivec3 user_${name}Dim`);\n        } else {\n          retArr.push(`${type} user_${name}`);\n        }\n      }\n    }\n\n    // Function opening\n    retArr.push(') {\\n');\n\n    // Body statement iteration\n    for (let i = 0; i < ast.body.body.length; ++i) {\n      this.astGeneric(ast.body.body[i], retArr);\n      retArr.push('\\n');\n    }\n\n    // Function closing\n    retArr.push('}\\n');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for to *return* statement\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astReturnStatement(ast, retArr) {\n    if (!ast.argument) throw this.astErrorOutput('Unexpected return statement', ast);\n    this.pushState('skip-literal-correction');\n    const type = this.getType(ast.argument);\n    this.popState('skip-literal-correction');\n\n    const result = [];\n\n    if (!this.returnType) {\n      if (type === 'LiteralInteger' || type === 'Integer') {\n        this.returnType = 'Number';\n      } else {\n        this.returnType = type;\n      }\n    }\n\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Float':\n        switch (type) {\n          case 'Integer':\n            result.push('float(');\n            this.astGeneric(ast.argument, result);\n            result.push(')');\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToFloat(ast.argument, result);\n\n            // Running astGeneric forces the LiteralInteger to pick a type, and here, if we are returning a float, yet\n            // the LiteralInteger has picked to be an integer because of constraints on it we cast it to float.\n            if (this.getType(ast) === 'Integer') {\n              result.unshift('float(');\n              result.push(')');\n            }\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Integer':\n        switch (type) {\n          case 'Float':\n          case 'Number':\n            this.castValueToInteger(ast.argument, result);\n            break;\n          case 'LiteralInteger':\n            this.castLiteralToInteger(ast.argument, result);\n            break;\n          default:\n            this.astGeneric(ast.argument, result);\n        }\n        break;\n      case 'Array(4)':\n      case 'Array(3)':\n      case 'Array(2)':\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n      case 'Input':\n        this.astGeneric(ast.argument, result);\n        break;\n      default:\n        throw this.astErrorOutput(`unhandled return type ${this.returnType}`, ast);\n    }\n\n    if (this.isRootKernel) {\n      retArr.push(`kernelResult = ${ result.join('') };`);\n      retArr.push('return;');\n    } else if (this.isSubKernel) {\n      retArr.push(`subKernelResult_${ this.name } = ${ result.join('') };`);\n      retArr.push(`return subKernelResult_${ this.name };`);\n    } else {\n      retArr.push(`return ${ result.join('') };`);\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *literal value*\n   *\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   *\n   * @returns {Array} the append retArr\n   */\n  astLiteral(ast, retArr) {\n    // Reject non numeric literals\n    if (isNaN(ast.value)) {\n      throw this.astErrorOutput(\n        'Non-numeric literal not supported : ' + ast.value,\n        ast\n      );\n    }\n\n    const key = this.astKey(ast);\n    if (Number.isInteger(ast.value)) {\n      if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n        this.literalTypes[key] = 'Integer';\n        retArr.push(`${ast.value}`);\n      } else if (this.isState('casting-to-float') || this.isState('building-float')) {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      } else {\n        this.literalTypes[key] = 'Number';\n        retArr.push(`${ast.value}.0`);\n      }\n    } else if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n      this.literalTypes[key] = 'Integer';\n      retArr.push(Math.round(ast.value));\n    } else {\n      this.literalTypes[key] = 'Number';\n      retArr.push(`${ast.value}`);\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *binary* expression\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astBinaryExpression(ast, retArr) {\n    if (this.checkAndUpconvertOperator(ast, retArr)) {\n      return retArr;\n    }\n\n    if (this.fixIntegerDivisionAccuracy && ast.operator === '/') {\n      retArr.push('divWithIntCheck(');\n      this.pushState('building-float');\n      switch (this.getType(ast.left)) {\n        case 'Integer':\n          this.castValueToFloat(ast.left, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.left, retArr);\n          break;\n        default:\n          this.astGeneric(ast.left, retArr);\n      }\n      retArr.push(', ');\n      switch (this.getType(ast.right)) {\n        case 'Integer':\n          this.castValueToFloat(ast.right, retArr);\n          break;\n        case 'LiteralInteger':\n          this.castLiteralToFloat(ast.right, retArr);\n          break;\n        default:\n          this.astGeneric(ast.right, retArr);\n      }\n      this.popState('building-float');\n      retArr.push(')');\n      return retArr;\n    }\n\n    retArr.push('(');\n    const leftType = this.getType(ast.left) || 'Number';\n    const rightType = this.getType(ast.right) || 'Number';\n    if (!leftType || !rightType) {\n      throw this.astErrorOutput(`Unhandled binary expression`, ast);\n    }\n    const key = leftType + ' & ' + rightType;\n    switch (key) {\n      case 'Integer & Integer':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n      case 'Number & Float':\n      case 'Float & Number':\n      case 'Float & Float':\n      case 'Number & Number':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & LiteralInteger':\n        if (this.isState('casting-to-integer') || this.isState('building-integer')) {\n          this.pushState('building-integer');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.astGeneric(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.castLiteralToFloat(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castLiteralToFloat(ast.right, retArr);\n          this.popState('building-float');\n        }\n        break;\n\n      case 'Integer & Float':\n      case 'Integer & Number':\n        if (ast.operator === '>' || ast.operator === '<' && ast.right.type === 'Literal') {\n          // if right value is actually a float, don't loose that information, cast left to right rather than the usual right to left\n          if (!Number.isInteger(ast.right.value)) {\n            this.pushState('building-float');\n            this.castValueToFloat(ast.left, retArr);\n            retArr.push(operatorMap[ast.operator] || ast.operator);\n            this.astGeneric(ast.right, retArr);\n            this.popState('building-float');\n            break;\n          }\n        }\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.pushState('casting-to-integer');\n        if (ast.right.type === 'Literal') {\n          const literalResult = [];\n          this.astGeneric(ast.right, literalResult);\n          const literalType = this.getType(ast.right);\n          if (literalType === 'Integer') {\n            retArr.push(literalResult.join(''));\n          } else {\n            throw this.astErrorOutput(`Unhandled binary expression with literal`, ast);\n          }\n        } else {\n          retArr.push('int(');\n          this.astGeneric(ast.right, retArr);\n          retArr.push(')');\n        }\n        this.popState('casting-to-integer');\n        this.popState('building-integer');\n        break;\n      case 'Integer & LiteralInteger':\n        this.pushState('building-integer');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToInteger(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Number & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'Float & LiteralInteger':\n      case 'Number & LiteralInteger':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castLiteralToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n      case 'LiteralInteger & Float':\n      case 'LiteralInteger & Number':\n        if (this.isState('casting-to-integer')) {\n          this.pushState('building-integer');\n          this.castLiteralToInteger(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.castValueToInteger(ast.right, retArr);\n          this.popState('building-integer');\n        } else {\n          this.pushState('building-float');\n          this.astGeneric(ast.left, retArr);\n          retArr.push(operatorMap[ast.operator] || ast.operator);\n          this.pushState('casting-to-float');\n          this.astGeneric(ast.right, retArr);\n          this.popState('casting-to-float');\n          this.popState('building-float');\n        }\n        break;\n      case 'LiteralInteger & Integer':\n        this.pushState('building-integer');\n        this.castLiteralToInteger(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-integer');\n        break;\n\n      case 'Boolean & Boolean':\n        this.pushState('building-boolean');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.astGeneric(ast.right, retArr);\n        this.popState('building-boolean');\n        break;\n\n      case 'Float & Integer':\n        this.pushState('building-float');\n        this.astGeneric(ast.left, retArr);\n        retArr.push(operatorMap[ast.operator] || ast.operator);\n        this.castValueToFloat(ast.right, retArr);\n        this.popState('building-float');\n        break;\n\n      default:\n        throw this.astErrorOutput(`Unhandled binary expression between ${key}`, ast);\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  checkAndUpconvertOperator(ast, retArr) {\n    const bitwiseResult = this.checkAndUpconvertBitwiseOperators(ast, retArr);\n    if (bitwiseResult) {\n      return bitwiseResult;\n    }\n    const upconvertableOperators = {\n      '%': this.fixIntegerDivisionAccuracy ? 'integerCorrectionModulo' : 'modulo',\n      '**': 'pow',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.left)) {\n      case 'Integer':\n        this.castValueToFloat(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    switch (this.getType(ast.right)) {\n      case 'Integer':\n        this.castValueToFloat(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToFloat(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseOperators(ast, retArr) {\n    const upconvertableOperators = {\n      '&': 'bitwiseAnd',\n      '|': 'bitwiseOr',\n      '^': 'bitwiseXOR',\n      '<<': 'bitwiseZeroFillLeftShift',\n      '>>': 'bitwiseSignedRightShift',\n      '>>>': 'bitwiseZeroFillRightShift',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    const leftType = this.getType(ast.left);\n    switch (leftType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.left, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.left, retArr);\n        break;\n      default:\n        this.astGeneric(ast.left, retArr);\n    }\n    retArr.push(',');\n    const rightType = this.getType(ast.right);\n    switch (rightType) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.right, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.right, retArr);\n        break;\n      default:\n        this.astGeneric(ast.right, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  checkAndUpconvertBitwiseUnary(ast, retArr) {\n    const upconvertableOperators = {\n      '~': 'bitwiseNot',\n    };\n    const foundOperator = upconvertableOperators[ast.operator];\n    if (!foundOperator) return null;\n    retArr.push(foundOperator);\n    retArr.push('(');\n    switch (this.getType(ast.argument)) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(ast.argument, retArr);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(ast.argument, retArr);\n        break;\n      default:\n        this.astGeneric(ast.argument, retArr);\n    }\n    retArr.push(')');\n    return retArr;\n  }\n\n  /**\n   *\n   * @param {Object} ast\n   * @param {Array} retArr\n   * @return {String[]}\n   */\n  castLiteralToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  /**\n   *\n   * @param {Object} ast\n   * @param {Array} retArr\n   * @return {String[]}\n   */\n  castLiteralToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    this.astGeneric(ast, retArr);\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  /**\n   *\n   * @param {Object} ast\n   * @param {Array} retArr\n   * @return {String[]}\n   */\n  castValueToInteger(ast, retArr) {\n    this.pushState('casting-to-integer');\n    retArr.push('int(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-integer');\n    return retArr;\n  }\n\n  /**\n   *\n   * @param {Object} ast\n   * @param {Array} retArr\n   * @return {String[]}\n   */\n  castValueToFloat(ast, retArr) {\n    this.pushState('casting-to-float');\n    retArr.push('float(');\n    this.astGeneric(ast, retArr);\n    retArr.push(')');\n    this.popState('casting-to-float');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *identifier* expression\n   * @param {Object} idtNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput('IdentifierExpression - not an Identifier', idtNode);\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      // https://stackoverflow.com/a/47543127/1324039\n      retArr.push('3.402823466e+38');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *for-loop* expression\n   * @param {Object} forNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed webgl string\n   */\n  astForStatement(forNode, retArr) {\n    if (forNode.type !== 'ForStatement') {\n      throw this.astErrorOutput('Invalid for statement', forNode);\n    }\n\n    const initArr = [];\n    const testArr = [];\n    const updateArr = [];\n    const bodyArr = [];\n    let isSafe = null;\n\n    if (forNode.init) {\n      const { declarations } = forNode.init;\n      if (declarations.length > 1) {\n        isSafe = false;\n      }\n      this.astGeneric(forNode.init, initArr);\n      for (let i = 0; i < declarations.length; i++) {\n        if (declarations[i].init && declarations[i].init.type !== 'Literal') {\n          isSafe = false;\n        }\n      }\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.test) {\n      this.astGeneric(forNode.test, testArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.update) {\n      this.astGeneric(forNode.update, updateArr);\n    } else {\n      isSafe = false;\n    }\n\n    if (forNode.body) {\n      this.pushState('loop-body');\n      this.astGeneric(forNode.body, bodyArr);\n      this.popState('loop-body');\n    }\n\n    // have all parts, now make them safe\n    if (isSafe === null) {\n      isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test);\n    }\n\n    if (isSafe) {\n      const initString = initArr.join('');\n      const initNeedsSemiColon = initString[initString.length - 1] !== ';';\n      retArr.push(`for (${initString}${initNeedsSemiColon ? ';' : ''}${testArr.join('')};${updateArr.join('')}){\\n`);\n      retArr.push(bodyArr.join(''));\n      retArr.push('}\\n');\n    } else {\n      const iVariableName = this.getInternalVariableName('safeI');\n      if (initArr.length > 0) {\n        retArr.push(initArr.join(''), '\\n');\n      }\n      retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n      if (testArr.length > 0) {\n        retArr.push(`if (!${testArr.join('')}) break;\\n`);\n      }\n      retArr.push(bodyArr.join(''));\n      retArr.push(`\\n${updateArr.join('')};`);\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *while* loop\n   * @param {Object} whileNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed webgl string\n   */\n  astWhileStatement(whileNode, retArr) {\n    if (whileNode.type !== 'WhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', whileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    retArr.push('if (!');\n    this.astGeneric(whileNode.test, retArr);\n    retArr.push(') break;\\n');\n    this.astGeneric(whileNode.body, retArr);\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *do while* loop\n   * @param {Object} doWhileNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the parsed webgl string\n   */\n  astDoWhileStatement(doWhileNode, retArr) {\n    if (doWhileNode.type !== 'DoWhileStatement') {\n      throw this.astErrorOutput('Invalid while statement', doWhileNode);\n    }\n\n    const iVariableName = this.getInternalVariableName('safeI');\n    retArr.push(`for (int ${iVariableName}=0;${iVariableName}<LOOP_MAX;${iVariableName}++){\\n`);\n    this.astGeneric(doWhileNode.body, retArr);\n    retArr.push('if (!');\n    this.astGeneric(doWhileNode.test, retArr);\n    retArr.push(') break;\\n');\n    retArr.push('}\\n');\n\n    return retArr;\n  }\n\n\n  /**\n   * @desc Parses the abstract syntax tree for *Assignment* Expression\n   * @param {Object} assNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astAssignmentExpression(assNode, retArr) {\n    // TODO: casting needs implemented here\n    if (assNode.operator === '%=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('mod(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else if (assNode.operator === '**=') {\n      this.astGeneric(assNode.left, retArr);\n      retArr.push('=');\n      retArr.push('pow(');\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(',');\n      this.astGeneric(assNode.right, retArr);\n      retArr.push(')');\n    } else {\n      const leftType = this.getType(assNode.left);\n      const rightType = this.getType(assNode.right);\n      this.astGeneric(assNode.left, retArr);\n      retArr.push(assNode.operator);\n      if (leftType !== 'Integer' && rightType === 'Integer') {\n        retArr.push('float(');\n        this.astGeneric(assNode.right, retArr);\n        retArr.push(')');\n      } else {\n        this.astGeneric(assNode.right, retArr);\n      }\n      return retArr;\n    }\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Block* statement\n   * @param {Object} bNode - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astBlockStatement(bNode, retArr) {\n    if (this.isState('loop-body')) {\n      this.pushState('block-body'); // this prevents recursive removal of braces\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      this.popState('block-body');\n    } else {\n      retArr.push('{\\n');\n      for (let i = 0; i < bNode.body.length; i++) {\n        this.astGeneric(bNode.body[i], retArr);\n      }\n      retArr.push('}\\n');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Variable Declaration*\n   * @param {Object} varDecNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astVariableDeclaration(varDecNode, retArr) {\n    const declarations = varDecNode.declarations;\n    if (!declarations || !declarations[0] || !declarations[0].init) {\n      throw this.astErrorOutput('Unexpected expression', varDecNode);\n    }\n    const result = [];\n    let lastType = null;\n    const declarationSets = [];\n    let declarationSet = [];\n    for (let i = 0; i < declarations.length; i++) {\n      const declaration = declarations[i];\n      const init = declaration.init;\n      const info = this.getDeclaration(declaration.id);\n      const actualType = this.getType(declaration.init);\n      let type = actualType;\n      if (type === 'LiteralInteger') {\n        if (info.suggestedType === 'Integer') {\n          type = 'Integer';\n        } else {\n          // We had the choice to go either float or int, choosing float\n          type = 'Number';\n        }\n      }\n      const markupType = typeMap[type];\n      if (!markupType) {\n        throw this.astErrorOutput(`Markup type ${ type } not handled`, varDecNode);\n      }\n      const declarationResult = [];\n      if (actualType === 'Integer' && type === 'Integer') {\n        // Since we are assigning to a float, ensure valueType is reset to that\n        info.valueType = 'Number';\n        if (i === 0 || lastType === null) {\n          declarationResult.push('float ');\n        } else if (type !== lastType) {\n          throw new Error('Unhandled declaration');\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        declarationResult.push('float(');\n        this.astGeneric(init, declarationResult);\n        declarationResult.push(')');\n      } else {\n        // Since we are assigning to a float, ensure valueType is reset to that\n        info.valueType = type;\n        if (i === 0 || lastType === null) {\n          declarationResult.push(`${markupType} `);\n        } else if (type !== lastType) {\n          declarationSets.push(declarationSet.join(','));\n          declarationSet = [];\n          declarationResult.push(`${markupType} `);\n        }\n        lastType = type;\n        declarationResult.push(`user_${utils.sanitizeName(declaration.id.name)}=`);\n        if (actualType === 'Number' && type === 'Integer') {\n          if (init.left && init.left.type === 'Literal') {\n            this.astGeneric(init, declarationResult);\n          } else {\n            declarationResult.push('int(');\n            this.astGeneric(init, declarationResult);\n            declarationResult.push(')');\n          }\n        } else if (actualType === 'LiteralInteger' && type === 'Integer') {\n          this.castLiteralToInteger(init, declarationResult);\n        } else {\n          this.astGeneric(init, declarationResult);\n        }\n      }\n      declarationSet.push(declarationResult.join(''));\n    }\n\n    if (declarationSet.length > 0) {\n      declarationSets.push(declarationSet.join(','));\n    }\n\n    result.push(declarationSets.join(';'));\n\n    retArr.push(result.join(''));\n    retArr.push(';');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *If* Statement\n   * @param {Object} ifNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astIfStatement(ifNode, retArr) {\n    retArr.push('if (');\n    this.astGeneric(ifNode.test, retArr);\n    retArr.push(')');\n    if (ifNode.consequent.type === 'BlockStatement') {\n      this.astGeneric(ifNode.consequent, retArr);\n    } else {\n      retArr.push(' {\\n');\n      this.astGeneric(ifNode.consequent, retArr);\n      retArr.push('\\n}\\n');\n    }\n\n    if (ifNode.alternate) {\n      retArr.push('else ');\n      if (ifNode.alternate.type === 'BlockStatement' || ifNode.alternate.type === 'IfStatement') {\n        this.astGeneric(ifNode.alternate, retArr);\n      } else {\n        retArr.push(' {\\n');\n        this.astGeneric(ifNode.alternate, retArr);\n        retArr.push('\\n}\\n');\n      }\n    }\n    return retArr;\n  }\n\n  astSwitchStatement(ast, retArr) {\n    if (ast.type !== 'SwitchStatement') {\n      throw this.astErrorOutput('Invalid switch statement', ast);\n    }\n    const { discriminant, cases } = ast;\n    const type = this.getType(discriminant);\n    const varName = `switchDiscriminant${this.astKey(ast, '_')}`;\n    switch (type) {\n      case 'Float':\n      case 'Number':\n        retArr.push(`float ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n      case 'Integer':\n        retArr.push(`int ${varName} = `);\n        this.astGeneric(discriminant, retArr);\n        retArr.push(';\\n');\n        break;\n    }\n    // switch with just a default:\n    if (cases.length === 1 && !cases[0].test) {\n      this.astGeneric(cases[0].consequent, retArr);\n      return retArr;\n    }\n\n    // regular switches:\n    let fallingThrough = false;\n    let defaultResult = [];\n    let movingDefaultToEnd = false;\n    let pastFirstIf = false;\n    for (let i = 0; i < cases.length; i++) {\n      // default\n      if (!cases[i].test) {\n        if (cases.length > i + 1) {\n          movingDefaultToEnd = true;\n          this.astGeneric(cases[i].consequent, defaultResult);\n          continue;\n        } else {\n          retArr.push(' else {\\n');\n        }\n      } else {\n        // all others\n        if (i === 0 || !pastFirstIf) {\n          pastFirstIf = true;\n          retArr.push(`if (${varName} == `);\n        } else {\n          if (fallingThrough) {\n            retArr.push(`${varName} == `);\n            fallingThrough = false;\n          } else {\n            retArr.push(` else if (${varName} == `);\n          }\n        }\n        if (type === 'Integer') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'Number':\n            case 'Float':\n              this.castValueToInteger(cases[i].test, retArr);\n              break;\n            case 'LiteralInteger':\n              this.castLiteralToInteger(cases[i].test, retArr);\n              break;\n          }\n        } else if (type === 'Float') {\n          const testType = this.getType(cases[i].test);\n          switch (testType) {\n            case 'LiteralInteger':\n              this.castLiteralToFloat(cases[i].test, retArr);\n              break;\n            case 'Integer':\n              this.castValueToFloat(cases[i].test, retArr);\n              break;\n          }\n        } else {\n          throw new Error('unhanlded');\n        }\n        if (!cases[i].consequent || cases[i].consequent.length === 0) {\n          fallingThrough = true;\n          retArr.push(' || ');\n          continue;\n        }\n        retArr.push(`) {\\n`);\n      }\n      this.astGeneric(cases[i].consequent, retArr);\n      retArr.push('\\n}');\n    }\n    if (movingDefaultToEnd) {\n      retArr.push(' else {');\n      retArr.push(defaultResult.join(''));\n      retArr.push('}');\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *This* expression\n   * @param {Object} tNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astThisExpression(tNode, retArr) {\n    retArr.push('this');\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Member* Expression\n   * @param {Object} mNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astMemberExpression(mNode, retArr) {\n    const {\n      property,\n      name,\n      signature,\n      origin,\n      type,\n      xProperty,\n      yProperty,\n      zProperty\n    } = this.getMemberExpressionDetails(mNode);\n    switch (signature) {\n      case 'value.thread.value':\n      case 'this.thread.value':\n        if (name !== 'x' && name !== 'y' && name !== 'z') {\n          throw this.astErrorOutput('Unexpected expression, expected `this.thread.x`, `this.thread.y`, or `this.thread.z`', mNode);\n        }\n        retArr.push(`threadId.${name}`);\n        return retArr;\n      case 'this.output.value':\n        if (this.dynamicOutput) {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.x)');\n              } else {\n                retArr.push('uOutputDim.x');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.y)');\n              } else {\n                retArr.push('uOutputDim.y');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-float')) {\n                retArr.push('float(uOutputDim.z)');\n              } else {\n                retArr.push('uOutputDim.z');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        } else {\n          switch (name) {\n            case 'x':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[0]);\n              } else {\n                retArr.push(this.output[0], '.0');\n              }\n              break;\n            case 'y':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[1]);\n              } else {\n                retArr.push(this.output[1], '.0');\n              }\n              break;\n            case 'z':\n              if (this.isState('casting-to-integer')) {\n                retArr.push(this.output[2]);\n              } else {\n                retArr.push(this.output[2], '.0');\n              }\n              break;\n            default:\n              throw this.astErrorOutput('Unexpected expression', mNode);\n          }\n        }\n        return retArr;\n      case 'value':\n        throw this.astErrorOutput('Unexpected expression', mNode);\n      case 'value[]':\n      case 'value[][]':\n      case 'value[][][]':\n      case 'value[][][][]':\n      case 'value.value':\n        if (origin === 'Math') {\n          retArr.push(Math[name]);\n          return retArr;\n        }\n        const cleanName = utils.sanitizeName(name);\n        switch (property) {\n          case 'r':\n            retArr.push(`user_${ cleanName }.r`);\n            return retArr;\n          case 'g':\n            retArr.push(`user_${ cleanName }.g`);\n            return retArr;\n          case 'b':\n            retArr.push(`user_${ cleanName }.b`);\n            return retArr;\n          case 'a':\n            retArr.push(`user_${ cleanName }.a`);\n            return retArr;\n        }\n        break;\n      case 'this.constants.value':\n        if (typeof xProperty === 'undefined') {\n          switch (type) {\n            case 'Array(2)':\n            case 'Array(3)':\n            case 'Array(4)':\n              retArr.push(`constants_${ utils.sanitizeName(name) }`);\n              return retArr;\n          }\n        }\n      case 'this.constants.value[]':\n      case 'this.constants.value[][]':\n      case 'this.constants.value[][][]':\n      case 'this.constants.value[][][][]':\n        break;\n      case 'fn()[]':\n        this.astCallExpression(mNode.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(property));\n        retArr.push(']');\n        return retArr;\n      case 'fn()[][]':\n        this.astCallExpression(mNode.object.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(mNode.object.property));\n        retArr.push(']');\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(mNode.property));\n        retArr.push(']');\n        return retArr;\n      case '[][]':\n        this.astArrayExpression(mNode.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(property));\n        retArr.push(']');\n        return retArr;\n      default:\n        throw this.astErrorOutput('Unexpected expression', mNode);\n    }\n\n    if (mNode.computed === false) {\n      // handle simple types\n      switch (type) {\n        case 'Number':\n        case 'Integer':\n        case 'Float':\n        case 'Boolean':\n          retArr.push(`${origin}_${utils.sanitizeName(name)}`);\n          return retArr;\n      }\n    }\n\n    // handle more complex types\n    // argument may have come from a parent\n    const markupName = `${origin}_${utils.sanitizeName(name)}`;\n\n    switch (type) {\n      case 'Array(2)':\n      case 'Array(3)':\n      case 'Array(4)':\n        // Get from local vec4\n        this.astGeneric(mNode.object, retArr);\n        retArr.push('[');\n        retArr.push(this.memberExpressionPropertyMarkup(xProperty));\n        retArr.push(']');\n        break;\n      case 'HTMLImageArray':\n        retArr.push(`getImage3D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(1)':\n        retArr.push(`getFloatFromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(2)':\n      case 'Array2D(2)':\n      case 'Array3D(2)':\n        retArr.push(`getMemoryOptimizedVec2(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(2)':\n        retArr.push(`getVec2FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(3)':\n      case 'Array2D(3)':\n      case 'Array3D(3)':\n        retArr.push(`getMemoryOptimizedVec3(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(3)':\n        retArr.push(`getVec3FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Array1D(4)':\n      case 'Array2D(4)':\n      case 'Array3D(4)':\n        retArr.push(`getMemoryOptimizedVec4(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'ArrayTexture(4)':\n      case 'HTMLCanvas':\n      case 'OffscreenCanvas':\n      case 'HTMLImage':\n      case 'ImageBitmap':\n      case 'ImageData':\n      case 'HTMLVideo':\n        retArr.push(`getVec4FromSampler2D(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'NumberTexture':\n      case 'Array':\n      case 'Array2D':\n      case 'Array3D':\n      case 'Array4D':\n      case 'Input':\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        if (this.precision === 'single') {\n          // bitRatio is always 4 here, javascript doesn't yet have 8 or 16 bit support\n          // TODO: make 8 or 16 bit work anyway!\n          retArr.push(`getMemoryOptimized32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        } else {\n          const bitRatio = (origin === 'user' ?\n            this.lookupFunctionArgumentBitRatio(this.name, name) :\n            this.constantBitRatios[name]\n          );\n          switch (bitRatio) {\n            case 1:\n              retArr.push(`get8(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 2:\n              retArr.push(`get16(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            case 4:\n            case 0:\n              retArr.push(`get32(${markupName}, ${markupName}Size, ${markupName}Dim, `);\n              break;\n            default:\n              throw new Error(`unhandled bit ratio of ${bitRatio}`);\n          }\n          this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n          retArr.push(')');\n        }\n        break;\n      case 'MemoryOptimizedNumberTexture':\n        retArr.push(`getMemoryOptimized32(${ markupName }, ${ markupName }Size, ${ markupName }Dim, `);\n        this.memberExpressionXYZ(xProperty, yProperty, zProperty, retArr);\n        retArr.push(')');\n        break;\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`${markupName}[${this.memberExpressionPropertyMarkup(yProperty)}]`);\n        if (yProperty) {\n          retArr.push(`[${this.memberExpressionPropertyMarkup(xProperty)}]`);\n        }\n        break;\n      default:\n        throw new Error(`unhandled member expression \"${ type }\"`);\n    }\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *call* expression\n   * @param {Object} ast - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns  {Array} the append retArr\n   */\n  astCallExpression(ast, retArr) {\n    if (!ast.callee) {\n      throw this.astErrorOutput('Unknown CallExpression', ast);\n    }\n\n    let functionName = null;\n    const isMathFunction = this.isAstMathFunction(ast);\n\n    // Its a math operator or this.something(), remove the prefix\n    if (isMathFunction || (ast.callee.object && ast.callee.object.type === 'ThisExpression')) {\n      functionName = ast.callee.property.name;\n    }\n    // Issue #212, BABEL!\n    else if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[0].type === 'Literal' && !isNaN(ast.callee.expressions[0].raw)) {\n      functionName = ast.callee.expressions[1].property.name;\n    } else {\n      functionName = ast.callee.name;\n    }\n\n    if (!functionName) {\n      throw this.astErrorOutput(`Unhandled function, couldn't find name`, ast);\n    }\n\n    // if this if grows to more than one, lets use a switch\n    switch (functionName) {\n      case 'pow':\n        functionName = '_pow';\n        break;\n      case 'round':\n        functionName = '_round';\n        break;\n    }\n\n    // Register the function into the called registry\n    if (this.calledFunctions.indexOf(functionName) < 0) {\n      this.calledFunctions.push(functionName);\n    }\n\n    if (functionName === 'random' && this.plugins && this.plugins.length > 0) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.functionMatch === 'Math.random()' && plugin.functionReplace) {\n          retArr.push(plugin.functionReplace);\n          return retArr;\n        }\n      }\n    }\n\n    // track the function was called\n    if (this.onFunctionCall) {\n      this.onFunctionCall(this.name, functionName, ast.arguments);\n    }\n\n    // Call the function\n    retArr.push(functionName);\n\n    // Open arguments space\n    retArr.push('(');\n\n    // Add the arguments\n    if (isMathFunction) {\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        const argumentType = this.getType(argument);\n        if (i > 0) {\n          retArr.push(', ');\n        }\n\n        switch (argumentType) {\n          case 'Integer':\n            this.castValueToFloat(argument, retArr);\n            break;\n          default:\n            this.astGeneric(argument, retArr);\n            break;\n        }\n      }\n    } else {\n      const targetTypes = this.lookupFunctionArgumentTypes(functionName) || [];\n      for (let i = 0; i < ast.arguments.length; ++i) {\n        const argument = ast.arguments[i];\n        let targetType = targetTypes[i];\n        if (i > 0) {\n          retArr.push(', ');\n        }\n        const argumentType = this.getType(argument);\n        if (!targetType) {\n          this.triggerImplyArgumentType(functionName, i, argumentType, this);\n          targetType = argumentType;\n        }\n        switch (argumentType) {\n          case 'Boolean':\n            this.astGeneric(argument, retArr);\n            continue;\n          case 'Number':\n          case 'Float':\n            if (targetType === 'Integer') {\n              retArr.push('int(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.astGeneric(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Integer':\n            if (targetType === 'Number' || targetType === 'Float') {\n              retArr.push('float(');\n              this.astGeneric(argument, retArr);\n              retArr.push(')');\n              continue;\n            } else if (targetType === 'Integer') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'LiteralInteger':\n            if (targetType === 'Integer') {\n              this.castLiteralToInteger(argument, retArr);\n              continue;\n            } else if (targetType === 'Number' || targetType === 'Float') {\n              this.castLiteralToFloat(argument, retArr);\n              continue;\n            } else if (targetType === 'LiteralInteger') {\n              this.astGeneric(argument, retArr);\n              continue;\n            }\n            break;\n          case 'Array(2)':\n          case 'Array(3)':\n          case 'Array(4)':\n            if (targetType === argumentType) {\n              if (argument.type === 'Identifier') {\n                retArr.push(`user_${utils.sanitizeName(argument.name)}`);\n              } else if (argument.type === 'ArrayExpression' || argument.type === 'MemberExpression' || argument.type === 'CallExpression') {\n                this.astGeneric(argument, retArr);\n              } else {\n                throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              }\n              continue;\n            }\n            break;\n          case 'HTMLCanvas':\n          case 'OffscreenCanvas':\n          case 'HTMLImage':\n          case 'ImageBitmap':\n          case 'ImageData':\n          case 'HTMLImageArray':\n          case 'HTMLVideo':\n          case 'ArrayTexture(1)':\n          case 'ArrayTexture(2)':\n          case 'ArrayTexture(3)':\n          case 'ArrayTexture(4)':\n          case 'Array':\n          case 'Input':\n            if (targetType === argumentType) {\n              if (argument.type !== 'Identifier') throw this.astErrorOutput(`Unhandled argument type ${ argument.type }`, ast);\n              this.triggerImplyArgumentBitRatio(this.name, argument.name, functionName, i);\n              const name = utils.sanitizeName(argument.name);\n              retArr.push(`user_${name},user_${name}Size,user_${name}Dim`);\n              continue;\n            }\n            break;\n        }\n        throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType } for argument named \"${ argument.name }\"`, ast);\n      }\n    }\n    // Close arguments space\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  /**\n   * @desc Parses the abstract syntax tree for *Array* Expression\n   * @param {Object} arrNode - the AST object to parse\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astArrayExpression(arrNode, retArr) {\n    const returnType = this.getType(arrNode);\n\n    const arrLen = arrNode.elements.length;\n\n    switch (returnType) {\n      case 'Matrix(2)':\n      case 'Matrix(3)':\n      case 'Matrix(4)':\n        retArr.push(`mat${arrLen}(`);\n        break;\n      default:\n        retArr.push(`vec${arrLen}(`);\n    }\n    for (let i = 0; i < arrLen; ++i) {\n      if (i > 0) {\n        retArr.push(', ');\n      }\n      const subNode = arrNode.elements[i];\n      this.astGeneric(subNode, retArr)\n    }\n    retArr.push(')');\n\n    return retArr;\n  }\n\n  memberExpressionXYZ(x, y, z, retArr) {\n    if (z) {\n      retArr.push(this.memberExpressionPropertyMarkup(z), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    if (y) {\n      retArr.push(this.memberExpressionPropertyMarkup(y), ', ');\n    } else {\n      retArr.push('0, ');\n    }\n    retArr.push(this.memberExpressionPropertyMarkup(x));\n    return retArr;\n  }\n\n  memberExpressionPropertyMarkup(property) {\n    if (!property) {\n      throw new Error('Property not set');\n    }\n    const type = this.getType(property);\n    const result = [];\n    switch (type) {\n      case 'Number':\n      case 'Float':\n        this.castValueToInteger(property, result);\n        break;\n      case 'LiteralInteger':\n        this.castLiteralToInteger(property, result);\n        break;\n      default:\n        this.astGeneric(property, result);\n    }\n    return result.join('');\n  }\n}\n\nconst typeMap = {\n  'Array': 'sampler2D',\n  'Array(2)': 'vec2',\n  'Array(3)': 'vec3',\n  'Array(4)': 'vec4',\n  'Matrix(2)': 'mat2',\n  'Matrix(3)': 'mat3',\n  'Matrix(4)': 'mat4',\n  'Array2D': 'sampler2D',\n  'Array3D': 'sampler2D',\n  'Boolean': 'bool',\n  'Float': 'float',\n  'Input': 'sampler2D',\n  'Integer': 'int',\n  'Number': 'float',\n  'LiteralInteger': 'float',\n  'NumberTexture': 'sampler2D',\n  'MemoryOptimizedNumberTexture': 'sampler2D',\n  'ArrayTexture(1)': 'sampler2D',\n  'ArrayTexture(2)': 'sampler2D',\n  'ArrayTexture(3)': 'sampler2D',\n  'ArrayTexture(4)': 'sampler2D',\n  'HTMLVideo': 'sampler2D',\n  'HTMLCanvas': 'sampler2D',\n  'OffscreenCanvas': 'sampler2D',\n  'HTMLImage': 'sampler2D',\n  'ImageBitmap': 'sampler2D',\n  'ImageData': 'sampler2D',\n  'HTMLImageArray': 'sampler2DArray',\n};\n\nconst operatorMap = {\n  '===': '==',\n  '!==': '!='\n};\n\nmodule.exports = {\n  WebGLFunctionNode\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/array.js",
    "content": "const { WebGLKernelValue } = require('./index');\nconst { Input } = require('../../../input');\n\n/**\n * @abstract\n */\nclass WebGLKernelArray extends WebGLKernelValue {\n  /**\n   *\n   * @param {number} width\n   * @param {number} height\n   */\n  checkSize(width, height) {\n    if (!this.kernel.validate) return;\n    const { maxTextureSize } = this.kernel.constructor.features;\n    if (width > maxTextureSize || height > maxTextureSize) {\n      if (width > height) {\n        throw new Error(`Argument texture width of ${width} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else if (width < height) {\n        throw new Error(`Argument texture height of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      } else {\n        throw new Error(`Argument texture height and width of ${height} larger than maximum size of ${maxTextureSize} for your GPU`);\n      }\n    }\n  }\n\n  setup() {\n    this.requestTexture();\n    this.setupTexture();\n    this.defineTexture();\n  }\n\n  requestTexture() {\n    this.texture = this.onRequestTexture();\n  }\n\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n  }\n\n  setupTexture() {\n    this.contextHandle = this.onRequestContextHandle();\n    this.index = this.onRequestIndex();\n    this.dimensionsId = this.id + 'Dim';\n    this.sizeId = this.id + 'Size';\n  }\n\n  /**\n   * bit storage ratio of source to target 'buffer', i.e. if 8bit array -> 32bit tex = 4\n   * @param value\n   * @returns {number}\n   */\n  getBitRatio(value) {\n    if (Array.isArray(value[0])) {\n      return this.getBitRatio(value[0]);\n    } else if (value.constructor === Input) {\n      return this.getBitRatio(value.value);\n    }\n    switch (value.constructor) {\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Int8Array:\n        return 1;\n      case Uint16Array:\n      case Int16Array:\n        return 2;\n      case Float32Array:\n      case Int32Array:\n      default:\n        return 4;\n    }\n  }\n\n  destroy() {\n    if (this.prevArg) {\n      this.prevArg.delete();\n    }\n    this.context.deleteTexture(this.texture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelArray\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/array2.js",
    "content": "const { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray2 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec2 ${this.id} = vec2(${value[0]},${value[1]});\\n`;\n    }\n    return `uniform vec2 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    // resetting isn't supported for Array(2)\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform2fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray2\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/array3.js",
    "content": "const { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray3 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec3 ${this.id} = vec3(${value[0]},${value[1]},${value[2]});\\n`;\n    }\n    return `uniform vec3 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    // resetting isn't supported for Array(3)\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform3fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray3\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/array4.js",
    "content": "const { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueArray4 extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const vec4 ${this.id} = vec4(${value[0]},${value[1]},${value[2]},${value[3]});\\n`;\n    }\n    return `uniform vec4 ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    // resetting isn't supported for Array(4)\n    if (this.origin === 'constants') return '';\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform4fv(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueArray4\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/boolean.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueBoolean extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const bool ${this.id} = ${value};\\n`;\n    }\n    return `uniform bool ${this.id};\\n`;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueBoolean\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-html-image.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueDynamicHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLImage\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-html-video.js",
    "content": "const { WebGLKernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGLKernelValueDynamicHTMLVideo extends WebGLKernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueDynamicHTMLVideo\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-memory-optimized-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(inputTexture) {\n    this.dimensions = inputTexture.dimensions;\n    this.checkSize(inputTexture.size[0], inputTexture.size[1]);\n    this.textureSize = inputTexture.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(inputTexture);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicMemoryOptimizedNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('./number-texture');\n\nclass WebGLKernelValueDynamicNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = value.dimensions;\n    this.checkSize(value.size[0], value.size[1]);\n    this.textureSize = value.size;\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-single-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('./single-array');\n\nclass WebGLKernelValueDynamicSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-single-array1d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('./single-array1d-i');\n\nclass WebGLKernelValueDynamicSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray1DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-single-array2d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('./single-array2d-i');\n\nclass WebGLKernelValueDynamicSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray2DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-single-array3d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('./single-array3d-i');\n\nclass WebGLKernelValueDynamicSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleArray3DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-single-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('./single-input');\n\nclass WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicSingleInput\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-unsigned-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('./unsigned-array');\n\nclass WebGLKernelValueDynamicUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedArray\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/dynamic-unsigned-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('./unsigned-input');\n\nclass WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    const Type = this.getTransferArrayType(value.value);\n    this.preUploadValue = new Type(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueDynamicUnsignedInput\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/float.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueFloat extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      if (Number.isInteger(value)) {\n        return `const float ${this.id} = ${value}.0;\\n`;\n      }\n      return `const float ${this.id} = ${value};\\n`;\n    }\n    return `uniform float ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1f(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueFloat\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/html-image.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueHTMLImage extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const { width, height } = value;\n    this.checkSize(width, height);\n    this.dimensions = [width, height, 1];\n    this.textureSize = [width, height];\n    this.uploadValue = value;\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(inputImage) {\n    if (inputImage.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputImage.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue = inputImage);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueHTMLImage\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/html-video.js",
    "content": "const { WebGLKernelValueHTMLImage } = require('./html-image');\n\nclass WebGLKernelValueHTMLVideo extends WebGLKernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGLKernelValueHTMLVideo\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/index.js",
    "content": "const { utils } = require('../../../utils');\nconst { KernelValue } = require('../../kernel-value');\n\nclass WebGLKernelValue extends KernelValue {\n  /**\n   * @param {KernelVariable} value\n   * @param {IWebGLKernelValueSettings} settings\n   */\n  constructor(value, settings) {\n    super(value, settings);\n    this.dimensionsId = null;\n    this.sizeId = null;\n    this.initialValueConstructor = value.constructor;\n    this.onRequestTexture = settings.onRequestTexture;\n    this.onRequestIndex = settings.onRequestIndex;\n    this.uploadValue = null;\n    this.textureSize = null;\n    this.bitRatio = null;\n    this.prevArg = null;\n  }\n\n  get id() {\n    return `${this.origin}_${utils.sanitizeName(this.name)}`;\n  }\n\n  setup() {}\n\n  getTransferArrayType(value) {\n    if (Array.isArray(value[0])) {\n      return this.getTransferArrayType(value[0]);\n    }\n    switch (value.constructor) {\n      case Array:\n      case Int32Array:\n      case Int16Array:\n      case Int8Array:\n        return Float32Array;\n      case Uint8ClampedArray:\n      case Uint8Array:\n      case Uint16Array:\n      case Uint32Array:\n      case Float32Array:\n      case Float64Array:\n        return value.constructor;\n    }\n    console.warn('Unfamiliar constructor type.  Will go ahead and use, but likley this may result in a transfer of zeros');\n    return value.constructor;\n  }\n\n  /**\n   * Used for when we want a string output of our kernel, so we can still input values to the kernel\n   */\n  getStringValueHandler() {\n    throw new Error(`\"getStringValueHandler\" not implemented on ${this.constructor.name}`);\n  }\n\n  getVariablePrecisionString() {\n    return this.kernel.getVariablePrecisionString(this.textureSize || undefined, this.tactic || undefined);\n  }\n\n  destroy() {}\n}\n\nmodule.exports = {\n  WebGLKernelValue\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/integer.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValue } = require('./index');\n\nclass WebGLKernelValueInteger extends WebGLKernelValue {\n  constructor(value, settings) {\n    super(value, settings);\n    this.uploadValue = value;\n  }\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource(value) {\n    if (this.origin === 'constants') {\n      return `const int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueInteger\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/memory-optimized-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nconst sameError = `Source and destination textures are the same.  Use immutable = true and manually cleanup kernel output texture memory with texture.delete()`;\n\nclass WebGLKernelValueMemoryOptimizedNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    this.dimensions = value.dimensions;\n    this.textureSize = value.size;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  /**\n   * @param {GLTextureMemoryOptimized} inputTexture\n   */\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueMemoryOptimizedNumberTexture,\n  sameError\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\nconst { sameError } = require('./memory-optimized-number-texture');\n\nclass WebGLKernelValueNumberTexture extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    const [width, height] = value.size;\n    this.checkSize(width, height);\n    const { size: textureSize, dimensions } = value;\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = dimensions;\n    this.textureSize = textureSize;\n    this.uploadValue = value.texture;\n    this.forceUploadEachRun = true;\n  }\n\n  setup() {\n    this.setupTexture();\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName}.texture;\\n`;\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  /**\n   *\n   * @param {GLTexture} inputTexture\n   */\n  updateValue(inputTexture) {\n    if (inputTexture.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(inputTexture.constructor);\n      return;\n    }\n    if (this.checkContext && inputTexture.context !== this.context) {\n      throw new Error(`Value ${this.name} (${this.type}) must be from same context`);\n    }\n\n    const { kernel, context: gl } = this;\n    if (kernel.pipeline) {\n      if (kernel.immutable) {\n        kernel.updateTextureArgumentRefs(this, inputTexture);\n      } else {\n        if (kernel.texture && kernel.texture.texture === inputTexture.texture) {\n          throw new Error(sameError);\n        } else if (kernel.mappedTextures) {\n          const { mappedTextures } = kernel;\n          for (let i = 0; i < mappedTextures.length; i++) {\n            if (mappedTextures[i].texture === inputTexture.texture) {\n              throw new Error(sameError);\n            }\n          }\n        }\n      }\n    }\n\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/single-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/single-array1d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray1DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], 1, 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten2dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray1DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/single-array2d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray2DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], 1]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten3dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray2DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/single-array3d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleArray3DI extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    this.setShape(value);\n  }\n\n  setShape(value) {\n    const valueDimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(valueDimensions, this.bitRatio);\n    this.dimensions = new Int32Array([valueDimensions[1], valueDimensions[2], valueDimensions[3]]);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flatten4dArrayTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleArray3DI\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/single-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueSingleInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = 4;\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`,\n      `flattenTo(${this.varName}.value, uploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(input.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueSingleInput\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/unsigned-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedArray\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value/unsigned-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('./array');\n\nclass WebGLKernelValueUnsignedInput extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.bitRatio = this.getBitRatio(value);\n    const [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio);\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.TranserArrayType = this.getTransferArrayType(value.value);\n    this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength);\n    this.uploadValue = new Uint8Array(this.preUploadValue.buffer);\n  }\n\n  getStringValueHandler() {\n    return utils.linesToString([\n      `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`,\n      `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`,\n      `flattenTo(${this.varName}.value, preUploadValue_${this.name})`,\n    ]);\n  }\n\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    if (input.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.preUploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGLKernelValueUnsignedInput\n};"
  },
  {
    "path": "src/backend/web-gl/kernel-value-maps.js",
    "content": "const { WebGLKernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGLKernelValueFloat } = require('./kernel-value/float');\nconst { WebGLKernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGLKernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGLKernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGLKernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGLKernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGLKernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGLKernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGLKernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGLKernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGLKernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGLKernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGLKernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGLKernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGLKernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGLKernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGLKernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGLKernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGLKernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGLKernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGLKernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGLKernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGLKernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueUnsignedArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGLKernelValueUnsignedInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Integer': WebGLKernelValueInteger,\n      'Float': WebGLKernelValueFloat,\n      'Array': WebGLKernelValueDynamicSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueDynamicSingleArray3DI,\n      'Input': WebGLKernelValueDynamicSingleInput,\n      'NumberTexture': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImage': WebGLKernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGLKernelValueDynamicHTMLImage,\n      'ImageData': WebGLKernelValueDynamicHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGLKernelValueBoolean,\n      'Float': WebGLKernelValueFloat,\n      'Integer': WebGLKernelValueInteger,\n      'Array': WebGLKernelValueSingleArray,\n      'Array(2)': WebGLKernelValueArray2,\n      'Array(3)': WebGLKernelValueArray3,\n      'Array(4)': WebGLKernelValueArray4,\n      'Array1D(2)': WebGLKernelValueSingleArray1DI,\n      'Array1D(3)': WebGLKernelValueSingleArray1DI,\n      'Array1D(4)': WebGLKernelValueSingleArray1DI,\n      'Array2D(2)': WebGLKernelValueSingleArray2DI,\n      'Array2D(3)': WebGLKernelValueSingleArray2DI,\n      'Array2D(4)': WebGLKernelValueSingleArray2DI,\n      'Array3D(2)': WebGLKernelValueSingleArray3DI,\n      'Array3D(3)': WebGLKernelValueSingleArray3DI,\n      'Array3D(4)': WebGLKernelValueSingleArray3DI,\n      'Input': WebGLKernelValueSingleInput,\n      'NumberTexture': WebGLKernelValueNumberTexture,\n      'ArrayTexture(1)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(2)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(3)': WebGLKernelValueNumberTexture,\n      'ArrayTexture(4)': WebGLKernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGLKernelValueHTMLImage,\n      'OffscreenCanvas': WebGLKernelValueHTMLImage,\n      'HTMLImage': WebGLKernelValueHTMLImage,\n      'ImageBitmap': WebGLKernelValueHTMLImage,\n      'ImageData': WebGLKernelValueHTMLImage,\n      'HTMLImageArray': false,\n      'HTMLVideo': WebGLKernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  lookupKernelValueType,\n  kernelValueMaps,\n};"
  },
  {
    "path": "src/backend/web-gl/kernel.js",
    "content": "const { GLKernel } = require('../gl/kernel');\nconst { FunctionBuilder } = require('../function-builder');\nconst { WebGLFunctionNode } = require('./function-node');\nconst { utils } = require('../../utils');\nconst mrud = require('../../plugins/math-random-uniformly-distributed');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { glKernelString } = require('../gl/kernel-string');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\n/**\n *\n * @type {HTMLCanvasElement|OffscreenCanvas|null}\n */\nlet testCanvas = null;\n/**\n *\n * @type {WebGLRenderingContext|null}\n */\nlet testContext = null;\nlet testExtensions = null;\nlet features = null;\n\nconst plugins = [mrud];\nconst canvases = [];\nconst maxTexSizes = {};\n\n\n/**\n * @desc Kernel Implementation for WebGL.\n * <p>This builds the shaders and runs them on the GPU,\n * the outputs the result back as float(enabled by default) and Texture.</p>\n *\n * @property {WebGLTexture[]} textureCache - webGl Texture cache\n * @property {Object.<string, WebGLUniformLocation>} programUniformLocationCache - Location of program variables in memory\n * @property {WebGLFramebuffer} framebuffer - Webgl frameBuffer\n * @property {WebGLBuffer} buffer - WebGL buffer\n * @property {WebGLProgram} program - The webGl Program\n * @property {FunctionBuilder} functionBuilder - Function Builder instance bound to this Kernel\n * @property {Boolean} pipeline - Set output type to FAST mode (GPU to GPU via Textures), instead of float\n * @property {string} endianness - Endian information like Little-endian, Big-endian.\n * @property {string[]} argumentTypes - Types of parameters sent to the Kernel\n * @property {string|null} compiledFragmentShader - Compiled fragment shader string\n * @property {string|null} compiledVertexShader - Compiled Vertical shader string\n * @extends GLKernel\n */\nclass WebGLKernel extends GLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      OES_texture_float: testContext.getExtension('OES_texture_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: testContext.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    if (typeof WebGLRenderingContext !== 'undefined') {\n      return context instanceof WebGLRenderingContext;\n    }\n    return false;\n  }\n\n  static getIsTextureFloat() {\n    return Boolean(testExtensions.OES_texture_float);\n  }\n\n  static getIsDrawBuffers() {\n    return Boolean(testExtensions.WEBGL_draw_buffers);\n  }\n\n  static getChannelCount() {\n    return testExtensions.WEBGL_draw_buffers ?\n      testContext.getParameter(testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL) :\n      1;\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  /**\n   *\n   * @param type\n   * @param dynamic\n   * @param precision\n   * @param value\n   * @returns {KernelValue}\n   */\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  /**\n   *\n   * @param {String|IKernelJSON} source\n   * @param {IDirectKernelSettings} settings\n   */\n  constructor(source, settings) {\n    super(source, settings);\n    this.program = null;\n    this.pipeline = settings.pipeline;\n    this.endianness = utils.systemEndianness();\n    this.extensions = {};\n    this.argumentTextureCount = 0;\n    this.constantTextureCount = 0;\n    this.fragShader = null;\n    this.vertShader = null;\n    this.drawBuffersMap = null;\n\n    /**\n     *\n     * @type {Int32Array|null}\n     */\n    this.maxTexSize = null;\n    this.onRequestSwitchKernel = null;\n\n    this.texture = null;\n    this.mappedTextures = null;\n    this.mergeSettings(source.settings || settings);\n\n    /**\n     * The thread dimensions, x, y and z\n     * @type {Array|null}\n     */\n    this.threadDim = null;\n    this.framebuffer = null;\n    this.buffer = null;\n\n    this.textureCache = [];\n    this.programUniformLocationCache = {};\n    this.uniform1fCache = {};\n    this.uniform1iCache = {};\n    this.uniform2fCache = {};\n    this.uniform2fvCache = {};\n    this.uniform2ivCache = {};\n    this.uniform3fvCache = {};\n    this.uniform3ivCache = {};\n    this.uniform4fvCache = {};\n    this.uniform4ivCache = {};\n  }\n\n  initCanvas() {\n    if (typeof document !== 'undefined') {\n      const canvas = document.createElement('canvas');\n      // Default width and height, to fix webgl issue in safari\n      canvas.width = 2;\n      canvas.height = 2;\n      return canvas;\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      return new OffscreenCanvas(0, 0);\n    }\n  }\n\n  /**\n   *\n   * @return {WebGLRenderingContext}\n   */\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl', settings) || this.canvas.getContext('experimental-webgl', settings);\n  }\n\n  /**\n   *\n   * @param {IDirectKernelSettings} settings\n   * @return {string[]}\n   */\n  initPlugins(settings) {\n    // default plugins\n    const pluginsToUse = [];\n    const { source } = this;\n    if (typeof source === 'string') {\n      for (let i = 0; i < plugins.length; i++) {\n        const plugin = plugins[i];\n        if (source.match(plugin.functionMatch)) {\n          pluginsToUse.push(plugin);\n        }\n      }\n    } else if (typeof source === 'object') {\n      // `source` is from object, json\n      if (settings.pluginNames) { //TODO: in context of JSON support, pluginNames may not exist here\n        for (let i = 0; i < plugins.length; i++) {\n          const plugin = plugins[i];\n          const usePlugin = settings.pluginNames.some(pluginName => pluginName === plugin.name);\n          if (usePlugin) {\n            pluginsToUse.push(plugin);\n          }\n        }\n      }\n    }\n    return pluginsToUse;\n  }\n\n  initExtensions() {\n    this.extensions = {\n      OES_texture_float: this.context.getExtension('OES_texture_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n      OES_element_index_uint: this.context.getExtension('OES_element_index_uint'),\n      WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'),\n      WEBGL_color_buffer_float: this.context.getExtension('WEBGL_color_buffer_float'),\n    };\n  }\n\n  /**\n   * @desc Validate settings related to Kernel, such as dimensions size, and auto output support.\n   * @param {IArguments} args\n   */\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n\n    if (this.optimizeFloatMemory === true && !features.isTextureFloat) {\n      throw new Error('Float textures are not supported');\n    } else if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Single precision not supported');\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.subKernels && this.subKernels.length > 0 && !this.extensions.WEBGL_draw_buffers) {\n      throw new Error('could not instantiate draw buffers extension');\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'precision') {\n        this.precision = 'unsigned';\n        console.warn('Cannot use graphical mode and single precision at the same time');\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  updateMaxTexSize() {\n    const { texSize, canvas } = this;\n    if (this.maxTexSize === null) {\n      let canvasIndex = canvases.indexOf(canvas);\n      if (canvasIndex === -1) {\n        canvasIndex = canvases.length;\n        canvases.push(canvas);\n        maxTexSizes[canvasIndex] = [texSize[0], texSize[1]];\n      }\n      this.maxTexSize = maxTexSizes[canvasIndex];\n    }\n    if (this.maxTexSize[0] < texSize[0]) {\n      this.maxTexSize[0] = texSize[0];\n    }\n    if (this.maxTexSize[1] < texSize[1]) {\n      this.maxTexSize[1] = texSize[1];\n    }\n  }\n\n  setupArguments(args) {\n    this.kernelArguments = [];\n    this.argumentTextureCount = 0;\n    const needsArgumentTypes = this.argumentTypes === null;\n    // TODO: remove\n    if (needsArgumentTypes) {\n      this.argumentTypes = [];\n    }\n    this.argumentSizes = [];\n    this.argumentBitRatios = [];\n    // TODO: end remove\n\n    if (args.length < this.argumentNames.length) {\n      throw new Error('not enough arguments for kernel');\n    } else if (args.length > this.argumentNames.length) {\n      throw new Error('too many arguments for kernel');\n    }\n\n    const { context: gl } = this;\n    let textureIndexes = 0;\n\n    const onRequestTexture = () => {\n      return this.createTexture();\n    };\n    const onRequestIndex = () => {\n      return this.constantTextureCount + textureIndexes++;\n    };\n    const onUpdateValueMismatch = (constructor) => {\n      this.switchKernels({\n        type: 'argumentMismatch',\n        needed: constructor\n      });\n    };\n    const onRequestContextHandle = () => {\n      return gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount++;\n    };\n\n    for (let index = 0; index < args.length; index++) {\n      const value = args[index];\n      const name = this.argumentNames[index];\n      let type;\n      if (needsArgumentTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.argumentTypes.push(type);\n      } else {\n        type = this.argumentTypes[index];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, this.dynamicArguments ? 'dynamic' : 'static', this.precision, args[index]);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelArgument = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'user',\n        context: gl,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture,\n        onRequestIndex,\n        onUpdateValueMismatch,\n        onRequestContextHandle,\n      });\n      this.kernelArguments.push(kernelArgument);\n      kernelArgument.setup();\n      this.argumentSizes.push(kernelArgument.textureSize);\n      this.argumentBitRatios[index] = kernelArgument.bitRatio;\n    }\n  }\n\n  createTexture() {\n    const texture = this.context.createTexture();\n    this.textureCache.push(texture);\n    return texture;\n  }\n\n  setupConstants(args) {\n    const { context: gl } = this;\n    this.kernelConstants = [];\n    this.forceUploadKernelConstants = [];\n    let needsConstantTypes = this.constantTypes === null;\n    if (needsConstantTypes) {\n      this.constantTypes = {};\n    }\n    this.constantBitRatios = {};\n    let textureIndexes = 0;\n    for (const name in this.constants) {\n      const value = this.constants[name];\n      let type;\n      if (needsConstantTypes) {\n        type = utils.getVariableType(value, this.strictIntegers);\n        this.constantTypes[name] = type;\n      } else {\n        type = this.constantTypes[name];\n      }\n      const KernelValue = this.constructor.lookupKernelValueType(type, 'static', this.precision, value);\n      if (KernelValue === null) {\n        return this.requestFallback(args);\n      }\n      const kernelValue = new KernelValue(value, {\n        name,\n        type,\n        tactic: this.tactic,\n        origin: 'constants',\n        context: this.context,\n        checkContext: this.checkContext,\n        kernel: this,\n        strictIntegers: this.strictIntegers,\n        onRequestTexture: () => {\n          return this.createTexture();\n        },\n        onRequestIndex: () => {\n          return textureIndexes++;\n        },\n        onRequestContextHandle: () => {\n          return gl.TEXTURE0 + this.constantTextureCount++;\n        }\n      });\n      this.constantBitRatios[name] = kernelValue.bitRatio;\n      this.kernelConstants.push(kernelValue);\n      kernelValue.setup();\n      if (kernelValue.forceUploadEachRun) {\n        this.forceUploadKernelConstants.push(kernelValue);\n      }\n    }\n  }\n\n  build() {\n    if (this.built) return;\n    this.initExtensions();\n    this.validateSettings(arguments);\n    this.setupConstants(arguments);\n    if (this.fallbackRequested) return;\n    this.setupArguments(arguments);\n    if (this.fallbackRequested) return;\n    this.updateMaxTexSize();\n    this.translateSource();\n    const failureResult = this.pickRenderStrategy(arguments);\n    if (failureResult) {\n      return failureResult;\n    }\n    const { texSize, context: gl, canvas } = this;\n    gl.enable(gl.SCISSOR_TEST);\n    if (this.pipeline && this.precision === 'single') {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    } else {\n      gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]);\n      canvas.width = this.maxTexSize[0];\n      canvas.height = this.maxTexSize[1];\n    }\n    const threadDim = this.threadDim = Array.from(this.output);\n    while (threadDim.length < 3) {\n      threadDim.push(1);\n    }\n\n    const compiledVertexShader = this.getVertexShader(arguments);\n    const vertShader = gl.createShader(gl.VERTEX_SHADER);\n    gl.shaderSource(vertShader, compiledVertexShader);\n    gl.compileShader(vertShader);\n    this.vertShader = vertShader;\n\n    const compiledFragmentShader = this.getFragmentShader(arguments);\n    const fragShader = gl.createShader(gl.FRAGMENT_SHADER);\n    gl.shaderSource(fragShader, compiledFragmentShader);\n    gl.compileShader(fragShader);\n    this.fragShader = fragShader;\n\n    if (this.debug) {\n      console.log('GLSL Shader Output:');\n      console.log(compiledFragmentShader);\n    }\n\n    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader));\n    }\n    if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {\n      throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader));\n    }\n\n    const program = this.program = gl.createProgram();\n    gl.attachShader(program, vertShader);\n    gl.attachShader(program, fragShader);\n    gl.linkProgram(program);\n    this.framebuffer = gl.createFramebuffer();\n    this.framebuffer.width = texSize[0];\n    this.framebuffer.height = texSize[1];\n    this.rawValueFramebuffers = {};\n\n    const vertices = new Float32Array([-1, -1,\n      1, -1, -1, 1,\n      1, 1\n    ]);\n    const texCoords = new Float32Array([\n      0, 0,\n      1, 0,\n      0, 1,\n      1, 1\n    ]);\n\n    const texCoordOffset = vertices.byteLength;\n\n    let buffer = this.buffer;\n    if (!buffer) {\n      buffer = this.buffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n      gl.bufferData(gl.ARRAY_BUFFER, vertices.byteLength + texCoords.byteLength, gl.STATIC_DRAW);\n    } else {\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n    }\n\n    gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);\n    gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords);\n\n    const aPosLoc = gl.getAttribLocation(this.program, 'aPos');\n    gl.enableVertexAttribArray(aPosLoc);\n    gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0);\n    const aTexCoordLoc = gl.getAttribLocation(this.program, 'aTexCoord');\n    gl.enableVertexAttribArray(aTexCoordLoc);\n    gl.vertexAttribPointer(aTexCoordLoc, 2, gl.FLOAT, false, 0, texCoordOffset);\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\n    let i = 0;\n    gl.useProgram(this.program);\n    for (let p in this.constants) {\n      this.kernelConstants[i++].updateValue(this.constants[p]);\n    }\n\n    this._setupOutputTexture();\n    if (\n      this.subKernels !== null &&\n      this.subKernels.length > 0\n    ) {\n      this._mappedTextureSwitched = {};\n      this._setupSubOutputTextures();\n    }\n    this.buildSignature(arguments);\n    this.built = true;\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGLFunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  setupReturnTypes(functionBuilder) {\n    if (!this.graphical && !this.returnType) {\n      this.returnType = functionBuilder.getKernelResultType();\n    }\n\n    if (this.subKernels && this.subKernels.length > 0) {\n      for (let i = 0; i < this.subKernels.length; i++) {\n        const subKernel = this.subKernels[i];\n        if (!subKernel.returnType) {\n          subKernel.returnType = functionBuilder.getSubKernelResultType(i);\n        }\n      }\n    }\n  }\n\n  run() {\n    const { kernelArguments, texSize, forceUploadKernelConstants, context: gl } = this;\n\n    gl.useProgram(this.program);\n    gl.scissor(0, 0, texSize[0], texSize[1]);\n    if (this.dynamicOutput) {\n      this.setUniform3iv('uOutputDim', new Int32Array(this.threadDim));\n      this.setUniform2iv('uTexSize', texSize);\n    }\n\n    this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);\n\n    for (let i = 0; i < forceUploadKernelConstants.length; i++) {\n      const constant = forceUploadKernelConstants[i];\n      constant.updateValue(this.constants[constant.name]);\n      if (this.switchingKernels) return;\n    }\n    for (let i = 0; i < kernelArguments.length; i++) {\n      kernelArguments[i].updateValue(arguments[i]);\n      if (this.switchingKernels) return;\n    }\n\n    if (this.plugins) {\n      for (let i = 0; i < this.plugins.length; i++) {\n        const plugin = this.plugins[i];\n        if (plugin.onBeforeRun) {\n          plugin.onBeforeRun(this);\n        }\n      }\n    }\n\n    if (this.graphical) {\n      if (this.pipeline) {\n        gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n        gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n        if (this.immutable) {\n          this._replaceOutputTexture();\n        }\n        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n        return this.immutable ? this.texture.clone() : this.texture;\n      }\n      gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n      gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n      return;\n    }\n\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    if (this.immutable) {\n      this._replaceOutputTexture();\n    }\n\n    if (this.subKernels !== null) {\n      if (this.immutable) {\n        this._replaceSubOutputTextures();\n      }\n      this.drawBuffers();\n    }\n\n    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n  }\n\n  drawBuffers() {\n    this.extensions.WEBGL_draw_buffers.drawBuffersWEBGL(this.drawBuffersMap);\n  }\n\n  getInternalFormat() {\n    return this.context.RGBA;\n  }\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n\n  /**\n   *\n   * @desc replace output textures where arguments my be the same values\n   */\n  _replaceOutputTexture() {\n    if (this.texture.beforeMutate() || this._textureSwitched) {\n      const gl = this.context;\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      this._textureSwitched = false;\n    }\n  }\n\n  /**\n   * @desc Setup output texture\n   */\n  _setupOutputTexture() {\n    const gl = this.context;\n    const texSize = this.texSize;\n    if (this.texture) {\n      // here we inherit from an already existing kernel, so go ahead and just bind textures to the framebuffer\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    const texture = this.createTexture();\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  /**\n   *\n   * @desc replace sub-output textures where arguments my be the same values\n   */\n  _replaceSubOutputTextures() {\n    const gl = this.context;\n    for (let i = 0; i < this.mappedTextures.length; i++) {\n      const mappedTexture = this.mappedTextures[i];\n      if (mappedTexture.beforeMutate() || this._mappedTextureSwitched[i]) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, mappedTexture.texture, 0);\n        this._mappedTextureSwitched[i] = false;\n      }\n    }\n  }\n\n  /**\n   * @desc Setup on inherit sub-output textures\n   */\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      // here we inherit from an already existing kernel, so go ahead and just bind textures to the framebuffer\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      if (this.precision === 'single') {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  setUniform1f(name, value) {\n    if (this.uniform1fCache.hasOwnProperty(name)) {\n      const cache = this.uniform1fCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1fCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1f(loc, value);\n  }\n\n  setUniform1i(name, value) {\n    if (this.uniform1iCache.hasOwnProperty(name)) {\n      const cache = this.uniform1iCache[name];\n      if (value === cache) {\n        return;\n      }\n    }\n    this.uniform1iCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform1i(loc, value);\n  }\n\n  setUniform2f(name, value1, value2) {\n    if (this.uniform2fCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fCache[name];\n      if (\n        value1 === cache[0] &&\n        value2 === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fCache[name] = [value1, value2];\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2f(loc, value1, value2);\n  }\n\n  setUniform2fv(name, value) {\n    if (this.uniform2fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform2fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2fv(loc, value);\n  }\n\n  setUniform2iv(name, value) {\n    if (this.uniform2ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform2ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1]\n      ) {\n        return;\n      }\n    }\n    this.uniform2ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform2iv(loc, value);\n  }\n\n  setUniform3fv(name, value) {\n    if (this.uniform3fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform3fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3fv(loc, value);\n  }\n\n  setUniform3iv(name, value) {\n    if (this.uniform3ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform3ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2]\n      ) {\n        return;\n      }\n    }\n    this.uniform3ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform3iv(loc, value);\n  }\n\n  setUniform4fv(name, value) {\n    if (this.uniform4fvCache.hasOwnProperty(name)) {\n      const cache = this.uniform4fvCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4fvCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4fv(loc, value);\n  }\n\n  setUniform4iv(name, value) {\n    if (this.uniform4ivCache.hasOwnProperty(name)) {\n      const cache = this.uniform4ivCache[name];\n      if (\n        value[0] === cache[0] &&\n        value[1] === cache[1] &&\n        value[2] === cache[2] &&\n        value[3] === cache[3]\n      ) {\n        return;\n      }\n    }\n    this.uniform4ivCache[name] = value;\n    const loc = this.getUniformLocation(name);\n    this.context.uniform4iv(loc, value);\n  }\n\n  /**\n   * @desc Return WebGlUniformLocation for various variables\n   * related to webGl program, such as user-defined variables,\n   * as well as, dimension sizes, etc.\n   */\n  getUniformLocation(name) {\n    if (this.programUniformLocationCache.hasOwnProperty(name)) {\n      return this.programUniformLocationCache[name];\n    }\n    return this.programUniformLocationCache[name] = this.context.getUniformLocation(this.program, name);\n  }\n\n  /**\n   * @desc Generate Shader artifacts for the kernel program.\n   * The final object contains HEADER, KERNEL, MAIN_RESULT, and others.\n   *\n   * @param {Array} args - The actual parameters sent to the Kernel\n   * @returns {Object} An object containing the Shader Artifacts(CONSTANTS, HEADER, KERNEL, etc.)\n   */\n  _getFragShaderArtifactMap(args) {\n    return {\n      HEADER: this._getHeaderString(),\n      LOOP_MAX: this._getLoopMaxString(),\n      PLUGINS: this._getPluginsString(),\n      CONSTANTS: this._getConstantsString(),\n      DECODE32_ENDIANNESS: this._getDecode32EndiannessString(),\n      ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(),\n      DIVIDE_WITH_INTEGER_CHECK: this._getDivideWithIntegerCheckString(),\n      INJECTED_NATIVE: this._getInjectedNative(),\n      MAIN_CONSTANTS: this._getMainConstantsString(),\n      MAIN_ARGUMENTS: this._getMainArgumentsString(args),\n      KERNEL: this.getKernelString(),\n      MAIN_RESULT: this.getMainResultString(),\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  /**\n   * @desc Generate Shader artifacts for the kernel program.\n   * The final object contains HEADER, KERNEL, MAIN_RESULT, and others.\n   *\n   * @param {Array} args - The actual parameters sent to the Kernel\n   * @returns {Object} An object containing the Shader Artifacts(CONSTANTS, HEADER, KERNEL, etc.)\n   */\n  _getVertShaderArtifactMap(args) {\n    return {\n      FLOAT_TACTIC_DECLARATION: this.getFloatTacticDeclaration(),\n      INT_TACTIC_DECLARATION: this.getIntTacticDeclaration(),\n      SAMPLER_2D_TACTIC_DECLARATION: this.getSampler2DTacticDeclaration(),\n      SAMPLER_2D_ARRAY_TACTIC_DECLARATION: this.getSampler2DArrayTacticDeclaration(),\n    };\n  }\n\n  /**\n   * @desc Get the header string for the program.\n   * This returns an empty string if no sub-kernels are defined.\n   *\n   * @returns {String} result\n   */\n  _getHeaderString() {\n    return (\n      this.subKernels !== null ?\n      '#extension GL_EXT_draw_buffers : require\\n' :\n      ''\n    );\n  }\n\n  /**\n   * @desc Get the maximum loop size String.\n   * @returns {String} result\n   */\n  _getLoopMaxString() {\n    return (\n      this.loopMaxIterations ?\n      ` ${parseInt(this.loopMaxIterations)};\\n` :\n      ' 1000;\\n'\n    );\n  }\n\n  _getPluginsString() {\n    if (!this.plugins) return '\\n';\n    return this.plugins.map(plugin => plugin.source && this.source.match(plugin.functionMatch) ? plugin.source : '').join('\\n');\n  }\n\n  /**\n   * @desc Generate transpiled glsl Strings for constant parameters sent to a kernel\n   * @returns {String} result\n   */\n  _getConstantsString() {\n    const result = [];\n    const { threadDim, texSize } = this;\n    if (this.dynamicOutput) {\n      result.push(\n        'uniform ivec3 uOutputDim',\n        'uniform ivec2 uTexSize'\n      );\n    } else {\n      result.push(\n        `ivec3 uOutputDim = ivec3(${threadDim[0]}, ${threadDim[1]}, ${threadDim[2]})`,\n        `ivec2 uTexSize = ivec2(${texSize[0]}, ${texSize[1]})`\n      );\n    }\n    return utils.linesToString(result);\n  }\n\n  /**\n   * @desc Get texture coordinate string for the program\n   * @returns {String} result\n   */\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    if (subKernels === null || subKernels.length < 1) {\n      return 'varying vec2 vTexCoord;\\n';\n    } else {\n      return 'out vec2 vTexCoord;\\n';\n    }\n  }\n\n  /**\n   * @desc Get Decode32 endianness string for little-endian and big-endian\n   * @returns {String} result\n   */\n  _getDecode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  /**\n   * @desc Get Encode32 endianness string for little-endian and big-endian\n   * @returns {String} result\n   */\n  _getEncode32EndiannessString() {\n    return (\n      this.endianness === 'LE' ?\n      '' :\n      '  texel.rgba = texel.abgr;\\n'\n    );\n  }\n\n  /**\n   * @desc if fixIntegerDivisionAccuracy provide method to replace /\n   * @returns {String} result\n   */\n  _getDivideWithIntegerCheckString() {\n    return this.fixIntegerDivisionAccuracy ?\n      `float divWithIntCheck(float x, float y) {\n  if (floor(x) == x && floor(y) == y && integerMod(x, y) == 0.0) {\n    return float(int(x) / int(y));\n  }\n  return x / y;\n}\n\nfloat integerCorrectionModulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -(number - (divisor * floor(divWithIntCheck(number, divisor))));\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return number - (divisor * floor(divWithIntCheck(number, divisor)));\n}` :\n      '';\n  }\n\n  /**\n   * @desc Generate transpiled glsl Strings for user-defined parameters sent to a kernel\n   * @param {Array} args - The actual parameters sent to the Kernel\n   * @returns {String} result\n   */\n  _getMainArgumentsString(args) {\n    const results = [];\n    const { argumentNames } = this;\n    for (let i = 0; i < argumentNames.length; i++) {\n      results.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return results.join('');\n  }\n\n  _getInjectedNative() {\n    return this.injectedNative || '';\n  }\n\n  _getMainConstantsString() {\n    const result = [];\n    const { constants } = this;\n    if (constants) {\n      let i = 0;\n      for (const name in constants) {\n        if (!this.constants.hasOwnProperty(name)) continue;\n        result.push(this.kernelConstants[i++].getSource(this.constants[name]));\n      }\n    }\n    return result.join('');\n  }\n\n  getRawValueFramebuffer(width, height) {\n    if (!this.rawValueFramebuffers[width]) {\n      this.rawValueFramebuffers[width] = {};\n    }\n    if (!this.rawValueFramebuffers[width][height]) {\n      const framebuffer = this.context.createFramebuffer();\n      framebuffer.width = width;\n      framebuffer.height = height;\n      this.rawValueFramebuffers[width][height] = framebuffer;\n    }\n    return this.rawValueFramebuffers[width][height];\n  }\n\n  getKernelResultDeclaration() {\n    switch (this.returnType) {\n      case 'Array(2)':\n        return 'vec2 kernelResult';\n      case 'Array(3)':\n        return 'vec3 kernelResult';\n      case 'Array(4)':\n        return 'vec4 kernelResult';\n      case 'LiteralInteger':\n      case 'Float':\n      case 'Number':\n      case 'Integer':\n        return 'float kernelResult';\n      default:\n        if (this.graphical) {\n          return 'float kernelResult';\n        } else {\n          throw new Error(`unrecognized output type \"${ this.returnType }\"`);\n        }\n    }\n  }\n  /**\n   * @desc Get Kernel program string (in *glsl*) for a kernel.\n   * @returns {String} result\n   */\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const { subKernels } = this;\n    if (subKernels !== null) {\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`\n            );\n          }\n          break;\n      }\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragColor = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  /**\n   * @return {String}\n   */\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  /**\n   * @return {String}\n   */\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}] = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultMemoryOptimizedFloats() {\n    const result = [\n      '  index *= 4',\n    ];\n\n    switch (this.returnType) {\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        const channels = ['r', 'g', 'b', 'a'];\n        for (let i = 0; i < channels.length; i++) {\n          const channel = channels[i];\n          this.getMainResultKernelMemoryOptimizedFloats(result, channel);\n          this.getMainResultSubKernelMemoryOptimizedFloats(result, channel);\n          if (i + 1 < channels.length) {\n            result.push('  index += 1');\n          }\n        }\n        break;\n      default:\n        throw new Error(`optimized output only usable with Numbers, ${this.returnType} specified`);\n    }\n\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  gl_FragData[0].${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = float(subKernelResult_${this.subKernels[i].name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}].${channel} = subKernelResult_${this.subKernels[i].name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  gl_FragData[${i + 1}][0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0][0] = kernelResult[0]',\n      '  gl_FragData[0][1] = kernelResult[1]',\n      '  gl_FragData[0][2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n        `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n        `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  gl_FragData[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    switch (this.returnType) {\n      case 'Number':\n      case 'Float':\n      case 'Integer':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          const subKernel = this.subKernels[i];\n          if (subKernel.returnType === 'Integer') {\n            result.push(\n              `  gl_FragData[${i + 1}] = float(subKernelResult_${this.subKernels[i].name})`\n            );\n          } else {\n            result.push(\n              `  gl_FragData[${i + 1}] = subKernelResult_${this.subKernels[i].name}`\n            );\n          }\n        }\n        break;\n      case 'Array(2)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`\n          );\n        }\n        break;\n      case 'Array(3)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`\n          );\n        }\n        break;\n      case 'Array(4)':\n        for (let i = 0; i < this.subKernels.length; ++i) {\n          result.push(\n            `  gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`,\n            `  gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`,\n            `  gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`,\n            `  gl_FragData[${i + 1}][3] = subKernelResult_${this.subKernels[i].name}[3]`\n          );\n        }\n        break;\n    }\n\n    return result;\n  }\n\n  /**\n   * @param {String} src - Shader string\n   * @param {Object} map - Variables/Constants associated with shader\n   */\n  replaceArtifacts(src, map) {\n    return src.replace(/[ ]*__([A-Z]+[0-9]*([_]?[A-Z]*[0-9]?)*)__;\\n/g, (match, artifact) => {\n      if (map.hasOwnProperty(artifact)) {\n        return map[artifact];\n      }\n      throw `unhandled artifact ${artifact}`;\n    });\n  }\n\n  /**\n   * @desc Get the fragment shader String.\n   * If the String hasn't been compiled yet,\n   * then this method compiles it as well\n   *\n   * @param {Array} args - The actual parameters sent to the Kernel\n   * @returns {string} Fragment Shader string\n   */\n  getFragmentShader(args) {\n    if (this.compiledFragmentShader !== null) {\n      return this.compiledFragmentShader;\n    }\n    return this.compiledFragmentShader = this.replaceArtifacts(this.constructor.fragmentShader, this._getFragShaderArtifactMap(args));\n  }\n\n  /**\n   * @desc Get the vertical shader String\n   * @param {Array|IArguments} args - The actual parameters sent to the Kernel\n   * @returns {string} Vertical Shader string\n   */\n  getVertexShader(args) {\n    if (this.compiledVertexShader !== null) {\n      return this.compiledVertexShader;\n    }\n    return this.compiledVertexShader = this.replaceArtifacts(this.constructor.vertexShader, this._getVertShaderArtifactMap(args));\n  }\n\n  /**\n   * @desc Returns the *pre-compiled* Kernel as a JS Object String, that can be reused.\n   */\n  toString() {\n    const setupContextString = utils.linesToString([\n      `const gl = context`,\n    ]);\n    return glKernelString(this.constructor, arguments, this, setupContextString);\n  }\n\n  destroy(removeCanvasReferences) {\n    if (!this.context) return;\n    if (this.buffer) {\n      this.context.deleteBuffer(this.buffer);\n    }\n    if (this.framebuffer) {\n      this.context.deleteFramebuffer(this.framebuffer);\n    }\n    for (const width in this.rawValueFramebuffers) {\n      for (const height in this.rawValueFramebuffers[width]) {\n        this.context.deleteFramebuffer(this.rawValueFramebuffers[width][height]);\n        delete this.rawValueFramebuffers[width][height];\n      }\n      delete this.rawValueFramebuffers[width];\n    }\n    if (this.vertShader) {\n      this.context.deleteShader(this.vertShader);\n    }\n    if (this.fragShader) {\n      this.context.deleteShader(this.fragShader);\n    }\n    if (this.program) {\n      this.context.deleteProgram(this.program);\n    }\n    if (this.texture) {\n      this.texture.delete();\n      const textureCacheIndex = this.textureCache.indexOf(this.texture.texture);\n      if (textureCacheIndex > -1) {\n        this.textureCache.splice(textureCacheIndex, 1);\n      }\n      this.texture = null;\n    }\n    if (this.mappedTextures && this.mappedTextures.length) {\n      for (let i = 0; i < this.mappedTextures.length; i++) {\n        const mappedTexture = this.mappedTextures[i];\n        mappedTexture.delete();\n        const textureCacheIndex = this.textureCache.indexOf(mappedTexture.texture);\n        if (textureCacheIndex > -1) {\n          this.textureCache.splice(textureCacheIndex, 1);\n        }\n      }\n      this.mappedTextures = null;\n    }\n    if (this.kernelArguments) {\n      for (let i = 0; i < this.kernelArguments.length; i++) {\n        this.kernelArguments[i].destroy();\n      }\n    }\n    if (this.kernelConstants) {\n      for (let i = 0; i < this.kernelConstants.length; i++) {\n        this.kernelConstants[i].destroy();\n      }\n    }\n    while (this.textureCache.length > 0) {\n      const texture = this.textureCache.pop();\n      this.context.deleteTexture(texture);\n    }\n    if (removeCanvasReferences) {\n      const idx = canvases.indexOf(this.canvas);\n      if (idx >= 0) {\n        canvases[idx] = null;\n        maxTexSizes[idx] = null;\n      }\n    }\n    this.destroyExtensions();\n    delete this.context;\n    delete this.canvas;\n    if (!this.gpu) return;\n    const i = this.gpu.kernels.indexOf(this);\n    if (i === -1) return;\n    this.gpu.kernels.splice(i, 1);\n  }\n\n  destroyExtensions() {\n    this.extensions.OES_texture_float = null;\n    this.extensions.OES_texture_float_linear = null;\n    this.extensions.OES_element_index_uint = null;\n    this.extensions.WEBGL_draw_buffers = null;\n  }\n\n  static destroyContext(context) {\n    const extension = context.getExtension('WEBGL_lose_context');\n    if (extension) {\n      extension.loseContext();\n    }\n  }\n\n  /**\n   * @return {IKernelJSON}\n   */\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGLFunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGLKernel\n};"
  },
  {
    "path": "src/backend/web-gl/vertex-shader.js",
    "content": "// language=GLSL\nconst vertexShader = `__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nattribute vec2 aPos;\nattribute vec2 aTexCoord;\n\nvarying vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};"
  },
  {
    "path": "src/backend/web-gl2/fragment-shader.js",
    "content": "// language=GLSL\nconst fragmentShader = `#version 300 es\n__HEADER__;\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n__SAMPLER_2D_ARRAY_TACTIC_DECLARATION__;\n\nconst int LOOP_MAX = __LOOP_MAX__;\n\n__PLUGINS__;\n__CONSTANTS__;\n\nin vec2 vTexCoord;\n\nfloat atan2(float v1, float v2) {\n  if (v1 == 0.0 || v2 == 0.0) return 0.0;\n  return atan(v1 / v2);\n}\n\nfloat cbrt(float x) {\n  if (x >= 0.0) {\n    return pow(x, 1.0 / 3.0);\n  } else {\n    return -pow(x, 1.0 / 3.0);\n  }\n}\n\nfloat expm1(float x) {\n  return pow(${Math.E}, x) - 1.0; \n}\n\nfloat fround(highp float x) {\n  return x;\n}\n\nfloat imul(float v1, float v2) {\n  return float(int(v1) * int(v2));\n}\n\nfloat log10(float x) {\n  return log2(x) * (1.0 / log2(10.0));\n}\n\nfloat log1p(float x) {\n  return log(1.0 + x);\n}\n\nfloat _pow(float v1, float v2) {\n  if (v2 == 0.0) return 1.0;\n  return pow(v1, v2);\n}\n\nfloat _round(float x) {\n  return floor(x + 0.5);\n}\n\n\nconst int BIT_COUNT = 32;\nint modi(int x, int y) {\n  return x - y * (x / y);\n}\n\nint bitwiseOr(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) || (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseXOR(int a, int b) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) != (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 || b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseAnd(int a, int b) {\n  int result = 0;\n  int n = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if ((modi(a, 2) == 1) && (modi(b, 2) == 1)) {\n      result += n;\n    }\n    a = a / 2;\n    b = b / 2;\n    n = n * 2;\n    if(!(a > 0 && b > 0)) {\n      break;\n    }\n  }\n  return result;\n}\nint bitwiseNot(int a) {\n  int result = 0;\n  int n = 1;\n  \n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (modi(a, 2) == 0) {\n      result += n;    \n    }\n    a = a / 2;\n    n = n * 2;\n  }\n  return result;\n}\nint bitwiseZeroFillLeftShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n *= 2;\n  }\n\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nint bitwiseSignedRightShift(int num, int shifts) {\n  return int(floor(float(num) / pow(2.0, float(shifts))));\n}\n\nint bitwiseZeroFillRightShift(int n, int shift) {\n  int maxBytes = BIT_COUNT;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (maxBytes >= n) {\n      break;\n    }\n    maxBytes *= 2;\n  }\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= shift) {\n      break;\n    }\n    n /= 2;\n  }\n  int result = 0;\n  int byteVal = 1;\n  for (int i = 0; i < BIT_COUNT; i++) {\n    if (i >= maxBytes) break;\n    if (modi(n, 2) > 0) { result += byteVal; }\n    n = int(n / 2);\n    byteVal *= 2;\n  }\n  return result;\n}\n\nvec2 integerMod(vec2 x, float y) {\n  vec2 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec3 integerMod(vec3 x, float y) {\n  vec3 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nvec4 integerMod(vec4 x, vec4 y) {\n  vec4 res = floor(mod(x, y));\n  return res * step(1.0 - floor(y), -res);\n}\n\nfloat integerMod(float x, float y) {\n  float res = floor(mod(x, y));\n  return res * (res > floor(y) - 1.0 ? 0.0 : 1.0);\n}\n\nint integerMod(int x, int y) {\n  return x - (y * int(x/y));\n}\n\n__DIVIDE_WITH_INTEGER_CHECK__;\n\n// Here be dragons!\n// DO NOT OPTIMIZE THIS CODE\n// YOU WILL BREAK SOMETHING ON SOMEBODY\\'S MACHINE\n// LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME\nconst vec2 MAGIC_VEC = vec2(1.0, -256.0);\nconst vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0);\nconst vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536\nfloat decode32(vec4 texel) {\n  __DECODE32_ENDIANNESS__;\n  texel *= 255.0;\n  vec2 gte128;\n  gte128.x = texel.b >= 128.0 ? 1.0 : 0.0;\n  gte128.y = texel.a >= 128.0 ? 1.0 : 0.0;\n  float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC);\n  float res = exp2(round(exponent));\n  texel.b = texel.b - 128.0 * gte128.x;\n  res = dot(texel, SCALE_FACTOR) * exp2(round(exponent-23.0)) + res;\n  res *= gte128.y * -2.0 + 1.0;\n  return res;\n}\n\nfloat decode16(vec4 texel, int index) {\n  int channel = integerMod(index, 2);\n  return texel[channel*2] * 255.0 + texel[channel*2 + 1] * 65280.0;\n}\n\nfloat decode8(vec4 texel, int index) {\n  int channel = integerMod(index, 4);\n  return texel[channel] * 255.0;\n}\n\nvec4 legacyEncode32(float f) {\n  float F = abs(f);\n  float sign = f < 0.0 ? 1.0 : 0.0;\n  float exponent = floor(log2(F));\n  float mantissa = (exp2(-exponent) * F);\n  // exponent += floor(log2(mantissa));\n  vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV;\n  texel.rg = integerMod(texel.rg, 256.0);\n  texel.b = integerMod(texel.b, 128.0);\n  texel.a = exponent*0.5 + 63.5;\n  texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0;\n  texel = floor(texel);\n  texel *= 0.003921569; // 1/255\n  __ENCODE32_ENDIANNESS__;\n  return texel;\n}\n\n// https://github.com/gpujs/gpu.js/wiki/Encoder-details\nvec4 encode32(float value) {\n  if (value == 0.0) return vec4(0, 0, 0, 0);\n\n  float exponent;\n  float mantissa;\n  vec4  result;\n  float sgn;\n\n  sgn = step(0.0, -value);\n  value = abs(value);\n\n  exponent = floor(log2(value));\n\n  mantissa = value*pow(2.0, -exponent)-1.0;\n  exponent = exponent+127.0;\n  result   = vec4(0,0,0,0);\n\n  result.a = floor(exponent/2.0);\n  exponent = exponent - result.a*2.0;\n  result.a = result.a + 128.0*sgn;\n\n  result.b = floor(mantissa * 128.0);\n  mantissa = mantissa - result.b / 128.0;\n  result.b = result.b + exponent*128.0;\n\n  result.g = floor(mantissa*32768.0);\n  mantissa = mantissa - result.g/32768.0;\n\n  result.r = floor(mantissa*8388608.0);\n  return result/255.0;\n}\n// Dragons end here\n\nint index;\nivec3 threadId;\n\nivec3 indexTo3D(int idx, ivec3 texDim) {\n  int z = int(idx / (texDim.x * texDim.y));\n  idx -= z * int(texDim.x * texDim.y);\n  int y = int(idx / texDim.x);\n  int x = int(integerMod(idx, texDim.x));\n  return ivec3(x, y, z);\n}\n\nfloat get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return decode32(texel);\n}\n\nfloat get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 2;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 2, texSize.y));\n  return decode16(texel, index);\n}\n\nfloat get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int w = texSize.x * 4;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize.x * 4, texSize.y));\n  return decode8(texel, index);\n}\n\nfloat getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + (texDim.x * (y + (texDim.y * z)));\n  int channel = integerMod(index, 4);\n  index = index / 4;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  index = index / 4;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return texel[channel];\n}\n\nvec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, st / vec2(texSize));\n}\n\nvec4 getImage3D(sampler2DArray tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  return texture(tex, vec3(st / vec2(texSize), z));\n}\n\nfloat getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return result[0];\n}\n\nvec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec2(result[0], result[1]);\n}\n\nvec2 getMemoryOptimizedVec2(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  index = index / 2;\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  if (channel == 0) return vec2(texel.r, texel.g);\n  if (channel == 1) return vec2(texel.b, texel.a);\n  return vec2(0.0, 0.0);\n}\n\nvec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  vec4 result = getImage2D(tex, texSize, texDim, z, y, x);\n  return vec3(result[0], result[1], result[2]);\n}\n\nvec3 getMemoryOptimizedVec3(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int fieldIndex = 3 * (x + texDim.x * (y + texDim.y * z));\n  int vectorIndex = fieldIndex / 4;\n  int vectorOffset = fieldIndex - vectorIndex * 4;\n  int readY = vectorIndex / texSize.x;\n  int readX = vectorIndex - readY * texSize.x;\n  vec4 tex1 = texture(tex, (vec2(readX, readY) + 0.5) / vec2(texSize));\n\n  if (vectorOffset == 0) {\n    return tex1.xyz;\n  } else if (vectorOffset == 1) {\n    return tex1.yzw;\n  } else {\n    readX++;\n    if (readX >= texSize.x) {\n      readX = 0;\n      readY++;\n    }\n    vec4 tex2 = texture(tex, vec2(readX, readY) / vec2(texSize));\n    if (vectorOffset == 2) {\n      return vec3(tex1.z, tex1.w, tex2.x);\n    } else {\n      return vec3(tex1.w, tex2.x, tex2.y);\n    }\n  }\n}\n\nvec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  return getImage2D(tex, texSize, texDim, z, y, x);\n}\n\nvec4 getMemoryOptimizedVec4(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) {\n  int index = x + texDim.x * (y + texDim.y * z);\n  int channel = integerMod(index, 2);\n  int w = texSize.x;\n  vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5;\n  vec4 texel = texture(tex, st / vec2(texSize));\n  return vec4(texel.r, texel.g, texel.b, texel.a);\n}\n\nvec4 actualColor;\nvoid color(float r, float g, float b, float a) {\n  actualColor = vec4(r,g,b,a);\n}\n\nvoid color(float r, float g, float b) {\n  color(r,g,b,1.0);\n}\n\nfloat modulo(float number, float divisor) {\n  if (number < 0.0) {\n    number = abs(number);\n    if (divisor < 0.0) {\n      divisor = abs(divisor);\n    }\n    return -mod(number, divisor);\n  }\n  if (divisor < 0.0) {\n    divisor = abs(divisor);\n  }\n  return mod(number, divisor);\n}\n\n__INJECTED_NATIVE__;\n__MAIN_CONSTANTS__;\n__MAIN_ARGUMENTS__;\n__KERNEL__;\n\nvoid main(void) {\n  index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x;\n  __MAIN_RESULT__;\n}`;\n\nmodule.exports = {\n  fragmentShader\n};"
  },
  {
    "path": "src/backend/web-gl2/function-node.js",
    "content": "const { utils } = require('../../utils');\nconst { WebGLFunctionNode } = require('../web-gl/function-node');\n\n/**\n * @class WebGL2FunctionNode\n * @desc [INTERNAL] Takes in a function node, and does all the AST voodoo required to toString its respective webGL code.\n * @extends WebGLFunctionNode\n * @returns the converted webGL function string\n */\nclass WebGL2FunctionNode extends WebGLFunctionNode {\n\n  /**\n   * @desc Parses the abstract syntax tree for *identifier* expression\n   * @param {Object} idtNode - An ast Node\n   * @param {Array} retArr - return array string\n   * @returns {Array} the append retArr\n   */\n  astIdentifierExpression(idtNode, retArr) {\n    if (idtNode.type !== 'Identifier') {\n      throw this.astErrorOutput(\n        'IdentifierExpression - not an Identifier',\n        idtNode\n      );\n    }\n\n    const type = this.getType(idtNode);\n\n    const name = utils.sanitizeName(idtNode.name);\n    if (idtNode.name === 'Infinity') {\n      retArr.push('intBitsToFloat(2139095039)');\n    } else if (type === 'Boolean') {\n      if (this.argumentNames.indexOf(name) > -1) {\n        retArr.push(`bool(user_${name})`);\n      } else {\n        retArr.push(`user_${name}`);\n      }\n    } else {\n      retArr.push(`user_${name}`);\n    }\n\n    return retArr;\n  }\n}\n\nmodule.exports = {\n  WebGL2FunctionNode\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/array2.js",
    "content": "const { WebGLKernelValueArray2 } = require('../../web-gl/kernel-value/array2');\n\nclass WebGL2KernelValueArray2 extends WebGLKernelValueArray2 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray2\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/array3.js",
    "content": "const { WebGLKernelValueArray3 } = require('../../web-gl/kernel-value/array3');\n\nclass WebGL2KernelValueArray3 extends WebGLKernelValueArray3 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray3\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/array4.js",
    "content": "const { WebGLKernelValueArray4 } = require('../../web-gl/kernel-value/array4');\n\nclass WebGL2KernelValueArray4 extends WebGLKernelValueArray4 {}\n\nmodule.exports = {\n  WebGL2KernelValueArray4\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/boolean.js",
    "content": "const { WebGLKernelValueBoolean } = require('../../web-gl/kernel-value/boolean');\n\nclass WebGL2KernelValueBoolean extends WebGLKernelValueBoolean {}\n\nmodule.exports = {\n  WebGL2KernelValueBoolean\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-html-image-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImageArray } = require('./html-image-array');\n\nclass WebGL2KernelValueDynamicHTMLImageArray extends WebGL2KernelValueHTMLImageArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { width, height } = images[0];\n    this.checkSize(width, height);\n    this.dimensions = [width, height, images.length];\n    this.textureSize = [width, height];\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(images);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImageArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-html-image.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicHTMLImage } = require('../../web-gl/kernel-value/dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLImage extends WebGLKernelValueDynamicHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLImage\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-html-video.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./dynamic-html-image');\n\nclass WebGL2KernelValueDynamicHTMLVideo extends WebGL2KernelValueDynamicHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicHTMLVideo\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-memory-optimized-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/dynamic-memory-optimized-number-texture');\n\nclass WebGL2KernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueDynamicMemoryOptimizedNumberTexture {\n  getSource() {\n    return utils.linesToString([\n      `uniform sampler2D ${this.id}`,\n      `uniform ivec2 ${this.sizeId}`,\n      `uniform ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicMemoryOptimizedNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicNumberTexture } = require('../../web-gl/kernel-value/dynamic-number-texture');\n\nclass WebGL2KernelValueDynamicNumberTexture extends WebGLKernelValueDynamicNumberTexture {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-single-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray } = require('../../web-gl2/kernel-value/single-array');\n\nclass WebGL2KernelValueDynamicSingleArray extends WebGL2KernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.dimensions = utils.getDimensions(value, true);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-single-array1d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray1DI } = require('../../web-gl2/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueDynamicSingleArray1DI extends WebGL2KernelValueSingleArray1DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray1DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-single-array2d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray2DI } = require('../../web-gl2/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueDynamicSingleArray2DI extends WebGL2KernelValueSingleArray2DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray2DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-single-array3d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleArray3DI } = require('../../web-gl2/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueDynamicSingleArray3DI extends WebGL2KernelValueSingleArray3DI {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    this.setShape(value);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleArray3DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-single-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueSingleInput } = require('../../web-gl2/kernel-value/single-input');\n\nclass WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n\n  updateValue(value) {\n    let [w, h, d] = value.size;\n    this.dimensions = new Int32Array([w || 1, h || 1, d || 1]);\n    this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio);\n    this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio;\n    this.checkSize(this.textureSize[0], this.textureSize[1]);\n    this.uploadValue = new Float32Array(this.uploadArrayLength);\n    this.kernel.setUniform3iv(this.dimensionsId, this.dimensions);\n    this.kernel.setUniform2iv(this.sizeId, this.textureSize);\n    super.updateValue(value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicSingleInput\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-unsigned-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedArray } = require('../../web-gl/kernel-value/dynamic-unsigned-array');\n\nclass WebGL2KernelValueDynamicUnsignedArray extends WebGLKernelValueDynamicUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/dynamic-unsigned-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueDynamicUnsignedInput } = require('../../web-gl/kernel-value/dynamic-unsigned-input');\n\nclass WebGL2KernelValueDynamicUnsignedInput extends WebGLKernelValueDynamicUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `uniform ${ variablePrecision } ivec2 ${this.sizeId}`,\n      `uniform ${ variablePrecision } ivec3 ${this.dimensionsId}`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueDynamicUnsignedInput\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/float.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueFloat } = require('../../web-gl/kernel-value/float');\n\nclass WebGL2KernelValueFloat extends WebGLKernelValueFloat {}\n\nmodule.exports = {\n  WebGL2KernelValueFloat\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/html-image-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelArray } = require('../../web-gl/kernel-value/array');\n\nclass WebGL2KernelValueHTMLImageArray extends WebGLKernelArray {\n  constructor(value, settings) {\n    super(value, settings);\n    this.checkSize(value[0].width, value[0].height);\n    this.dimensions = [value[0].width, value[0].height, value.length];\n    this.textureSize = [value[0].width, value[0].height];\n  }\n  defineTexture() {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  }\n\n  getStringValueHandler() {\n    return `const uploadValue_${this.name} = ${this.varName};\\n`;\n  }\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2DArray ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(images) {\n    const { context: gl } = this;\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n    // Upload the images into the texture.\n    gl.texImage3D(\n      gl.TEXTURE_2D_ARRAY,\n      0,\n      gl.RGBA,\n      images[0].width,\n      images[0].height,\n      images.length,\n      0,\n      gl.RGBA,\n      gl.UNSIGNED_BYTE,\n      null\n    );\n    for (let i = 0; i < images.length; i++) {\n      const xOffset = 0;\n      const yOffset = 0;\n      const imageDepth = 1;\n      gl.texSubImage3D(\n        gl.TEXTURE_2D_ARRAY,\n        0,\n        xOffset,\n        yOffset,\n        i,\n        images[i].width,\n        images[i].height,\n        imageDepth,\n        gl.RGBA,\n        gl.UNSIGNED_BYTE,\n        this.uploadValue = images[i]\n      );\n    }\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImageArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/html-image.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueHTMLImage } = require('../../web-gl/kernel-value/html-image');\n\nclass WebGL2KernelValueHTMLImage extends WebGLKernelValueHTMLImage {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLImage\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/html-video.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGL2KernelValueHTMLImage } = require('./html-image');\n\nclass WebGL2KernelValueHTMLVideo extends WebGL2KernelValueHTMLImage {}\n\nmodule.exports = {\n  WebGL2KernelValueHTMLVideo\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/integer.js",
    "content": "const { WebGLKernelValueInteger } = require('../../web-gl/kernel-value/integer');\n\nclass WebGL2KernelValueInteger extends WebGLKernelValueInteger {\n  getSource(value) {\n    const variablePrecision = this.getVariablePrecisionString();\n    if (this.origin === 'constants') {\n      return `const ${ variablePrecision } int ${this.id} = ${ parseInt(value) };\\n`;\n    }\n    return `uniform ${ variablePrecision } int ${this.id};\\n`;\n  }\n\n  updateValue(value) {\n    if (this.origin === 'constants') return;\n    this.kernel.setUniform1i(this.id, this.uploadValue = value);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueInteger\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/memory-optimized-number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/memory-optimized-number-texture');\n\nclass WebGL2KernelValueMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueMemoryOptimizedNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/number-texture.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueNumberTexture } = require('../../web-gl/kernel-value/number-texture');\n\nclass WebGL2KernelValueNumberTexture extends WebGLKernelValueNumberTexture {\n  getSource() {\n    const { id, sizeId, textureSize, dimensionsId, dimensions } = this;\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${id}`,\n      `${ variablePrecision } ivec2 ${sizeId} = ivec2(${textureSize[0]}, ${textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${dimensionsId} = ivec3(${dimensions[0]}, ${dimensions[1]}, ${dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueNumberTexture\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/single-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray } = require('../../web-gl/kernel-value/single-array');\n\nclass WebGL2KernelValueSingleArray extends WebGLKernelValueSingleArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/single-array1d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray1DI } = require('../../web-gl/kernel-value/single-array1d-i');\n\nclass WebGL2KernelValueSingleArray1DI extends WebGLKernelValueSingleArray1DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray1DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/single-array2d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray2DI } = require('../../web-gl/kernel-value/single-array2d-i');\n\nclass WebGL2KernelValueSingleArray2DI extends WebGLKernelValueSingleArray2DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray2DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/single-array3d-i.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleArray3DI } = require('../../web-gl/kernel-value/single-array3d-i');\n\nclass WebGL2KernelValueSingleArray3DI extends WebGLKernelValueSingleArray3DI {\n  updateValue(value) {\n    if (value.constructor !== this.initialValueConstructor) {\n      this.onUpdateValueMismatch(value.constructor);\n      return;\n    }\n    const { context: gl } = this;\n    utils.flattenTo(value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleArray3DI\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/single-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueSingleInput } = require('../../web-gl/kernel-value/single-input');\n\nclass WebGL2KernelValueSingleInput extends WebGLKernelValueSingleInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n\n  updateValue(input) {\n    const { context: gl } = this;\n    utils.flattenTo(input.value, this.uploadValue);\n    gl.activeTexture(this.contextHandle);\n    gl.bindTexture(gl.TEXTURE_2D, this.texture);\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue);\n    this.kernel.setUniform1i(this.id, this.index);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueSingleInput\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/unsigned-array.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedArray } = require('../../web-gl/kernel-value/unsigned-array');\n\nclass WebGL2KernelValueUnsignedArray extends WebGLKernelValueUnsignedArray {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedArray\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value/unsigned-input.js",
    "content": "const { utils } = require('../../../utils');\nconst { WebGLKernelValueUnsignedInput } = require('../../web-gl/kernel-value/unsigned-input');\n\nclass WebGL2KernelValueUnsignedInput extends WebGLKernelValueUnsignedInput {\n  getSource() {\n    const variablePrecision = this.getVariablePrecisionString();\n    return utils.linesToString([\n      `uniform ${ variablePrecision } sampler2D ${this.id}`,\n      `${ variablePrecision } ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`,\n      `${ variablePrecision } ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`,\n    ]);\n  }\n}\n\nmodule.exports = {\n  WebGL2KernelValueUnsignedInput\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel-value-maps.js",
    "content": "const { WebGL2KernelValueBoolean } = require('./kernel-value/boolean');\nconst { WebGL2KernelValueFloat } = require('./kernel-value/float');\nconst { WebGL2KernelValueInteger } = require('./kernel-value/integer');\n\nconst { WebGL2KernelValueHTMLImage } = require('./kernel-value/html-image');\nconst { WebGL2KernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image');\n\nconst { WebGL2KernelValueHTMLImageArray } = require('./kernel-value/html-image-array');\nconst { WebGL2KernelValueDynamicHTMLImageArray } = require('./kernel-value/dynamic-html-image-array');\n\nconst { WebGL2KernelValueHTMLVideo } = require('./kernel-value/html-video');\nconst { WebGL2KernelValueDynamicHTMLVideo } = require('./kernel-value/dynamic-html-video');\n\nconst { WebGL2KernelValueSingleInput } = require('./kernel-value/single-input');\nconst { WebGL2KernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input');\n\nconst { WebGL2KernelValueUnsignedInput } = require('./kernel-value/unsigned-input');\nconst { WebGL2KernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input');\n\nconst { WebGL2KernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture');\nconst { WebGL2KernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture');\n\nconst { WebGL2KernelValueNumberTexture } = require('./kernel-value/number-texture');\nconst { WebGL2KernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture');\n\nconst { WebGL2KernelValueSingleArray } = require('./kernel-value/single-array');\nconst { WebGL2KernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array');\n\nconst { WebGL2KernelValueSingleArray1DI } = require('./kernel-value/single-array1d-i');\nconst { WebGL2KernelValueDynamicSingleArray1DI } = require('./kernel-value/dynamic-single-array1d-i');\n\nconst { WebGL2KernelValueSingleArray2DI } = require('./kernel-value/single-array2d-i');\nconst { WebGL2KernelValueDynamicSingleArray2DI } = require('./kernel-value/dynamic-single-array2d-i');\n\nconst { WebGL2KernelValueSingleArray3DI } = require('./kernel-value/single-array3d-i');\nconst { WebGL2KernelValueDynamicSingleArray3DI } = require('./kernel-value/dynamic-single-array3d-i');\n\nconst { WebGL2KernelValueArray2 } = require('./kernel-value/array2');\nconst { WebGL2KernelValueArray3 } = require('./kernel-value/array3');\nconst { WebGL2KernelValueArray4 } = require('./kernel-value/array4');\n\nconst { WebGL2KernelValueUnsignedArray } = require('./kernel-value/unsigned-array');\nconst { WebGL2KernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array');\n\nconst kernelValueMaps = {\n  unsigned: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueDynamicUnsignedInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueUnsignedArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': false,\n      'Array1D(3)': false,\n      'Array1D(4)': false,\n      'Array2D(2)': false,\n      'Array2D(3)': false,\n      'Array2D(4)': false,\n      'Array3D(2)': false,\n      'Array3D(3)': false,\n      'Array3D(4)': false,\n      'Input': WebGL2KernelValueUnsignedInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n  single: {\n    dynamic: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Integer': WebGL2KernelValueInteger,\n      'Float': WebGL2KernelValueFloat,\n      'Array': WebGL2KernelValueDynamicSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueDynamicSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueDynamicSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueDynamicSingleArray3DI,\n      'Input': WebGL2KernelValueDynamicSingleInput,\n      'NumberTexture': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImage': WebGL2KernelValueDynamicHTMLImage,\n      'ImageBitmap': WebGL2KernelValueDynamicHTMLImage,\n      'ImageData': WebGL2KernelValueDynamicHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueDynamicHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueDynamicHTMLVideo,\n    },\n    static: {\n      'Boolean': WebGL2KernelValueBoolean,\n      'Float': WebGL2KernelValueFloat,\n      'Integer': WebGL2KernelValueInteger,\n      'Array': WebGL2KernelValueSingleArray,\n      'Array(2)': WebGL2KernelValueArray2,\n      'Array(3)': WebGL2KernelValueArray3,\n      'Array(4)': WebGL2KernelValueArray4,\n      'Array1D(2)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(3)': WebGL2KernelValueSingleArray1DI,\n      'Array1D(4)': WebGL2KernelValueSingleArray1DI,\n      'Array2D(2)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(3)': WebGL2KernelValueSingleArray2DI,\n      'Array2D(4)': WebGL2KernelValueSingleArray2DI,\n      'Array3D(2)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(3)': WebGL2KernelValueSingleArray3DI,\n      'Array3D(4)': WebGL2KernelValueSingleArray3DI,\n      'Input': WebGL2KernelValueSingleInput,\n      'NumberTexture': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(1)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(2)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(3)': WebGL2KernelValueNumberTexture,\n      'ArrayTexture(4)': WebGL2KernelValueNumberTexture,\n      'MemoryOptimizedNumberTexture': WebGL2KernelValueMemoryOptimizedNumberTexture,\n      'HTMLCanvas': WebGL2KernelValueHTMLImage,\n      'OffscreenCanvas': WebGL2KernelValueHTMLImage,\n      'HTMLImage': WebGL2KernelValueHTMLImage,\n      'ImageBitmap': WebGL2KernelValueHTMLImage,\n      'ImageData': WebGL2KernelValueHTMLImage,\n      'HTMLImageArray': WebGL2KernelValueHTMLImageArray,\n      'HTMLVideo': WebGL2KernelValueHTMLVideo,\n    }\n  },\n};\n\nfunction lookupKernelValueType(type, dynamic, precision, value) {\n  if (!type) {\n    throw new Error('type missing');\n  }\n  if (!dynamic) {\n    throw new Error('dynamic missing');\n  }\n  if (!precision) {\n    throw new Error('precision missing');\n  }\n  if (value.type) {\n    type = value.type;\n  }\n  const types = kernelValueMaps[precision][dynamic];\n  if (types[type] === false) {\n    return null;\n  } else if (types[type] === undefined) {\n    throw new Error(`Could not find a KernelValue for ${ type }`);\n  }\n  return types[type];\n}\n\nmodule.exports = {\n  kernelValueMaps,\n  lookupKernelValueType\n};"
  },
  {
    "path": "src/backend/web-gl2/kernel.js",
    "content": "const { WebGLKernel } = require('../web-gl/kernel');\nconst { WebGL2FunctionNode } = require('./function-node');\nconst { FunctionBuilder } = require('../function-builder');\nconst { utils } = require('../../utils');\nconst { fragmentShader } = require('./fragment-shader');\nconst { vertexShader } = require('./vertex-shader');\nconst { lookupKernelValueType } = require('./kernel-value-maps');\n\nlet isSupported = null;\n/**\n *\n * @type {HTMLCanvasElement|OffscreenCanvas}\n */\nlet testCanvas = null;\n/**\n *\n * @type {WebGLRenderingContext}\n */\nlet testContext = null;\nlet testExtensions = null;\n\n/**\n *\n * @type {IKernelFeatures}\n */\nlet features = null;\n\n/**\n * @extends WebGLKernel\n */\nclass WebGL2Kernel extends WebGLKernel {\n  static get isSupported() {\n    if (isSupported !== null) {\n      return isSupported;\n    }\n    this.setupFeatureChecks();\n    isSupported = this.isContextMatch(testContext);\n    return isSupported;\n  }\n\n  static setupFeatureChecks() {\n    if (typeof document !== 'undefined') {\n      testCanvas = document.createElement('canvas');\n    } else if (typeof OffscreenCanvas !== 'undefined') {\n      testCanvas = new OffscreenCanvas(0, 0);\n    }\n    if (!testCanvas) return;\n    testContext = testCanvas.getContext('webgl2');\n    if (!testContext || !testContext.getExtension) return;\n    testExtensions = {\n      EXT_color_buffer_float: testContext.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'),\n    };\n    features = this.getFeatures();\n  }\n\n  static isContextMatch(context) {\n    // from global\n    if (typeof WebGL2RenderingContext !== 'undefined') {\n      return context instanceof WebGL2RenderingContext;\n    }\n    return false;\n  }\n\n  /**\n   *\n   * @return {IKernelFeatures}\n   */\n  static getFeatures() {\n    const gl = this.testContext;\n    return Object.freeze({\n      isFloatRead: this.getIsFloatRead(),\n      isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(),\n      isSpeedTacticSupported: this.getIsSpeedTacticSupported(),\n      kernelMap: true,\n      isTextureFloat: true,\n      isDrawBuffers: true,\n      channelCount: this.getChannelCount(),\n      maxTextureSize: this.getMaxTextureSize(),\n      lowIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT),\n      lowFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT),\n      mediumIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT),\n      mediumFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT),\n      highIntPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT),\n      highFloatPrecision: gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT),\n    });\n  }\n\n  static getIsTextureFloat() {\n    return true;\n  }\n\n  static getChannelCount() {\n    return testContext.getParameter(testContext.MAX_DRAW_BUFFERS);\n  }\n\n  static getMaxTextureSize() {\n    return testContext.getParameter(testContext.MAX_TEXTURE_SIZE);\n  }\n\n  static lookupKernelValueType(type, dynamic, precision, value) {\n    return lookupKernelValueType(type, dynamic, precision, value);\n  }\n\n  static get testCanvas() {\n    return testCanvas;\n  }\n\n  static get testContext() {\n    return testContext;\n  }\n\n  /**\n   *\n   * @returns {{isFloatRead: Boolean, isIntegerDivisionAccurate: Boolean, kernelMap: Boolean, isTextureFloat: Boolean}}\n   */\n  static get features() {\n    return features;\n  }\n\n  static get fragmentShader() {\n    return fragmentShader;\n  }\n  static get vertexShader() {\n    return vertexShader;\n  }\n\n  /**\n   *\n   * @return {WebGLRenderingContext|WebGL2RenderingContext}\n   */\n  initContext() {\n    const settings = {\n      alpha: false,\n      depth: false,\n      antialias: false\n    };\n    return this.canvas.getContext('webgl2', settings);\n  }\n\n  initExtensions() {\n    this.extensions = {\n      EXT_color_buffer_float: this.context.getExtension('EXT_color_buffer_float'),\n      OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'),\n    };\n  }\n\n  /**\n   * @desc Validate settings related to Kernel, such as dimensions size, and auto output support.\n   * @param {IArguments} args\n   */\n  validateSettings(args) {\n    if (!this.validate) {\n      this.texSize = utils.getKernelTextureSize({\n        optimizeFloatMemory: this.optimizeFloatMemory,\n        precision: this.precision,\n      }, this.output);\n      return;\n    }\n\n    const { features } = this.constructor;\n    if (this.precision === 'single' && !features.isFloatRead) {\n      throw new Error('Float texture outputs are not supported');\n    } else if (!this.graphical && this.precision === null) {\n      this.precision = features.isFloatRead ? 'single' : 'unsigned';\n    }\n\n    if (this.fixIntegerDivisionAccuracy === null) {\n      this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate;\n    } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) {\n      this.fixIntegerDivisionAccuracy = false;\n    }\n\n    this.checkOutput();\n\n    if (!this.output || this.output.length === 0) {\n      if (args.length !== 1) {\n        throw new Error('Auto output only supported for kernels with only one input');\n      }\n\n      const argType = utils.getVariableType(args[0], this.strictIntegers);\n      switch (argType) {\n        case 'Array':\n          this.output = utils.getDimensions(argType);\n          break;\n        case 'NumberTexture':\n        case 'MemoryOptimizedNumberTexture':\n        case 'ArrayTexture(1)':\n        case 'ArrayTexture(2)':\n        case 'ArrayTexture(3)':\n        case 'ArrayTexture(4)':\n          this.output = args[0].output;\n          break;\n        default:\n          throw new Error('Auto output not supported for input type: ' + argType);\n      }\n    }\n\n    if (this.graphical) {\n      if (this.output.length !== 2) {\n        throw new Error('Output must have 2 dimensions on graphical mode');\n      }\n\n      if (this.precision === 'single') {\n        console.warn('Cannot use graphical mode and single precision at the same time');\n        this.precision = 'unsigned';\n      }\n\n      this.texSize = utils.clone(this.output);\n      return;\n    } else if (!this.graphical && this.precision === null && features.isTextureFloat) {\n      this.precision = 'single';\n    }\n\n    this.texSize = utils.getKernelTextureSize({\n      optimizeFloatMemory: this.optimizeFloatMemory,\n      precision: this.precision,\n    }, this.output);\n\n    this.checkTextureSize();\n  }\n\n  translateSource() {\n    const functionBuilder = FunctionBuilder.fromKernel(this, WebGL2FunctionNode, {\n      fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy\n    });\n    this.translatedSource = functionBuilder.getPrototypeString('kernel');\n    this.setupReturnTypes(functionBuilder);\n  }\n\n  drawBuffers() {\n    this.context.drawBuffers(this.drawBuffersMap);\n  }\n\n  getTextureFormat() {\n    const { context: gl } = this;\n    switch (this.getInternalFormat()) {\n      case gl.R32F:\n        return gl.RED;\n      case gl.RG32F:\n        return gl.RG;\n      case gl.RGBA32F:\n        return gl.RGBA;\n      case gl.RGBA:\n        return gl.RGBA;\n      default:\n        throw new Error('Unknown internal format');\n    }\n  }\n  getInternalFormat() {\n    const { context: gl } = this;\n\n    if (this.precision === 'single') {\n      if (this.pipeline) {\n        switch (this.returnType) {\n          case 'Number':\n          case 'Float':\n          case 'Integer':\n            if (this.optimizeFloatMemory) {\n              return gl.RGBA32F;\n            } else {\n              return gl.R32F;\n            }\n          case 'Array(2)':\n            return gl.RG32F;\n          case 'Array(3)': // there is _no_ 3 channel format which is guaranteed to be color-renderable\n          case 'Array(4)':\n            return gl.RGBA32F;\n          default:\n            throw new Error('Unhandled return type');\n        }\n      }\n      return gl.RGBA32F;\n    }\n    return gl.RGBA;\n  }\n\n  _setupOutputTexture() {\n    const gl = this.context;\n    if (this.texture) {\n      // here we inherit from an already existing kernel, so go ahead and just bind textures to the framebuffer\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);\n      return;\n    }\n    gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n    const texture = gl.createTexture();\n    const texSize = this.texSize;\n    gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount);\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    const format = this.getInternalFormat();\n    if (this.precision === 'single') {\n      gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n    } else {\n      gl.texImage2D(gl.TEXTURE_2D, 0, format, texSize[0], texSize[1], 0, format, gl.UNSIGNED_BYTE, null);\n    }\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    this.texture = new this.TextureConstructor({\n      texture,\n      size: texSize,\n      dimensions: this.threadDim,\n      output: this.output,\n      context: this.context,\n      internalFormat: this.getInternalFormat(),\n      textureFormat: this.getTextureFormat(),\n      kernel: this,\n    });\n  }\n\n  _setupSubOutputTextures() {\n    const gl = this.context;\n    if (this.mappedTextures) {\n      // here we inherit from an already existing kernel, so go ahead and just bind textures to the framebuffer\n      for (let i = 0; i < this.subKernels.length; i++) {\n        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, this.mappedTextures[i].texture, 0);\n      }\n      return;\n    }\n    const texSize = this.texSize;\n    this.drawBuffersMap = [gl.COLOR_ATTACHMENT0];\n    this.mappedTextures = [];\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const texture = this.createTexture();\n      this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1);\n      gl.activeTexture(gl.TEXTURE0 + this.constantTextureCount + this.argumentTextureCount + i);\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      // TODO: upgrade this\n      const format = this.getInternalFormat();\n      if (this.precision === 'single') {\n        gl.texStorage2D(gl.TEXTURE_2D, 1, format, texSize[0], texSize[1]);\n        // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null);\n      } else {\n        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      }\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0);\n\n      this.mappedTextures.push(new this.TextureConstructor({\n        texture,\n        size: texSize,\n        dimensions: this.threadDim,\n        output: this.output,\n        context: this.context,\n        internalFormat: this.getInternalFormat(),\n        textureFormat: this.getTextureFormat(),\n        kernel: this,\n      }));\n    }\n  }\n\n  /**\n   *\n   * @desc Get the header string for the program.\n   * This returns an empty string if no sub-kernels are defined.\n   *\n   * @returns {String} result\n   */\n  _getHeaderString() {\n    return '';\n  }\n\n  /**\n   * @desc Get texture coordinate string for the program\n   * @returns {String} result\n   */\n  _getTextureCoordinate() {\n    const subKernels = this.subKernels;\n    const variablePrecision = this.getVariablePrecisionString(this.texSize, this.tactic);\n    if (subKernels === null || subKernels.length < 1) {\n      return `in ${ variablePrecision } vec2 vTexCoord;\\n`;\n    } else {\n      return `out ${ variablePrecision } vec2 vTexCoord;\\n`;\n    }\n  }\n\n  /**\n   * @desc Generate transpiled glsl Strings for user-defined parameters sent to a kernel\n   * @param {Array} args - The actual parameters sent to the Kernel\n   * @returns {String} result\n   */\n  _getMainArgumentsString(args) {\n    const result = [];\n    const argumentNames = this.argumentNames;\n    for (let i = 0; i < argumentNames.length; i++) {\n      result.push(this.kernelArguments[i].getSource(args[i]));\n    }\n    return result.join('');\n  }\n\n  /**\n   * @desc Get Kernel program string (in *glsl*) for a kernel.\n   * @returns {String} result\n   */\n  getKernelString() {\n    const result = [this.getKernelResultDeclaration()];\n    const subKernels = this.subKernels;\n    if (subKernels !== null) {\n      result.push(\n        'layout(location = 0) out vec4 data0'\n      );\n      switch (this.returnType) {\n        case 'Number':\n        case 'Float':\n        case 'Integer':\n          for (let i = 0; i < subKernels.length; i++) {\n            const subKernel = subKernels[i];\n            result.push(\n              subKernel.returnType === 'Integer' ?\n              `int subKernelResult_${ subKernel.name } = 0` :\n              `float subKernelResult_${ subKernel.name } = 0.0`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(2)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec2 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(3)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec3 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n        case 'Array(4)':\n          for (let i = 0; i < subKernels.length; i++) {\n            result.push(\n              `vec4 subKernelResult_${ subKernels[i].name }`,\n              `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }`\n            );\n          }\n          break;\n      }\n    } else {\n      result.push(\n        'out vec4 data0'\n      );\n    }\n\n    return utils.linesToString(result) + this.translatedSource;\n  }\n\n  getMainResultGraphical() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = actualColor',\n    ]);\n  }\n\n  getMainResultPackedPixels() {\n    switch (this.returnType) {\n      case 'LiteralInteger':\n      case 'Number':\n      case 'Integer':\n      case 'Float':\n        return this.getMainResultKernelPackedPixels() +\n          this.getMainResultSubKernelPackedPixels();\n      default:\n        throw new Error(`packed output only usable with Numbers, \"${this.returnType}\" specified`);\n    }\n  }\n\n  /**\n   * @return {String}\n   */\n  getMainResultKernelPackedPixels() {\n    return utils.linesToString([\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0 = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(kernelResult)`\n    ]);\n  }\n\n  /**\n   * @return {String}\n   */\n  getMainResultSubKernelPackedPixels() {\n    const result = [];\n    if (!this.subKernels) return '';\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(float(subKernelResult_${this.subKernels[i].name}))`\n        );\n      } else {\n        result.push(\n          `  data${i + 1} = ${this.useLegacyEncoder ? 'legacyEncode32' : 'encode32'}(subKernelResult_${this.subKernels[i].name})`\n        );\n      }\n    }\n    return utils.linesToString(result);\n  }\n\n  getMainResultKernelMemoryOptimizedFloats(result, channel) {\n    result.push(\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      `  data0.${channel} = kernelResult`\n    );\n  }\n\n  getMainResultSubKernelMemoryOptimizedFloats(result, channel) {\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; i++) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}.${channel} = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}.${channel} = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n  }\n\n  getMainResultKernelNumberTexture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelNumberTexture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      if (subKernel.returnType === 'Integer') {\n        result.push(\n          `  data${i + 1}[0] = float(subKernelResult_${subKernel.name})`\n        );\n      } else {\n        result.push(\n          `  data${i + 1}[0] = subKernelResult_${subKernel.name}`\n        );\n      }\n    }\n    return result;\n  }\n\n  getMainResultKernelArray2Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n    ];\n  }\n\n  getMainResultSubKernelArray2Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray3Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0[0] = kernelResult[0]',\n      '  data0[1] = kernelResult[1]',\n      '  data0[2] = kernelResult[2]',\n    ];\n  }\n\n  getMainResultSubKernelArray3Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      const subKernel = this.subKernels[i];\n      result.push(\n        `  data${i + 1}[0] = subKernelResult_${subKernel.name}[0]`,\n        `  data${i + 1}[1] = subKernelResult_${subKernel.name}[1]`,\n        `  data${i + 1}[2] = subKernelResult_${subKernel.name}[2]`\n      );\n    }\n    return result;\n  }\n\n  getMainResultKernelArray4Texture() {\n    return [\n      '  threadId = indexTo3D(index, uOutputDim)',\n      '  kernel()',\n      '  data0 = kernelResult',\n    ];\n  }\n\n  getMainResultSubKernelArray4Texture() {\n    const result = [];\n    if (!this.subKernels) return result;\n    for (let i = 0; i < this.subKernels.length; ++i) {\n      result.push(\n        `  data${i + 1} = subKernelResult_${this.subKernels[i].name}`\n      );\n    }\n    return result;\n  }\n\n  destroyExtensions() {\n    this.extensions.EXT_color_buffer_float = null;\n    this.extensions.OES_texture_float_linear = null;\n  }\n\n  /**\n   * @return {IKernelJSON}\n   */\n  toJSON() {\n    const json = super.toJSON();\n    json.functionNodes = FunctionBuilder.fromKernel(this, WebGL2FunctionNode).toJSON();\n    json.settings.threadDim = this.threadDim;\n    return json;\n  }\n}\n\nmodule.exports = {\n  WebGL2Kernel\n};"
  },
  {
    "path": "src/backend/web-gl2/vertex-shader.js",
    "content": "// language=GLSL\nconst vertexShader = `#version 300 es\n__FLOAT_TACTIC_DECLARATION__;\n__INT_TACTIC_DECLARATION__;\n__SAMPLER_2D_TACTIC_DECLARATION__;\n\nin vec2 aPos;\nin vec2 aTexCoord;\n\nout vec2 vTexCoord;\nuniform vec2 ratio;\n\nvoid main(void) {\n  gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1);\n  vTexCoord = aTexCoord;\n}`;\n\nmodule.exports = {\n  vertexShader\n};"
  },
  {
    "path": "src/browser-header.txt",
    "content": "/**\n * <%= pkg.name %>\n * <%= pkg.homepage %>\n *\n * <%= pkg.description %>\n *\n * @version <%= pkg.version %>\n * @date <%= new Date() %>\n *\n * @license <%= pkg.license %>\n * The MIT License\n *\n * Copyright (c) <%= new Date().getFullYear() %> gpu.js Team\n */"
  },
  {
    "path": "src/browser.js",
    "content": "const lib = require('./index');\nconst GPU = lib.GPU;\nfor (const p in lib) {\n  if (!lib.hasOwnProperty(p)) continue;\n  if (p === 'GPU') continue; //prevent recursive reference\n  GPU[p] = lib[p];\n}\n\nif (typeof window !== 'undefined') {\n  bindTo(window);\n}\nif (typeof self !== 'undefined') {\n  bindTo(self);\n}\n\nfunction bindTo(target) {\n  if (target.GPU) return;\n  Object.defineProperty(target, 'GPU', {\n    get() {\n      return GPU;\n    }\n  });\n}\n\nmodule.exports = lib;"
  },
  {
    "path": "src/gpu.js",
    "content": "const { gpuMock } = require('gpu-mock.js');\nconst { utils } = require('./utils');\nconst { Kernel } = require('./backend/kernel');\nconst { CPUKernel } = require('./backend/cpu/kernel');\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelRunShortcut } = require('./kernel-run-shortcut');\n\n\n/**\n *\n * @type {Array.<Kernel>}\n */\nconst kernelOrder = [HeadlessGLKernel, WebGL2Kernel, WebGLKernel];\n\n/**\n *\n * @type {string[]}\n */\nconst kernelTypes = ['gpu', 'cpu'];\n\nconst internalKernels = {\n  'headlessgl': HeadlessGLKernel,\n  'webgl2': WebGL2Kernel,\n  'webgl': WebGLKernel,\n};\n\nlet validate = true;\n\n/**\n * The GPU.js library class which manages the GPU context for the creating kernels\n * @class\n * @return {GPU}\n */\nclass GPU {\n  static disableValidation() {\n    validate = false;\n  }\n\n  static enableValidation() {\n    validate = true;\n  }\n\n  static get isGPUSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported);\n  }\n\n  /**\n   *\n   * @returns {boolean}\n   */\n  static get isKernelMapSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.kernelMap);\n  }\n\n  /**\n   * @desc TRUE is platform supports OffscreenCanvas\n   */\n  static get isOffscreenCanvasSupported() {\n    return (typeof Worker !== 'undefined' && typeof OffscreenCanvas !== 'undefined') || typeof importScripts !== 'undefined';\n  }\n\n  /**\n   * @desc TRUE if platform supports WebGL\n   */\n  static get isWebGLSupported() {\n    return WebGLKernel.isSupported;\n  }\n\n  /**\n   * @desc TRUE if platform supports WebGL2\n   */\n  static get isWebGL2Supported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  /**\n   * @desc TRUE if platform supports HeadlessGL\n   */\n  static get isHeadlessGLSupported() {\n    return HeadlessGLKernel.isSupported;\n  }\n\n  /**\n   *\n   * @desc TRUE if platform supports Canvas\n   */\n  static get isCanvasSupported() {\n    return typeof HTMLCanvasElement !== 'undefined';\n  }\n\n  /**\n   * @desc TRUE if platform supports HTMLImageArray}\n   */\n  static get isGPUHTMLImageArraySupported() {\n    return WebGL2Kernel.isSupported;\n  }\n\n  /**\n   * @desc TRUE if platform supports single precision}\n   * @returns {boolean}\n   */\n  static get isSinglePrecisionSupported() {\n    return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.isFloatRead && Kernel.features.isTextureFloat);\n  }\n\n  /**\n   * Creates an instance of GPU.\n   * @param {IGPUSettings} [settings] - Settings to set mode, and other properties\n   * @constructor\n   */\n  constructor(settings) {\n    settings = settings || {};\n    this.canvas = settings.canvas || null;\n    this.context = settings.context || null;\n    this.mode = settings.mode;\n    this.Kernel = null;\n    this.kernels = [];\n    this.functions = [];\n    this.nativeFunctions = [];\n    this.injectedNative = null;\n    if (this.mode === 'dev') return;\n    this.chooseKernel();\n    // add functions from settings\n    if (settings.functions) {\n      for (let i = 0; i < settings.functions.length; i++) {\n        this.addFunction(settings.functions[i]);\n      }\n    }\n\n    // add native functions from settings\n    if (settings.nativeFunctions) {\n      for (const p in settings.nativeFunctions) {\n        if (!settings.nativeFunctions.hasOwnProperty(p)) continue;\n        const s = settings.nativeFunctions[p];\n        const { name, source } = s;\n        this.addNativeFunction(name, source, s);\n      }\n    }\n  }\n\n  /**\n   * Choose kernel type and save on .Kernel property of GPU\n   */\n  chooseKernel() {\n    if (this.Kernel) return;\n\n    /**\n     *\n     * @type {WebGLKernel|WebGL2Kernel|HeadlessGLKernel|CPUKernel}\n     */\n    let Kernel = null;\n\n    if (this.context) {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        const ExternalKernel = kernelOrder[i];\n        if (ExternalKernel.isContextMatch(this.context)) {\n          if (!ExternalKernel.isSupported) {\n            throw new Error(`Kernel type ${ExternalKernel.name} not supported`);\n          }\n          Kernel = ExternalKernel;\n          break;\n        }\n      }\n      if (Kernel === null) {\n        throw new Error('unknown Context');\n      }\n    } else if (this.mode) {\n      if (this.mode in internalKernels) {\n        if (!validate || internalKernels[this.mode].isSupported) {\n          Kernel = internalKernels[this.mode];\n        }\n      } else if (this.mode === 'gpu') {\n        for (let i = 0; i < kernelOrder.length; i++) {\n          if (kernelOrder[i].isSupported) {\n            Kernel = kernelOrder[i];\n            break;\n          }\n        }\n      } else if (this.mode === 'cpu') {\n        Kernel = CPUKernel;\n      }\n      if (!Kernel) {\n        throw new Error(`A requested mode of \"${this.mode}\" and is not supported`);\n      }\n    } else {\n      for (let i = 0; i < kernelOrder.length; i++) {\n        if (kernelOrder[i].isSupported) {\n          Kernel = kernelOrder[i];\n          break;\n        }\n      }\n      if (!Kernel) {\n        Kernel = CPUKernel;\n      }\n    }\n\n    if (!this.mode) {\n      this.mode = Kernel.mode;\n    }\n    this.Kernel = Kernel;\n  }\n\n  /**\n   * @desc This creates a callable function object to call the kernel function with the argument parameter set\n   * @param {Function|String|object} source - The calling to perform the conversion\n   * @param {IGPUKernelSettings} [settings] - The parameter configuration object\n   * @return {IKernelRunShortcut} callable function to run\n   */\n  createKernel(source, settings) {\n    if (typeof source === 'undefined') {\n      throw new Error('Missing source parameter');\n    }\n    if (typeof source !== 'object' && !utils.isFunction(source) && typeof source !== 'string') {\n      throw new Error('source parameter not a function');\n    }\n\n    const kernels = this.kernels;\n    if (this.mode === 'dev') {\n      const devKernel = gpuMock(source, upgradeDeprecatedCreateKernelSettings(settings));\n      kernels.push(devKernel);\n      return devKernel;\n    }\n\n    source = typeof source === 'function' ? source.toString() : source;\n    const switchableKernels = {};\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings) || {};\n    // handle conversion of argumentTypes\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    function onRequestFallback(args) {\n      console.warn('Falling back to CPU');\n      const fallbackKernel = new CPUKernel(source, {\n        argumentTypes: kernelRun.argumentTypes,\n        constantTypes: kernelRun.constantTypes,\n        graphical: kernelRun.graphical,\n        loopMaxIterations: kernelRun.loopMaxIterations,\n        constants: kernelRun.constants,\n        dynamicOutput: kernelRun.dynamicOutput,\n        dynamicArgument: kernelRun.dynamicArguments,\n        output: kernelRun.output,\n        precision: kernelRun.precision,\n        pipeline: kernelRun.pipeline,\n        immutable: kernelRun.immutable,\n        optimizeFloatMemory: kernelRun.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: kernelRun.fixIntegerDivisionAccuracy,\n        functions: kernelRun.functions,\n        nativeFunctions: kernelRun.nativeFunctions,\n        injectedNative: kernelRun.injectedNative,\n        subKernels: kernelRun.subKernels,\n        strictIntegers: kernelRun.strictIntegers,\n        debug: kernelRun.debug,\n      });\n      fallbackKernel.build.apply(fallbackKernel, args);\n      const result = fallbackKernel.run.apply(fallbackKernel, args);\n      kernelRun.replaceKernel(fallbackKernel);\n      return result;\n    }\n\n    /**\n     *\n     * @param {IReason[]} reasons\n     * @param {IArguments} args\n     * @param {Kernel} _kernel\n     * @returns {*}\n     */\n    function onRequestSwitchKernel(reasons, args, _kernel) {\n      if (_kernel.debug) {\n        console.warn('Switching kernels');\n      }\n      let newOutput = null;\n      if (_kernel.signature && !switchableKernels[_kernel.signature]) {\n        switchableKernels[_kernel.signature] = _kernel;\n      }\n      if (_kernel.dynamicOutput) {\n        for (let i = reasons.length - 1; i >= 0; i--) {\n          const reason = reasons[i];\n          if (reason.type === 'outputPrecisionMismatch') {\n            newOutput = reason.needed;\n          }\n        }\n      }\n\n      const Constructor = _kernel.constructor;\n      const argumentTypes = Constructor.getArgumentTypes(_kernel, args);\n      const signature = Constructor.getSignature(_kernel, argumentTypes);\n      const existingKernel = switchableKernels[signature];\n      if (existingKernel) {\n        existingKernel.onActivate(_kernel);\n        return existingKernel;\n      }\n\n      const newKernel = switchableKernels[signature] = new Constructor(source, {\n        argumentTypes,\n        constantTypes: _kernel.constantTypes,\n        graphical: _kernel.graphical,\n        loopMaxIterations: _kernel.loopMaxIterations,\n        constants: _kernel.constants,\n        dynamicOutput: _kernel.dynamicOutput,\n        dynamicArgument: _kernel.dynamicArguments,\n        context: _kernel.context,\n        canvas: _kernel.canvas,\n        output: newOutput || _kernel.output,\n        precision: _kernel.precision,\n        pipeline: _kernel.pipeline,\n        immutable: _kernel.immutable,\n        optimizeFloatMemory: _kernel.optimizeFloatMemory,\n        fixIntegerDivisionAccuracy: _kernel.fixIntegerDivisionAccuracy,\n        functions: _kernel.functions,\n        nativeFunctions: _kernel.nativeFunctions,\n        injectedNative: _kernel.injectedNative,\n        subKernels: _kernel.subKernels,\n        strictIntegers: _kernel.strictIntegers,\n        debug: _kernel.debug,\n        gpu: _kernel.gpu,\n        validate,\n        returnType: _kernel.returnType,\n        tactic: _kernel.tactic,\n        onRequestFallback,\n        onRequestSwitchKernel,\n        texture: _kernel.texture,\n        mappedTextures: _kernel.mappedTextures,\n        drawBuffersMap: _kernel.drawBuffersMap,\n      });\n      newKernel.build.apply(newKernel, args);\n      kernelRun.replaceKernel(newKernel);\n      kernels.push(newKernel);\n      return newKernel;\n    }\n    const mergedSettings = Object.assign({\n      context: this.context,\n      canvas: this.canvas,\n      functions: this.functions,\n      nativeFunctions: this.nativeFunctions,\n      injectedNative: this.injectedNative,\n      gpu: this,\n      validate,\n      onRequestFallback,\n      onRequestSwitchKernel\n    }, settingsCopy);\n\n    const kernel = new this.Kernel(source, mergedSettings);\n    const kernelRun = kernelRunShortcut(kernel);\n\n    //if canvas didn't come from this, propagate from kernel\n    if (!this.canvas) {\n      this.canvas = kernel.canvas;\n    }\n\n    //if context didn't come from this, propagate from kernel\n    if (!this.context) {\n      this.context = kernel.context;\n    }\n\n    kernels.push(kernel);\n\n    return kernelRun;\n  }\n\n  /**\n   *\n   * Create a super kernel which executes sub kernels\n   * and saves their output to be used with the next sub kernel.\n   * This can be useful if we want to save the output on one kernel,\n   * and then use it as an input to another kernel. *Machine Learning*\n   *\n   * @param {Object|Array} subKernels - Sub kernels for this kernel\n   * @param {Function} rootKernel - Root kernel\n   *\n   * @returns {Function} callable kernel function\n   *\n   * @example\n   * const megaKernel = gpu.createKernelMap({\n   *   addResult: function add(a, b) {\n   *     return a[this.thread.x] + b[this.thread.x];\n   *   },\n   *   multiplyResult: function multiply(a, b) {\n   *     return a[this.thread.x] * b[this.thread.x];\n   *   },\n   *  }, function(a, b, c) {\n   *       return multiply(add(a, b), c);\n   * });\n   *\n   * megaKernel(a, b, c);\n   *\n   * Note: You can also define subKernels as an array of functions.\n   * > [add, multiply]\n   *\n   */\n  createKernelMap() {\n    let fn;\n    let settings;\n    const argument2Type = typeof arguments[arguments.length - 2];\n    if (argument2Type === 'function' || argument2Type === 'string') {\n      fn = arguments[arguments.length - 2];\n      settings = arguments[arguments.length - 1];\n    } else {\n      fn = arguments[arguments.length - 1];\n    }\n\n    if (this.mode !== 'dev') {\n      if (!this.Kernel.isSupported || !this.Kernel.features.kernelMap) {\n        if (this.mode && kernelTypes.indexOf(this.mode) < 0) {\n          throw new Error(`kernelMap not supported on ${this.Kernel.name}`);\n        }\n      }\n    }\n\n    const settingsCopy = upgradeDeprecatedCreateKernelSettings(settings);\n    // handle conversion of argumentTypes\n    if (settings && typeof settings.argumentTypes === 'object') {\n      settingsCopy.argumentTypes = Object.keys(settings.argumentTypes).map(argumentName => settings.argumentTypes[argumentName]);\n    }\n\n    if (Array.isArray(arguments[0])) {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let i = 0; i < functions.length; i++) {\n        const source = functions[i].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name,\n          source,\n          property: i,\n        });\n      }\n    } else {\n      settingsCopy.subKernels = [];\n      const functions = arguments[0];\n      for (let p in functions) {\n        if (!functions.hasOwnProperty(p)) continue;\n        const source = functions[p].toString();\n        const name = utils.getFunctionNameFromString(source);\n        settingsCopy.subKernels.push({\n          name: name || p,\n          source,\n          property: p,\n        });\n      }\n    }\n    return this.createKernel(fn, settingsCopy);\n  }\n\n  /**\n   *\n   * Combine different kernels into one super Kernel,\n   * useful to perform multiple operations inside one\n   * kernel without the penalty of data transfer between\n   * cpu and gpu.\n   *\n   * The number of kernel functions sent to this method can be variable.\n   * You can send in one, two, etc.\n   *\n   * @param {Function} subKernels - Kernel function(s) to combine.\n   * @param {Function} rootKernel - Root kernel to combine kernels into\n   *\n   * @example\n   *   combineKernels(add, multiply, function(a,b,c){\n   *     return add(multiply(a,b), c)\n   *  })\n   *\n   * @returns {Function} Callable kernel function\n   *\n   */\n  combineKernels() {\n    const firstKernel = arguments[0];\n    const combinedKernel = arguments[arguments.length - 1];\n    if (firstKernel.kernel.constructor.mode === 'cpu') return combinedKernel;\n    const canvas = arguments[0].canvas;\n    const context = arguments[0].context;\n    const max = arguments.length - 1;\n    for (let i = 0; i < max; i++) {\n      arguments[i]\n        .setCanvas(canvas)\n        .setContext(context)\n        .setPipeline(true);\n    }\n\n    return function() {\n      const texture = combinedKernel.apply(this, arguments);\n      if (texture.toArray) {\n        return texture.toArray();\n      }\n      return texture;\n    };\n  }\n\n  setFunctions(functions) {\n    this.functions = functions;\n    return this;\n  }\n\n  setNativeFunctions(nativeFunctions) {\n    this.nativeFunctions = nativeFunctions;\n    return this;\n  }\n\n  /**\n   * @desc Adds additional functions, that the kernel may call.\n   * @param {Function|String} source - Javascript function to convert\n   * @param {IFunctionSettings} [settings]\n   * @returns {GPU} returns itself\n   */\n  addFunction(source, settings) {\n    this.functions.push({ source, settings });\n    return this;\n  }\n\n  /**\n   * @desc Adds additional native functions, that the kernel may call.\n   * @param {String} name - native function name, used for reverse lookup\n   * @param {String} source - the native function implementation, as it would be defined in it's entirety\n   * @param {object} [settings]\n   * @returns {GPU} returns itself\n   */\n  addNativeFunction(name, source, settings) {\n    if (this.kernels.length > 0) {\n      throw new Error('Cannot call \"addNativeFunction\" after \"createKernels\" has been called.');\n    }\n    this.nativeFunctions.push(Object.assign({ name, source }, settings));\n    return this;\n  }\n\n  /**\n   * Inject a string just before translated kernel functions\n   * @param {String} source\n   * @return {GPU}\n   */\n  injectNative(source) {\n    this.injectedNative = source;\n    return this;\n  }\n\n  /**\n   * @desc Destroys all memory associated with gpu.js & the webGl if we created it\n   * @return {Promise}\n   * @resolve {void}\n   * @reject {Error}\n   */\n  destroy() {\n    return new Promise((resolve, reject) => {\n      if (!this.kernels) {\n        resolve();\n      }\n      // perform on next run loop - for some reason we dont get lose context events\n      // if webGl is created and destroyed in the same run loop.\n      setTimeout(() => {\n        try {\n          for (let i = 0; i < this.kernels.length; i++) {\n            this.kernels[i].destroy(true); // remove canvas if exists\n          }\n          // all kernels are associated with one context, go ahead and take care of it here\n          let firstKernel = this.kernels[0];\n          if (firstKernel) {\n            // if it is shortcut\n            if (firstKernel.kernel) {\n              firstKernel = firstKernel.kernel;\n            }\n            if (firstKernel.constructor.destroyContext) {\n              firstKernel.constructor.destroyContext(this.context);\n            }\n          }\n        } catch (e) {\n          reject(e);\n        }\n        resolve();\n      }, 0);\n    });\n  }\n}\n\n\nfunction upgradeDeprecatedCreateKernelSettings(settings) {\n  if (!settings) {\n    return {};\n  }\n  const upgradedSettings = Object.assign({}, settings);\n\n  if (settings.hasOwnProperty('floatOutput')) {\n    utils.warnDeprecated('setting', 'floatOutput', 'precision');\n    upgradedSettings.precision = settings.floatOutput ? 'single' : 'unsigned';\n  }\n  if (settings.hasOwnProperty('outputToTexture')) {\n    utils.warnDeprecated('setting', 'outputToTexture', 'pipeline');\n    upgradedSettings.pipeline = Boolean(settings.outputToTexture);\n  }\n  if (settings.hasOwnProperty('outputImmutable')) {\n    utils.warnDeprecated('setting', 'outputImmutable', 'immutable');\n    upgradedSettings.immutable = Boolean(settings.outputImmutable);\n  }\n  if (settings.hasOwnProperty('floatTextures')) {\n    utils.warnDeprecated('setting', 'floatTextures', 'optimizeFloatMemory');\n    upgradedSettings.optimizeFloatMemory = Boolean(settings.floatTextures);\n  }\n  return upgradedSettings;\n}\n\nmodule.exports = {\n  GPU,\n  kernelOrder,\n  kernelTypes\n};"
  },
  {
    "path": "src/index.d.ts",
    "content": "export class GPU {\n  static isGPUSupported: boolean;\n  static isCanvasSupported: boolean;\n  static isHeadlessGLSupported: boolean;\n  static isWebGLSupported: boolean;\n  static isWebGL2Supported: boolean;\n  static isKernelMapSupported: boolean;\n  static isOffscreenCanvasSupported: boolean;\n  static isGPUHTMLImageArraySupported: boolean;\n  static isSinglePrecisionSupported: boolean;\n  constructor(settings?: IGPUSettings);\n  functions: GPUFunction<ThreadKernelVariable[]>[];\n  nativeFunctions: IGPUNativeFunction[];\n  setFunctions(flag: any): this;\n  setNativeFunctions(flag: IGPUNativeFunction[]): this;\n  addFunction<ArgTypes extends ThreadKernelVariable[] = ThreadKernelVariable[], ConstantsType = {}>(kernel: GPUFunction<ArgTypes, ConstantsType>, settings?: IGPUFunctionSettings): this;\n  addNativeFunction(name: string, source: string, settings?: IGPUFunctionSettings): this;\n  combineKernels(...kernels: KernelFunction[]): IKernelRunShortcut;\n  combineKernels<KF extends KernelFunction>(...kernels: KF[]):\n    ((...args: Parameters<KF>) =>\n      ReturnType<KF>[]\n      | ReturnType<KF>[][]\n      | ReturnType<KF>[][][]\n      | Texture\n      | void\n      )\n    & IKernelRunShortcutBase;\n  createKernel<ArgTypes extends ThreadKernelVariable[], ConstantsT extends IConstantsThis>(kernel: KernelFunction<ArgTypes, ConstantsT>, settings?: IGPUKernelSettings): IKernelRunShortcut;\n  createKernel<KernelType extends KernelFunction>(kernel: KernelType, settings?: IGPUKernelSettings):\n    ((...args: Parameters<KernelType>) =>\n      ReturnType<KernelType>[]\n      | ReturnType<KernelType>[][]\n      | ReturnType<KernelType>[][][]\n      | Texture\n      | void\n      )\n    & IKernelRunShortcutBase;\n  createKernelMap<\n    ArgTypes extends ThreadKernelVariable[],\n    ConstantsType = null,\n    >(\n    subKernels: ISubKernelObject,\n    rootKernel: ThreadFunction<ArgTypes, ConstantsType>,\n    settings?: IGPUKernelSettings): (((this: IKernelFunctionThis<ConstantsType>, ...args: ArgTypes) => IMappedKernelResult) & IKernelMapRunShortcut<typeof subKernels>);\n  destroy(): Promise<void>;\n  Kernel: typeof Kernel;\n  mode: string;\n  canvas: any;\n  context: any;\n}\n\nexport interface ISubKernelObject {\n  [targetLocation: string]:\n    ((...args: ThreadKernelVariable[]) => ThreadFunctionResult)\n    | ((...args: any[]) => ThreadFunctionResult);\n}\n\nexport interface ISubKernelArray {\n  [index: number]:\n    ((...args: ThreadKernelVariable[]) => ThreadFunctionResult)\n    | ((...args: any[]) => ThreadFunctionResult);\n}\n\nexport interface ISubKernelsResults {\n  [resultsLocation: string]: KernelOutput;\n}\n\nexport interface IGPUFunction extends IFunctionSettings {\n  source: string;\n}\n\nexport interface IGPUNativeFunction extends IGPUFunctionSettings {\n  name: string;\n  source: string;\n}\n\nexport interface IMappedKernelResult {\n  result?: KernelVariable;\n  [targetLocation: string]: KernelVariable\n}\n\nexport interface INativeFunction extends IGPUFunctionSettings {\n  name: string;\n  source: string;\n}\n\nexport interface IInternalNativeFunction extends IArgumentTypes {\n  name: string;\n  source: string;\n}\n\nexport interface INativeFunctionList {\n  [name: string]: INativeFunction\n}\n\nexport type GPUMode = 'gpu' | 'cpu' | 'dev';\nexport type GPUInternalMode = 'webgl' | 'webgl2' | 'headlessgl';\n\nexport interface IGPUSettings {\n  mode?: GPUMode | GPUInternalMode;\n  canvas?: object;\n  context?: object;\n  functions?: KernelFunction[];\n  nativeFunctions?: IInternalNativeFunction[];\n  // format: 'Float32Array' | 'Float16Array' | 'Float' // WE WANT THIS!\n}\n\nexport type GPUVariableType\n  = 'Array'\n  | 'Array(2)'\n  | 'Array(3)'\n  | 'Array(4)'\n  | 'Array1D(2)'\n  | 'Array2D(2)'\n  | 'Array3D(2)'\n  | 'Array1D(3)'\n  | 'Array2D(3)'\n  | 'Array3D(3)'\n  | 'Array1D(4)'\n  | 'Array2D(4)'\n  | 'Array3D(4)'\n  | 'Boolean'\n  | 'HTMLCanvas'\n  | 'HTMLImage'\n  | 'HTMLImageArray'\n  | 'Number'\n  | 'Float'\n  | 'Integer'\n  | GPUTextureType;\n\nexport type GPUTextureType\n  = 'NumberTexture'\n  | 'ArrayTexture(4)';\n\nexport interface IGPUArgumentTypes {\n  [argumentName: string]: GPUVariableType;\n}\n\nexport interface IGPUFunctionSettings {\n  argumentTypes?: IGPUArgumentTypes | string[],\n  returnType?: GPUVariableType;\n}\n\nexport class Kernel {\n  static isSupported: boolean;\n  static isContextMatch(context: any): boolean;\n  static disableValidation(): void;\n  static enableValidation(): void;\n  static nativeFunctionArguments(source: string): IArgumentTypes;\n  static nativeFunctionReturnType(source: string): string;\n  static destroyContext(context: any): void;\n  static features: IKernelFeatures;\n  static getFeatures(): IKernelFeatures;\n  static mode: GPUMode | GPUInternalMode;\n  source: string | IKernelJSON;\n  Kernel: Kernel;\n  output: number[];\n  debug: boolean;\n  graphical: boolean;\n  loopMaxIterations: number;\n  constants: IConstants;\n  canvas: any;\n  context: WebGLRenderingContext | any;\n  functions: IFunction[];\n  nativeFunctions: IInternalNativeFunction[];\n  subKernels: ISubKernel[];\n  validate: boolean;\n  immutable: boolean;\n  pipeline: boolean;\n  plugins: IPlugin[];\n  useLegacyEncoder: boolean;\n  tactic: Tactic;\n  built: boolean;\n  texSize: [number, number];\n  texture: Texture;\n  mappedTextures?: Texture[];\n  TextureConstructor: typeof Texture;\n  getPixels(flip?: boolean): Uint8ClampedArray[];\n  getVariablePrecisionString(textureSize?: number[], tactic?: Tactic, isInt?: boolean): string;\n  prependString(value: string): void;\n  hasPrependString(value: string): boolean;\n  constructor(kernel: KernelFunction|IKernelJSON|string, settings?: IDirectKernelSettings);\n  onRequestSwitchKernel?: Kernel;\n  onActivate(previousKernel: Kernel): void;\n  build(...args: KernelVariable[]): void;\n  run(...args: KernelVariable[]): KernelVariable;\n  toString(...args: KernelVariable[]): string;\n  toJSON(): IKernelJSON;\n  setOutput(flag: number[]): this;\n  setWarnVarUsage(flag: boolean): this;\n  setOptimizeFloatMemory(flag: boolean): this;\n  setArgumentTypes(flag: IKernelValueTypes): this;\n  setDebug(flag: boolean): this;\n  setGraphical(flag: boolean): this;\n  setLoopMaxIterations(flag: number): this;\n  setConstants(flag: IConstants): this;\n  setConstants<T>(flag: T & IConstants): this;\n  setConstantTypes(flag: IKernelValueTypes): this;\n  setDynamicOutput(flag: boolean): this;\n  setDynamicArguments(flag: boolean): this;\n  setPipeline(flag: boolean): this;\n  setPrecision(flag: Precision): this;\n  setImmutable(flag: boolean): this;\n  setCanvas(flag: any): this;\n  setContext(flag: any): this;\n  addFunction<ArgTypes extends ThreadKernelVariable[]>(flag: GPUFunction<ArgTypes>, settings?: IFunctionSettings): this;\n  setFunctions(flag: any): this;\n  setNativeFunctions(flag: IGPUNativeFunction[]): this;\n  setStrictIntegers(flag: boolean): this;\n  setTactic(flag: Tactic): this;\n  setUseLegacyEncoder(flag: boolean): this;\n  addSubKernel(subKernel: ISubKernel): this;\n  destroy(removeCanvasReferences?: boolean): void;\n  validateSettings(args: IArguments): void;\n\n  setUniform1f(name: string, value: number): void;\n  setUniform2f(name: string, value1: number, value2: number): void;\n  setUniform3f(name: string, value1: number, value2: number, value3: number): void;\n  setUniform4f(name: string, value1: number, value2: number, value3: number, value4: number): void;\n\n  setUniform2fv(name: string, value: [number, number]): void;\n  setUniform3fv(name: string, value: [number, number, number]): void;\n  setUniform4fv(name: string, value: [number, number, number, number]): void;\n\n  setUniform1i(name: string, value: number): void;\n  setUniform2i(name: string, value1: number, value2: number): void;\n  setUniform3i(name: string, value1: number, value2: number, value3: number): void;\n  setUniform4i(name: string, value1: number, value2: number, value3: number, value4: number): void;\n\n  setUniform2iv(name: string, value: [number, number]): void;\n  setUniform3iv(name: string, value: [number, number, number]): void;\n  setUniform4iv(name: string, value: [number, number, number, number]): void;\n}\n\n\nexport type GPUFunction<ArgTypes extends ThreadKernelVariable[] = ThreadKernelVariable[], ConstantsType = {}>\n  = ThreadFunction<ArgTypes, ConstantsType>\n  | IFunction\n  | IGPUFunction\n  |  string[];\n\nexport type ThreadFunction<ArgTypes extends ThreadKernelVariable[] = ThreadKernelVariable[], ConstantsType = {}> =\n  ((this: IKernelFunctionThis<ConstantsType>, ...args: ArgTypes) => ThreadFunctionResult);\n\nexport type Precision = 'single' | 'unsigned';\n\nexport class CPUKernel extends Kernel {\n\n}\nexport class GLKernel extends Kernel {\n\n}\nexport class WebGLKernel extends GLKernel {\n\n}\nexport class WebGL2Kernel extends WebGLKernel {\n\n}\nexport class HeadlessGLKernel extends WebGLKernel {\n\n}\n\nexport interface IArgumentTypes {\n  argumentTypes: GPUVariableType[],\n  argumentNames: string[],\n}\n\nexport interface IConstants {\n  [constantName: string]: KernelVariable;\n}\n\nexport interface IKernelValueTypes {\n  [constantType: string]: GPUVariableType;\n}\n\nexport interface IWebGLKernelValueSettings extends IKernelValueSettings {\n  onRequestTexture: () => object;\n  onRequestIndex: () => number;\n  onRequestContextHandle: () => number;\n  texture: any;\n}\n\nexport interface IKernelValueSettings {\n  name: string;\n  kernel: Kernel;\n  context: WebGLRenderingContext;\n  contextHandle?: number;\n  checkContext?: boolean;\n  onRequestContextHandle: () => number;\n  onUpdateValueMismatch: (constructor: object) => void;\n  origin: 'user' | 'constants';\n  strictIntegers?: boolean;\n  type: GPUVariableType;\n  tactic?: Tactic;\n  size: number[];\n  index?: number;\n}\n\nexport type Tactic = 'speed' | 'balanced' | 'precision';\n\nexport interface IConstantsThis {\n  [constantName: string]: ThreadKernelVariable;\n}\n\nexport interface IKernelXYZ {\n  x: number;\n  y: number;\n  z: number;\n}\n\nexport interface FunctionList {\n  [functionName: string]: Function\n}\n\nexport interface IGPUKernelSettings extends IKernelSettings {\n  argumentTypes?: ITypesList;\n  functions?: Function[]|FunctionList;\n  tactic?: Tactic;\n  onRequestSwitchKernel?: Kernel;\n}\n\nexport interface IKernelSettings {\n  pluginNames?: string[];\n  output?: number[] | IKernelXYZ;\n  precision?: Precision;\n  constants?: object;\n  context?: any;\n  canvas?: any;\n  pipeline?: boolean;\n  immutable?: boolean;\n  graphical?: boolean;\n  onRequestFallback?: () => Kernel;\n  optimizeFloatMemory?: boolean;\n  dynamicOutput?: boolean;\n  dynamicArguments?: boolean;\n  constantTypes?: ITypesList;\n  useLegacyEncoder?: boolean;\n  nativeFunctions?: IGPUNativeFunction[],\n  strictIntegers?: boolean;\n}\n\nexport interface IDirectKernelSettings extends IKernelSettings {\n  argumentTypes?: string[];\n  functions?: string[]|IFunction;\n}\n\nexport interface ITypesList {\n  [typeName: string]: GPUVariableType\n}\n\nexport interface IKernelRunShortcutBase<T = KernelOutput> extends Kernel {\n  kernel: Kernel;\n  (...args: KernelVariable[]): T;\n  exec(): Promise<T>;\n}\n\nexport interface IKernelRunShortcut extends IKernelRunShortcutBase {\n\n}\n\nexport interface IKernelMapRunShortcut<SubKernelType> extends IKernelRunShortcutBase<\n  { result: KernelOutput } & { [key in keyof SubKernelType]: KernelOutput }> {}\n\nexport interface IKernelFeatures {\n  isFloatRead: boolean;\n  kernelMap: boolean;\n  isIntegerDivisionAccurate: boolean;\n  isSpeedTacticSupported: boolean;\n  isTextureFloat: boolean;\n  isDrawBuffers: boolean;\n  channelCount: number;\n  maxTextureSize: number;\n  lowIntPrecision: { rangeMax: number };\n  mediumIntPrecision: { rangeMax: number };\n  highIntPrecision: { rangeMax: number };\n  lowFloatPrecision: { rangeMax: number };\n  mediumFloatPrecision: { rangeMax: number };\n  highFloatPrecision: { rangeMax: number };\n}\n\nexport interface IKernelFunctionThis<ConstantsT = {}> {\n  output: IKernelXYZ;\n  thread: IKernelXYZ;\n  constants: ConstantsT;\n  color(r: number): void,\n  color(r: number, g: number): void,\n  color(r: number, g: number, b: number): void,\n  color(r: number, g: number, b: number, a: number): void,\n}\n\nexport type KernelVariable =\n  boolean\n  | number\n  | Texture\n  | Input\n  | HTMLCanvasElement\n  | OffscreenCanvas\n  | HTMLVideoElement\n  | HTMLImageElement\n  | HTMLImageElement[]\n  | ImageBitmap\n  | ImageData\n  | Float32Array\n  | Uint8Array\n  | Uint16Array\n  | Uint32Array\n  | Uint8ClampedArray\n  | KernelOutput;\n\nexport type ThreadFunctionResult\n  = number\n  | number[]\n  | number[][]\n  | [number, number]\n  | [number, number, number]\n  | [number, number, number, number]\n  | Pixel\n  | Boolean;\n\nexport type ThreadKernelVariable\n  = boolean\n  | number\n  | number[]\n  | number[][]\n  | number[][][]\n\n  | Float32Array\n  | Float32Array[]\n  | Float32Array[][]\n\n  | Pixel\n  | Pixel[][]\n\n  | [number, number]\n  | [number, number][]\n  | [number, number][][]\n  | [number, number][][][]\n\n  | [number, number, number]\n  | [number, number, number][]\n  | [number, number, number][][]\n  | [number, number, number][][][]\n\n  | [number, number, number, number]\n  | [number, number, number, number][]\n  | [number, number, number, number][][]\n  | [number, number, number, number][][][]\n  ;\n\nexport type Pixel = {\n  r: number;\n  g: number;\n  b: number;\n  a: number;\n};\n\n// export type KernelFunction<ArgT extends ThreadKernelVariable[] = ThreadKernelVariable[], ConstantsT extends IConstantsThis = {}> = ((\n//   this: IKernelFunctionThis<ConstantsT>,\n//   ...args: ArgT\n// ) => KernelOutput);\n\nexport interface KernelFunction<ArgT extends ThreadKernelVariable[] = ThreadKernelVariable[], ConstantsT = {}> {\n  (\n    this: IKernelFunctionThis<ConstantsT>,\n    ...args: ArgT\n  ): KernelOutput;\n}\n\nexport type KernelOutput = void\n  | number\n  | number[]\n  | number[][]\n  | number[][][]\n\n  | Float32Array\n  | Float32Array[]\n  | Float32Array[][]\n\n  | [number, number][]\n  | [number, number, number][]\n  | [number, number, number, number][]\n\n  | [number, number][][]\n  | [number, number, number][][]\n  | [number, number, number, number][][]\n\n  | [number, number][][][]\n  | [number, number, number][][][]\n  | [number, number, number, number][][][]\n\n  | Texture;\n\nexport interface IFunction {\n  source: string;\n  settings: IFunctionSettings;\n}\n\nexport interface IFunctionSettings {\n  name?: string;\n  debug?: boolean;\n  argumentNames?: string[];\n  argumentTypes?: string[] | { [argumentName: string]: string };\n  argumentSizes?: number[];\n\n  constants?: IConstants;\n  constantTypes?: IKernelValueTypes;\n\n  output?: number[];\n  loopMaxIterations?: number;\n  returnType?: string;\n  isRootKernel?: boolean;\n  isSubKernel?: boolean;\n  onNestedFunction?(ast: any, source: string): void;\n  lookupReturnType?(functionName: string, ast: any, node: FunctionNode): void;\n  plugins?: any[];\n\n  useLegacyEncoder?: boolean;\n  ast?: any;\n}\n\nexport interface ISubKernel {\n  name: string;\n  source: string;\n  property: string | number;\n  returnType: string;\n}\n\n\nexport class FunctionBuilder {\n  static fromKernel(kernel: Kernel, FunctionNode: FunctionNode, extraNodeOptions?: any): FunctionBuilder;\n  constructor(settings: IFunctionBuilderSettings);\n  addFunctionNode(functionNode: FunctionNode): void;\n  traceFunctionCalls(functionName: string, retList?: string[]): string[];\n  getStringFromFunctionNames(functionName: string[]): string;\n  getPrototypesFromFunctionNames(functionName: string[]): string[];\n  getString(functionName: string): string;\n  getPrototypeString(functionName: string): string;\n}\n\n\nexport interface IFunctionBuilderSettings {\n  kernel: Kernel;\n  rootNode: FunctionNode;\n  functionNodes?: FunctionNode[];\n  nativeFunctions?: INativeFunctionList;\n  subKernelNodes?: FunctionNode[];\n}\n\n// These are mostly internal\nexport class FunctionNode implements IFunctionSettings {\n  constructor(source: string, settings?: IFunctionNodeSettings);\n}\n\nexport interface IFunctionNodeSettings extends IFunctionSettings {\n  argumentTypes: string[]\n}\n\nexport class WebGLFunctionNode extends FunctionNode {}\nexport class WebGL2FunctionNode extends WebGLFunctionNode {}\nexport class CPUFunctionNode extends FunctionNode {}\n\nexport interface IGPUTextureSettings {\n  texture: WebGLTexture;\n  size: number[];\n  dimensions: number[];\n  output: number[];\n  context: WebGLRenderingContext;\n  kernel: Kernel;\n  gpu?: GPU;\n  type?: GPUTextureType;\n}\n\nexport class Texture {\n  constructor(settings: IGPUTextureSettings)\n  toArray(): TextureArrayOutput;\n  clone(): Texture;\n  delete(): void;\n  clear(): void;\n  kernel: Kernel;\n}\n\nexport type TextureArrayOutput\n  = number[]\n  | number[][]\n  | number[][][]\n\n  | Float32Array\n  | Float32Array[]\n  | Float32Array[][]\n\n  | [number, number][]\n  | [number, number][][]\n  | [number, number][][][]\n\n  | [number, number, number][]\n  | [number, number, number][][]\n  | [number, number, number][][][]\n\n  | [number, number, number, number][]\n  | [number, number, number, number][][]\n  | [number, number, number, number][][][]\n  ;\n\nexport interface IPlugin {\n  source: string;\n  name: string;\n  functionMatch: string;\n  functionReplace: string;\n  functionReturnType: GPUVariableType;\n  onBeforeRun: (kernel: Kernel) => void;\n}\n\nexport type OutputDimensions = [number] | [number, number] | [number, number, number] | Int32Array;\nexport type TextureDimensions = [number, number];\n\nexport class Input {\n  value: number[];\n  size: number[];\n  constructor(value: number[], size: OutputDimensions);\n}\n\nexport type input = (value: number[], size: OutputDimensions) => Input;\n\nexport function alias<T>(name: string, source: T): T;\n\nexport class KernelValue {\n  constructor(value: KernelVariable, settings: IKernelValueSettings);\n  getSource(): string;\n  setup(): void;\n  updateValue(value: KernelVariable): void;\n}\n\nexport class WebGLKernelValue {\n  constructor(value: any, settings: IWebGLKernelValueSettings);\n}\n\nexport interface IFunctionNodeMemberExpressionDetails {\n  xProperty: object;\n  yProperty: object;\n  zProperty: object;\n  property: string;\n  type: string;\n  origin: 'user' | 'constants';\n  signature: string;\n}\n\nexport interface IKernelJSON {\n  settings: IJSONSettings;\n  functionNodes?: object;\n}\n\nexport interface IJSONSettings {\n  output: number[];\n  argumentsTypes: GPUVariableType;\n  returnType: string;\n  argumentNames?: string[];\n  constants?: IConstants;\n  pipeline?: boolean;\n  pluginNames?: string[];\n  tactic?: Tactic;\n  threadDim?: number[];\n}\n\nexport declare const utils: {\n  getMinifySafeName: <T>(arrowReference: () => T) => string\n}\n\nexport interface IReason {\n  type: 'argumentMismatch' | 'outputPrecisionMismatch';\n  needed: any;\n}\n\nexport interface IDeclaration {\n  ast: object;\n  context: object;\n  name: string;\n  origin: 'declaration';\n  inForLoopInit: boolean;\n  inForLoopTest: boolean;\n  assignable: boolean;\n  suggestedType: string;\n  valueType: string;\n  dependencies: any;\n  isSafe: boolean;\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "const { GPU } = require('./gpu');\nconst { alias } = require('./alias');\nconst { utils } = require('./utils');\nconst { Input, input } = require('./input');\nconst { Texture } = require('./texture');\nconst { FunctionBuilder } = require('./backend/function-builder');\nconst { FunctionNode } = require('./backend/function-node');\nconst { CPUFunctionNode } = require('./backend/cpu/function-node');\nconst { CPUKernel } = require('./backend/cpu/kernel');\n\nconst { HeadlessGLKernel } = require('./backend/headless-gl/kernel');\n\nconst { WebGLFunctionNode } = require('./backend/web-gl/function-node');\nconst { WebGLKernel } = require('./backend/web-gl/kernel');\nconst { kernelValueMaps: webGLKernelValueMaps } = require('./backend/web-gl/kernel-value-maps');\n\nconst { WebGL2FunctionNode } = require('./backend/web-gl2/function-node');\nconst { WebGL2Kernel } = require('./backend/web-gl2/kernel');\nconst { kernelValueMaps: webGL2KernelValueMaps } = require('./backend/web-gl2/kernel-value-maps');\n\nconst { GLKernel } = require('./backend/gl/kernel');\n\nconst { Kernel } = require('./backend/kernel');\n\nconst { FunctionTracer } = require('./backend/function-tracer');\n\nconst mathRandom = require('./plugins/math-random-uniformly-distributed');\n\nmodule.exports = {\n  alias,\n  CPUFunctionNode,\n  CPUKernel,\n  GPU,\n  FunctionBuilder,\n  FunctionNode,\n  HeadlessGLKernel,\n  Input,\n  input,\n  Texture,\n  utils,\n\n  WebGL2FunctionNode,\n  WebGL2Kernel,\n  webGL2KernelValueMaps,\n\n  WebGLFunctionNode,\n  WebGLKernel,\n  webGLKernelValueMaps,\n\n  GLKernel,\n  Kernel,\n  FunctionTracer,\n\n  plugins: {\n    mathRandom\n  }\n};"
  },
  {
    "path": "src/input.js",
    "content": "class Input {\n  constructor(value, size) {\n    this.value = value;\n    if (Array.isArray(size)) {\n      this.size = size;\n    } else {\n      this.size = new Int32Array(3);\n      if (size.z) {\n        this.size = new Int32Array([size.x, size.y, size.z]);\n      } else if (size.y) {\n        this.size = new Int32Array([size.x, size.y]);\n      } else {\n        this.size = new Int32Array([size.x]);\n      }\n    }\n\n    const [w, h, d] = this.size;\n    if (d) {\n      if (this.value.length !== (w * h * d)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`);\n      }\n    } else if (h) {\n      if (this.value.length !== (w * h)) {\n        throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`);\n      }\n    } else {\n      if (this.value.length !== w) {\n        throw new Error(`Input size ${this.value.length} does not match ${w}`);\n      }\n    }\n\n  }\n\n  toArray() {\n    const { utils } = require('./utils');\n    const [w, h, d] = this.size;\n    if (d) {\n      return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d);\n    } else if (h) {\n      return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h);\n    } else {\n      return this.value;\n    }\n  }\n}\n\nfunction input(value, size) {\n  return new Input(value, size);\n}\n\nmodule.exports = {\n  Input,\n  input\n};"
  },
  {
    "path": "src/kernel-run-shortcut.js",
    "content": "const { utils } = require('./utils');\n\n/**\n * Makes kernels easier for mortals (including me)\n * @param kernel\n * @returns {function()}\n */\nfunction kernelRunShortcut(kernel) {\n  let run = function() {\n    kernel.build.apply(kernel, arguments);\n    run = function() {\n      let result = kernel.run.apply(kernel, arguments);\n      if (kernel.switchingKernels) {\n        const reasons = kernel.resetSwitchingKernels();\n        const newKernel = kernel.onRequestSwitchKernel(reasons, arguments, kernel);\n        shortcut.kernel = kernel = newKernel;\n        result = newKernel.run.apply(newKernel, arguments);\n      }\n      if (kernel.renderKernels) {\n        return kernel.renderKernels();\n      } else if (kernel.renderOutput) {\n        return kernel.renderOutput();\n      } else {\n        return result;\n      }\n    };\n    return run.apply(kernel, arguments);\n  };\n  const shortcut = function() {\n    return run.apply(kernel, arguments);\n  };\n  /**\n   * Run kernel in async mode\n   * @returns {Promise<KernelOutput>}\n   */\n  shortcut.exec = function() {\n    return new Promise((accept, reject) => {\n      try {\n        accept(run.apply(this, arguments));\n      } catch (e) {\n        reject(e);\n      }\n    });\n  };\n  shortcut.replaceKernel = function(replacementKernel) {\n    kernel = replacementKernel;\n    bindKernelToShortcut(kernel, shortcut);\n  };\n\n  bindKernelToShortcut(kernel, shortcut);\n  return shortcut;\n}\n\nfunction bindKernelToShortcut(kernel, shortcut) {\n  if (shortcut.kernel) {\n    shortcut.kernel = kernel;\n    return;\n  }\n  const properties = utils.allPropertiesOf(kernel);\n  for (let i = 0; i < properties.length; i++) {\n    const property = properties[i];\n    if (property[0] === '_' && property[1] === '_') continue;\n    if (typeof kernel[property] === 'function') {\n      if (property.substring(0, 3) === 'add' || property.substring(0, 3) === 'set') {\n        shortcut[property] = function() {\n          shortcut.kernel[property].apply(shortcut.kernel, arguments);\n          return shortcut;\n        };\n      } else {\n        shortcut[property] = function() {\n          return shortcut.kernel[property].apply(shortcut.kernel, arguments);\n        };\n      }\n    } else {\n      shortcut.__defineGetter__(property, () => shortcut.kernel[property]);\n      shortcut.__defineSetter__(property, (value) => {\n        shortcut.kernel[property] = value;\n      });\n    }\n  }\n  shortcut.kernel = kernel;\n}\nmodule.exports = {\n  kernelRunShortcut\n};"
  },
  {
    "path": "src/plugins/math-random-triangle-noise.js",
    "content": "const source = `\nuniform highp float triangle_noise_seed;\nhighp float triangle_noise_shift = 0.000001;\n\n//https://www.shadertoy.com/view/4t2SDh\n//note: uniformly distributed, normalized rand, [0;1[\nfloat nrand( vec2 n )\n{\n  return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);\n}\n//note: remaps v to [0;1] in interval [a;b]\nfloat remap( float a, float b, float v )\n{\n  return clamp( (v-a) / (b-a), 0.0, 1.0 );\n}\n\nfloat n4rand( vec2 n )\n{\n  float t = fract( triangle_noise_seed + triangle_noise_shift );\n  float nrnd0 = nrand( n + 0.07*t );\n  float nrnd1 = nrand( n + 0.11*t );\n  float nrnd2 = nrand( n + 0.13*t );\n  float nrnd3 = nrand( n + 0.17*t );\n  float result = (nrnd0+nrnd1+nrnd2+nrnd3) / 4.0;\n  triangle_noise_shift = result + 0.000001;\n  return result;\n}`;\n\nconst name = 'math-random-triangle-noise-noise';\n\nconst functionMatch = 'Math.random()';\n\nconst functionReplace = 'nrand(vTexCoord)';\n\nconst functionReturnType = 'Number';\n\nconst onBeforeRun = (kernel) => {\n  kernel.setUniform1f('triangle_noise_seed', Math.random());\n};\n\n/**\n *\n * @type IPlugin\n */\nmodule.exports = {\n  name,\n  onBeforeRun,\n  functionMatch,\n  functionReplace,\n  functionReturnType,\n  source\n};"
  },
  {
    "path": "src/plugins/math-random-uniformly-distributed.js",
    "content": "// language=GLSL\nconst source = `// https://www.shadertoy.com/view/4t2SDh\n//note: uniformly distributed, normalized rand, [0,1]\nhighp float randomSeedShift = 1.0;\nhighp float slide = 1.0;\nuniform highp float randomSeed1;\nuniform highp float randomSeed2;\n\nhighp float nrand(highp vec2 n) {\n  highp float result = fract(sin(dot((n.xy + 1.0) * vec2(randomSeed1 * slide, randomSeed2 * randomSeedShift), vec2(12.9898, 78.233))) * 43758.5453);\n  randomSeedShift = result;\n  if (randomSeedShift > 0.5) {\n    slide += 0.00009; \n  } else {\n    slide += 0.0009;\n  }\n  return result;\n}`;\n\nconst name = 'math-random-uniformly-distributed';\n\n// language=JavaScript\nconst functionMatch = `Math.random()`;\n\nconst functionReplace = `nrand(vTexCoord)`;\n\nconst functionReturnType = 'Number';\n/**\n *\n * @param {Kernel} kernel\n */\nconst onBeforeRun = (kernel) => {\n  kernel.setUniform1f('randomSeed1', Math.random());\n  kernel.setUniform1f('randomSeed2', Math.random());\n};\n\n/**\n *\n * @type IPlugin\n */\nconst plugin = {\n  name,\n  onBeforeRun,\n  functionMatch,\n  functionReplace,\n  functionReturnType,\n  source\n};\n\nmodule.exports = plugin;"
  },
  {
    "path": "src/texture.js",
    "content": "/**\n * @desc WebGl Texture implementation in JS\n * @param {IGPUTextureSettings} settings\n */\nclass Texture {\n  constructor(settings) {\n    const {\n      texture,\n      size,\n      dimensions,\n      output,\n      context,\n      type = 'NumberTexture',\n      kernel,\n      internalFormat,\n      textureFormat\n    } = settings;\n    if (!output) throw new Error('settings property \"output\" required.');\n    if (!context) throw new Error('settings property \"context\" required.');\n    if (!texture) throw new Error('settings property \"texture\" required.');\n    if (!kernel) throw new Error('settings property \"kernel\" required.');\n    this.texture = texture;\n    if (texture._refs) {\n      texture._refs++;\n    } else {\n      texture._refs = 1;\n    }\n    this.size = size;\n    this.dimensions = dimensions;\n    this.output = output;\n    this.context = context;\n    /**\n     * @type {Kernel}\n     */\n    this.kernel = kernel;\n    this.type = type;\n    this._deleted = false;\n    this.internalFormat = internalFormat;\n    this.textureFormat = textureFormat;\n  }\n\n  /**\n   * @desc Converts the Texture into a JavaScript Array\n   * @returns {TextureArrayOutput}\n   */\n  toArray() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  /**\n   * @desc Clones the Texture\n   * @returns {Texture}\n   */\n  clone() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  /**\n   * @desc Deletes the Texture\n   */\n  delete() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n\n  clear() {\n    throw new Error(`Not implemented on ${this.constructor.name}`);\n  }\n}\n\nmodule.exports = {\n  Texture\n};"
  },
  {
    "path": "src/utils.js",
    "content": "const acorn = require('acorn');\nconst { Input } = require('./input');\nconst { Texture } = require('./texture');\n\nconst FUNCTION_NAME = /function ([^(]*)/;\nconst STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/mg;\nconst ARGUMENT_NAMES = /([^\\s,]+)/g;\n\n/**\n *\n * @desc Various utility functions / snippets of code that GPU.JS uses internally.\n * @type {utils}\n * This covers various snippets of code that is not entirely gpu.js specific (ie. may find uses elsewhere)\n */\nconst utils = {\n  /**\n   *\n   * @desc Gets the system endianness, and cache it\n   * @returns {String} 'LE' or 'BE' depending on system architecture\n   * Credit: https://gist.github.com/TooTallNate/4750953\n   */\n  systemEndianness() {\n    return _systemEndianness;\n  },\n  getSystemEndianness() {\n    const b = new ArrayBuffer(4);\n    const a = new Uint32Array(b);\n    const c = new Uint8Array(b);\n    a[0] = 0xdeadbeef;\n    if (c[0] === 0xef) return 'LE';\n    if (c[0] === 0xde) return 'BE';\n    throw new Error('unknown endianness');\n  },\n\n  /**\n   * @descReturn TRUE, on a JS function\n   * @param {Function} funcObj - Object to validate if its a function\n   * @returns  {Boolean} TRUE if the object is a JS function\n   */\n  isFunction(funcObj) {\n    return typeof(funcObj) === 'function';\n  },\n\n  /**\n   * @desc Return TRUE, on a valid JS function string\n   * Note: This does just a VERY simply sanity check. And may give false positives.\n   *\n   * @param {String} fn - String of JS function to validate\n   * @returns {Boolean} TRUE if the string passes basic validation\n   */\n  isFunctionString(fn) {\n    if (typeof fn === 'string') {\n      return (fn\n        .slice(0, 'function'.length)\n        .toLowerCase() === 'function');\n    }\n    return false;\n  },\n\n  /**\n   * @desc Return the function name from a JS function string\n   * @param {String} funcStr - String of JS function to validate\n   * @returns {String} Function name string (if found)\n   */\n  getFunctionNameFromString(funcStr) {\n    const result = FUNCTION_NAME.exec(funcStr);\n    if (!result || result.length === 0) return null;\n    return result[1].trim();\n  },\n\n  getFunctionBodyFromString(funcStr) {\n    return funcStr.substring(funcStr.indexOf('{') + 1, funcStr.lastIndexOf('}'));\n  },\n\n  /**\n   * @desc Return list of argument names extracted from a javascript function\n   * @param {String} fn - String of JS function to validate\n   * @returns {String[]}  Array representing all the parameter names\n   */\n  getArgumentNamesFromString(fn) {\n    const fnStr = fn.replace(STRIP_COMMENTS, '');\n    let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);\n    if (result === null) {\n      result = [];\n    }\n    return result;\n  },\n\n  /**\n   * @desc Returns a clone\n   * @param {Object} obj - Object to clone\n   * @returns {Object|Array} Cloned object\n   */\n  clone(obj) {\n    if (obj === null || typeof obj !== 'object' || obj.hasOwnProperty('isActiveClone')) return obj;\n\n    const temp = obj.constructor(); // changed\n\n    for (let key in obj) {\n      if (Object.prototype.hasOwnProperty.call(obj, key)) {\n        obj.isActiveClone = null;\n        temp[key] = utils.clone(obj[key]);\n        delete obj.isActiveClone;\n      }\n    }\n\n    return temp;\n  },\n\n  /**\n   * @desc Checks if is an array or Array-like object\n   * @param {Object} array - The argument object to check if is array\n   * @returns {Boolean}  true if is array or Array-like object\n   */\n  isArray(array) {\n    return !isNaN(array.length);\n  },\n\n  /**\n   * @desc Evaluate the argument type, to apply respective logic for it\n   * @param {*} value - The argument object to evaluate type\n   * @param {boolean} [strictIntegers]\n   * @returns {String}  Argument type Array/Number/Float/Texture/Unknown\n   */\n  getVariableType(value, strictIntegers) {\n    if (utils.isArray(value)) {\n      if (value.length > 0 && value[0].nodeName === 'IMG') {\n        return 'HTMLImageArray';\n      }\n      return 'Array';\n    }\n\n    switch (value.constructor) {\n      case Boolean:\n        return 'Boolean';\n      case Number:\n        if (strictIntegers && Number.isInteger(value)) {\n          return 'Integer';\n        }\n        return 'Float';\n      case Texture:\n        return value.type;\n      case Input:\n        return 'Input';\n    }\n    if ('nodeName' in value) {\n      switch (value.nodeName) {\n        case 'IMG':\n          return 'HTMLImage';\n        case 'CANVAS':\n          return 'HTMLImage';\n        case 'VIDEO':\n          return 'HTMLVideo';\n      }\n    } else if (value.hasOwnProperty('type')) {\n      return value.type;\n    } else if (typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas) {\n      return 'OffscreenCanvas';\n    } else if (typeof ImageBitmap !== 'undefined' && value instanceof ImageBitmap) {\n      return 'ImageBitmap';\n    } else if (typeof ImageData !== 'undefined' && value instanceof ImageData) {\n      return 'ImageData';\n    }\n    return 'Unknown';\n  },\n\n  getKernelTextureSize(settings, dimensions) {\n    let [w, h, d] = dimensions;\n    let texelCount = (w || 1) * (h || 1) * (d || 1);\n\n    if (settings.optimizeFloatMemory && settings.precision === 'single') {\n      w = texelCount = Math.ceil(texelCount / 4);\n    }\n    // if given dimensions == a 2d image\n    if (h > 1 && w * h === texelCount) {\n      return new Int32Array([w, h]);\n    }\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  /**\n   *\n   * @param {Number} length\n   * @returns {TextureDimensions}\n   */\n  closestSquareDimensions(length) {\n    const sqrt = Math.sqrt(length);\n    let high = Math.ceil(sqrt);\n    let low = Math.floor(sqrt);\n    while (high * low < length) {\n      high--;\n      low = Math.ceil(length / high);\n    }\n    return new Int32Array([low, Math.ceil(length / low)]);\n  },\n\n  /**\n   * A texture takes up four\n   * @param {OutputDimensions} dimensions\n   * @param {Number} bitRatio\n   * @returns {TextureDimensions}\n   */\n  getMemoryOptimizedFloatTextureSize(dimensions, bitRatio) {\n    const totalArea = utils.roundTo((dimensions[0] || 1) * (dimensions[1] || 1) * (dimensions[2] || 1) * (dimensions[3] || 1), 4);\n    const texelCount = totalArea / bitRatio;\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  /**\n   *\n   * @param dimensions\n   * @param bitRatio\n   * @returns {*|TextureDimensions}\n   */\n  getMemoryOptimizedPackedTextureSize(dimensions, bitRatio) {\n    const [w, h, d] = dimensions;\n    const totalArea = utils.roundTo((w || 1) * (h || 1) * (d || 1), 4);\n    const texelCount = totalArea / (4 / bitRatio);\n    return utils.closestSquareDimensions(texelCount);\n  },\n\n  roundTo(n, d) {\n    return Math.floor((n + d - 1) / d) * d;\n  },\n  /**\n   * @desc Return the dimension of an array.\n   * @param {Array|String|Texture|Input} x - The array\n   * @param {Boolean} [pad] - To include padding in the dimension calculation\n   * @returns {OutputDimensions}\n   */\n  getDimensions(x, pad) {\n    let ret;\n    if (utils.isArray(x)) {\n      const dim = [];\n      let temp = x;\n      while (utils.isArray(temp)) {\n        dim.push(temp.length);\n        temp = temp[0];\n      }\n      ret = dim.reverse();\n    } else if (x instanceof Texture) {\n      ret = x.output;\n    } else if (x instanceof Input) {\n      ret = x.size;\n    } else {\n      throw new Error(`Unknown dimensions of ${x}`);\n    }\n\n    if (pad) {\n      ret = Array.from(ret);\n      while (ret.length < 3) {\n        ret.push(1);\n      }\n    }\n\n    return new Int32Array(ret);\n  },\n\n  /**\n   * Puts a nested 2d array into a one-dimensional target array\n   * @param {Array|*} array\n   * @param {Float32Array|Float64Array} target\n   */\n  flatten2dArrayTo(array, target) {\n    let offset = 0;\n    for (let y = 0; y < array.length; y++) {\n      target.set(array[y], offset);\n      offset += array[y].length;\n    }\n  },\n\n  /**\n   * Puts a nested 3d array into a one-dimensional target array\n   * @param {Array|*} array\n   * @param {Float32Array|Float64Array} target\n   */\n  flatten3dArrayTo(array, target) {\n    let offset = 0;\n    for (let z = 0; z < array.length; z++) {\n      for (let y = 0; y < array[z].length; y++) {\n        target.set(array[z][y], offset);\n        offset += array[z][y].length;\n      }\n    }\n  },\n\n  /**\n   * Puts a nested 4d array into a one-dimensional target array\n   * @param {Array|*} array\n   * @param {Float32Array|Float64Array} target\n   */\n  flatten4dArrayTo(array, target) {\n    let offset = 0;\n    for (let l = 0; l < array.length; l++) {\n      for (let z = 0; z < array[l].length; z++) {\n        for (let y = 0; y < array[l][z].length; y++) {\n          target.set(array[l][z][y], offset);\n          offset += array[l][z][y].length;\n        }\n      }\n    }\n  },\n\n  /**\n   * Puts a nested 1d, 2d, or 3d array into a one-dimensional target array\n   * @param {Float32Array|Uint16Array|Uint8Array} array\n   * @param {Float32Array} target\n   */\n  flattenTo(array, target) {\n    if (utils.isArray(array[0])) {\n      if (utils.isArray(array[0][0])) {\n        if (utils.isArray(array[0][0][0])) {\n          utils.flatten4dArrayTo(array, target);\n        } else {\n          utils.flatten3dArrayTo(array, target);\n        }\n      } else {\n        utils.flatten2dArrayTo(array, target);\n      }\n    } else {\n      target.set(array);\n    }\n  },\n\n  /**\n   *\n   * @desc Splits an array into smaller arrays.\n   * Number of elements in one small chunk is given by `part`\n   *\n   * @param {Number[]} array - The array to split into chunks\n   * @param {Number} part - elements in one chunk\n   *\n   * @returns {Number[]} An array of smaller chunks\n   */\n  splitArray(array, part) {\n    const result = [];\n    for (let i = 0; i < array.length; i += part) {\n      result.push(new array.constructor(array.buffer, i * 4 + array.byteOffset, part));\n    }\n    return result;\n  },\n\n  getAstString(source, ast) {\n    const lines = Array.isArray(source) ? source : source.split(/\\r?\\n/g);\n    const start = ast.loc.start;\n    const end = ast.loc.end;\n    const result = [];\n    if (start.line === end.line) {\n      result.push(lines[start.line - 1].substring(start.column, end.column));\n    } else {\n      result.push(lines[start.line - 1].slice(start.column));\n      for (let i = start.line; i < end.line; i++) {\n        result.push(lines[i]);\n      }\n      result.push(lines[end.line - 1].slice(0, end.column));\n    }\n    return result.join('\\n');\n  },\n\n  allPropertiesOf(obj) {\n    const props = [];\n\n    do {\n      props.push.apply(props, Object.getOwnPropertyNames(obj));\n    } while (obj = Object.getPrototypeOf(obj));\n\n    return props;\n  },\n\n  /**\n   * @param {Array} lines - An Array of strings\n   * @returns {String} Single combined String, separated by *\\n*\n   */\n  linesToString(lines) {\n    if (lines.length > 0) {\n      return lines.join(';\\n') + ';\\n';\n    } else {\n      return '\\n';\n    }\n  },\n  warnDeprecated(type, oldName, newName) {\n    if (newName) {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been replaced with \"${ newName }\". Fixing, but please upgrade as it will soon be removed.`);\n    } else {\n      console.warn(`You are using a deprecated ${ type } \"${ oldName }\". It has been removed. Fixing, but please upgrade as it will soon be removed.`);\n    }\n  },\n  flipPixels: (pixels, width, height) => {\n    // https://stackoverflow.com/a/41973289/1324039\n    const halfHeight = height / 2 | 0; // the | 0 keeps the result an int\n    const bytesPerRow = width * 4;\n    // make a temp buffer to hold one row\n    const temp = new Uint8ClampedArray(width * 4);\n    const result = pixels.slice(0);\n    for (let y = 0; y < halfHeight; ++y) {\n      const topOffset = y * bytesPerRow;\n      const bottomOffset = (height - y - 1) * bytesPerRow;\n\n      // make copy of a row on the top half\n      temp.set(result.subarray(topOffset, topOffset + bytesPerRow));\n\n      // copy a row from the bottom half to the top\n      result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);\n\n      // copy the copy of the top half row to the bottom half\n      result.set(temp, bottomOffset);\n    }\n    return result;\n  },\n  erectPackedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erect2DPackedFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xStart = y * width;\n      const xEnd = xStart + width;\n      yResults[y] = array.subarray(xStart, xEnd);\n    }\n    return yResults;\n  },\n  erect3DPackedFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xStart = (z * height * width) + y * width;\n        const xEnd = xStart + width;\n        yResults[y] = array.subarray(xStart, xEnd);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectMemoryOptimizedFloat: (array, width) => {\n    return array.subarray(0, width);\n  },\n  erectMemoryOptimized2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const offset = y * width;\n      yResults[y] = array.subarray(offset, offset + width);\n    }\n    return yResults;\n  },\n  erectMemoryOptimized3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const offset = (z * height * width) + (y * width);\n        yResults[y] = array.subarray(offset, offset + width);\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectFloat: (array, width) => {\n    const xResults = new Float32Array(width);\n    let i = 0;\n    for (let x = 0; x < width; x++) {\n      xResults[x] = array[i];\n      i += 4;\n    }\n    return xResults;\n  },\n  erect2DFloat: (array, width, height) => {\n    const yResults = new Array(height);\n    let i = 0;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Float32Array(width);\n      for (let x = 0; x < width; x++) {\n        xResults[x] = array[i];\n        i += 4;\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DFloat: (array, width, height, depth) => {\n    const zResults = new Array(depth);\n    let i = 0;\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Float32Array(width);\n        for (let x = 0; x < width; x++) {\n          xResults[x] = array[i];\n          i += 4;\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray2: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 2);\n    }\n    return xResults;\n  },\n  erect2DArray2: (array, width, height) => {\n    const yResults = new Array(height);\n    const XResultsMax = width * 4;\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * XResultsMax;\n      let i = 0;\n      for (let x = 0; x < XResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 2);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray2: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 2);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray3: (array, width) => {\n    const xResults = new Array(width);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 3);\n    }\n    return xResults;\n  },\n  erect2DArray3: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 3);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray3: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 3);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n  erectArray4: (array, width) => {\n    const xResults = new Array(array);\n    const xResultsMax = width * 4;\n    let i = 0;\n    for (let x = 0; x < xResultsMax; x += 4) {\n      xResults[i++] = array.subarray(x, x + 4);\n    }\n    return xResults;\n  },\n  erect2DArray4: (array, width, height) => {\n    const xResultsMax = width * 4;\n    const yResults = new Array(height);\n    for (let y = 0; y < height; y++) {\n      const xResults = new Array(width);\n      const offset = y * xResultsMax;\n      let i = 0;\n      for (let x = 0; x < xResultsMax; x += 4) {\n        xResults[i++] = array.subarray(x + offset, x + offset + 4);\n      }\n      yResults[y] = xResults;\n    }\n    return yResults;\n  },\n  erect3DArray4: (array, width, height, depth) => {\n    const xResultsMax = width * 4;\n    const zResults = new Array(depth);\n    for (let z = 0; z < depth; z++) {\n      const yResults = new Array(height);\n      for (let y = 0; y < height; y++) {\n        const xResults = new Array(width);\n        const offset = (z * xResultsMax * height) + (y * xResultsMax);\n        let i = 0;\n        for (let x = 0; x < xResultsMax; x += 4) {\n          xResults[i++] = array.subarray(x + offset, x + offset + 4);\n        }\n        yResults[y] = xResults;\n      }\n      zResults[z] = yResults;\n    }\n    return zResults;\n  },\n\n  /**\n   *\n   * @param {String} source\n   * @param {Object} settings\n   * @return {String}\n   */\n  flattenFunctionToString: (source, settings) => {\n    const { findDependency, thisLookup, doNotDefine } = settings;\n    let flattened = settings.flattened;\n    if (!flattened) {\n      flattened = settings.flattened = {};\n    }\n    const ast = acorn.parse(source);\n    const functionDependencies = [];\n    let indent = 0;\n\n    function flatten(ast) {\n      if (Array.isArray(ast)) {\n        const results = [];\n        for (let i = 0; i < ast.length; i++) {\n          results.push(flatten(ast[i]));\n        }\n        return results.join('');\n      }\n      switch (ast.type) {\n        case 'Program':\n          return flatten(ast.body) + (ast.body[0].type === 'VariableDeclaration' ? ';' : '');\n        case 'FunctionDeclaration':\n          return `function ${ast.id.name}(${ast.params.map(flatten).join(', ')}) ${ flatten(ast.body) }`;\n        case 'BlockStatement': {\n          const result = [];\n          indent += 2;\n          for (let i = 0; i < ast.body.length; i++) {\n            const flat = flatten(ast.body[i]);\n            if (flat) {\n              result.push(' '.repeat(indent) + flat, ';\\n');\n            }\n          }\n          indent -= 2;\n          return `{\\n${result.join('')}}`;\n        }\n        case 'VariableDeclaration':\n          const declarations = utils.normalizeDeclarations(ast)\n            .map(flatten)\n            .filter(r => r !== null);\n          if (declarations.length < 1) {\n            return '';\n          } else {\n            return `${ast.kind} ${declarations.join(',')}`;\n          }\n        case 'VariableDeclarator':\n          if (ast.init.object && ast.init.object.type === 'ThisExpression') {\n            const lookup = thisLookup(ast.init.property.name, true);\n            if (lookup) {\n              return `${ast.id.name} = ${flatten(ast.init)}`;\n            } else {\n              return null;\n            }\n          } else {\n            return `${ast.id.name} = ${flatten(ast.init)}`;\n          }\n        case 'CallExpression': {\n          if (ast.callee.property.name === 'subarray') {\n            return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n          }\n          if (ast.callee.object.name === 'gl' || ast.callee.object.name === 'context') {\n            return `${flatten(ast.callee.object)}.${flatten(ast.callee.property)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n          }\n          if (ast.callee.object.type === 'ThisExpression') {\n            functionDependencies.push(findDependency('this', ast.callee.property.name));\n            return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n          } else if (ast.callee.object.name) {\n            const foundSource = findDependency(ast.callee.object.name, ast.callee.property.name);\n            if (foundSource === null) {\n              // we're not flattening it\n              return `${ast.callee.object.name}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n            } else {\n              functionDependencies.push(foundSource);\n              // we're flattening it\n              return `${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n            }\n          } else if (ast.callee.object.type === 'MemberExpression') {\n            return `${flatten(ast.callee.object)}.${ast.callee.property.name}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n          } else {\n            throw new Error('unknown ast.callee');\n          }\n        }\n        case 'ReturnStatement':\n          return `return ${flatten(ast.argument)}`;\n        case 'BinaryExpression':\n          return `(${flatten(ast.left)}${ast.operator}${flatten(ast.right)})`;\n        case 'UnaryExpression':\n          if (ast.prefix) {\n            return `${ast.operator} ${flatten(ast.argument)}`;\n          } else {\n            return `${flatten(ast.argument)} ${ast.operator}`;\n          }\n        case 'ExpressionStatement':\n          return `${flatten(ast.expression)}`;\n        case 'SequenceExpression':\n          return `(${flatten(ast.expressions)})`;\n        case 'ArrowFunctionExpression':\n          return `(${ast.params.map(flatten).join(', ')}) => ${flatten(ast.body)}`;\n        case 'Literal':\n          return ast.raw;\n        case 'Identifier':\n          return ast.name;\n        case 'MemberExpression':\n          if (ast.object.type === 'ThisExpression') {\n            return thisLookup(ast.property.name);\n          }\n          if (ast.computed) {\n            return `${flatten(ast.object)}[${flatten(ast.property)}]`;\n          }\n          return flatten(ast.object) + '.' + flatten(ast.property);\n        case 'ThisExpression':\n          return 'this';\n        case 'NewExpression':\n          return `new ${flatten(ast.callee)}(${ast.arguments.map(value => flatten(value)).join(', ')})`;\n        case 'ForStatement':\n          return `for (${flatten(ast.init)};${flatten(ast.test)};${flatten(ast.update)}) ${flatten(ast.body)}`;\n        case 'AssignmentExpression':\n          return `${flatten(ast.left)}${ast.operator}${flatten(ast.right)}`;\n        case 'UpdateExpression':\n          return `${flatten(ast.argument)}${ast.operator}`;\n        case 'IfStatement':\n          return `if (${flatten(ast.test)}) ${flatten(ast.consequent)}`;\n        case 'ThrowStatement':\n          return `throw ${flatten(ast.argument)}`;\n        case 'ObjectPattern':\n          return ast.properties.map(flatten).join(', ');\n        case 'ArrayPattern':\n          return ast.elements.map(flatten).join(', ');\n        case 'DebuggerStatement':\n          return 'debugger;';\n        case 'ConditionalExpression':\n          return `${flatten(ast.test)}?${flatten(ast.consequent)}:${flatten(ast.alternate)}`;\n        case 'Property':\n          if (ast.kind === 'init') {\n            return flatten(ast.key);\n          }\n      }\n      throw new Error(`unhandled ast.type of ${ ast.type }`);\n    }\n    const result = flatten(ast);\n    if (functionDependencies.length > 0) {\n      const flattenedFunctionDependencies = [];\n      for (let i = 0; i < functionDependencies.length; i++) {\n        const functionDependency = functionDependencies[i];\n        if (!flattened[functionDependency]) {\n          flattened[functionDependency] = true;\n        }\n        functionDependency ? flattenedFunctionDependencies.push(utils.flattenFunctionToString(functionDependency, settings) + '\\n') : '';\n      }\n      return flattenedFunctionDependencies.join('') + result;\n    }\n    return result;\n  },\n\n  normalizeDeclarations: (ast) => {\n    if (ast.type !== 'VariableDeclaration') throw new Error('Ast is not of type \"VariableDeclaration\"');\n    const normalizedDeclarations = [];\n    for (let declarationIndex = 0; declarationIndex < ast.declarations.length; declarationIndex++) {\n      const declaration = ast.declarations[declarationIndex];\n      if (declaration.id && declaration.id.type === 'ObjectPattern' && declaration.id.properties) {\n        const { properties } = declaration.id;\n        for (let propertyIndex = 0; propertyIndex < properties.length; propertyIndex++) {\n          const property = properties[propertyIndex];\n          if (property.value.type === 'ObjectPattern' && property.value.properties) {\n            for (let subPropertyIndex = 0; subPropertyIndex < property.value.properties.length; subPropertyIndex++) {\n              const subProperty = property.value.properties[subPropertyIndex];\n              if (subProperty.type === 'Property') {\n                normalizedDeclarations.push({\n                  type: 'VariableDeclarator',\n                  id: {\n                    type: 'Identifier',\n                    name: subProperty.key.name\n                  },\n                  init: {\n                    type: 'MemberExpression',\n                    object: {\n                      type: 'MemberExpression',\n                      object: declaration.init,\n                      property: {\n                        type: 'Identifier',\n                        name: property.key.name\n                      },\n                      computed: false\n                    },\n                    property: {\n                      type: 'Identifier',\n                      name: subProperty.key.name\n                    },\n                    computed: false\n                  }\n                });\n              } else {\n                throw new Error('unexpected state');\n              }\n            }\n          } else if (property.value.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: property.value && property.value.name ? property.value.name : property.key.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Identifier',\n                  name: property.key.name\n                },\n                computed: false\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else if (declaration.id && declaration.id.type === 'ArrayPattern' && declaration.id.elements) {\n        const { elements } = declaration.id;\n        for (let elementIndex = 0; elementIndex < elements.length; elementIndex++) {\n          const element = elements[elementIndex];\n          if (element.type === 'Identifier') {\n            normalizedDeclarations.push({\n              type: 'VariableDeclarator',\n              id: {\n                type: 'Identifier',\n                name: element.name\n              },\n              init: {\n                type: 'MemberExpression',\n                object: declaration.init,\n                property: {\n                  type: 'Literal',\n                  value: elementIndex,\n                  raw: elementIndex.toString(),\n                  start: element.start,\n                  end: element.end\n                },\n                computed: true\n              }\n            });\n          } else {\n            throw new Error('unexpected state');\n          }\n        }\n      } else {\n        normalizedDeclarations.push(declaration);\n      }\n    }\n    return normalizedDeclarations;\n  },\n\n  /**\n   *\n   * @param {GPU} gpu\n   * @param image\n   * @return {Array}\n   */\n  splitHTMLImageToRGB: (gpu, image) => {\n    const rKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.r * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const gKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.g * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const bKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.b * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const aKernel = gpu.createKernel(function(a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.a * 255;\n    }, {\n      output: [image.width, image.height],\n      precision: 'unsigned',\n      argumentTypes: { a: 'HTMLImage' },\n    });\n    const result = [\n      rKernel(image),\n      gKernel(image),\n      bKernel(image),\n      aKernel(image),\n    ];\n    result.rKernel = rKernel;\n    result.gKernel = gKernel;\n    result.bKernel = bKernel;\n    result.aKernel = aKernel;\n    result.gpu = gpu;\n    return result;\n  },\n\n  /**\n   * A visual debug utility\n   * @param {GPU} gpu\n   * @param rgba\n   * @param width\n   * @param height\n   * @return {Object[]}\n   */\n  splitRGBAToCanvases: (gpu, rgba, width, height) => {\n    const visualKernelR = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(pixel.r / 255, 0, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelR(rgba);\n\n    const visualKernelG = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, pixel.g / 255, 0, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelG(rgba);\n\n    const visualKernelB = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, 0, pixel.b / 255, 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelB(rgba);\n\n    const visualKernelA = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(255, 255, 255, pixel.a / 255);\n    }, {\n      output: [width, height],\n      graphical: true,\n      argumentTypes: { v: 'Array2D(4)' }\n    });\n    visualKernelA(rgba);\n    return [\n      visualKernelR.canvas,\n      visualKernelG.canvas,\n      visualKernelB.canvas,\n      visualKernelA.canvas,\n    ];\n  },\n\n  getMinifySafeName: (fn) => {\n    try {\n      const ast = acorn.parse(`const value = ${fn.toString()}`);\n      const { init } = ast.body[0].declarations[0];\n      return init.body.name || init.body.body[0].argument.name;\n    } catch (e) {\n      throw new Error('Unrecognized function type.  Please use `() => yourFunctionVariableHere` or function() { return yourFunctionVariableHere; }');\n    }\n  },\n  sanitizeName: function(name) {\n    if (dollarSign.test(name)) {\n      name = name.replace(dollarSign, 'S_S');\n    }\n    if (doubleUnderscore.test(name)) {\n      name = name.replace(doubleUnderscore, 'U_U');\n    } else if (singleUnderscore.test(name)) {\n      name = name.replace(singleUnderscore, 'u_u');\n    }\n    return name;\n  }\n};\n\nconst dollarSign = /\\$/;\nconst doubleUnderscore = /__/;\nconst singleUnderscore = /_/;\n\nconst _systemEndianness = utils.getSystemEndianness();\n\nmodule.exports = {\n  utils\n};"
  },
  {
    "path": "test/all-template.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>GPU.JS : Test All</title>\n  <link rel=\"stylesheet\" href=\"../node_modules/qunit/qunit/qunit.css\">\n\n  <script src=\"browser-test-utils.js\"></script>\n  <!-- gpu.js scripts -->\n  <script src=\"../dist/gpu-browser.js\"></script>\n</head>\n<body>\n<div id=\"qunit\"></div>\n<div id=\"qunit-fixture\"></div>\n<script>\n  const isBrowser = true;\n  const browserGPU = { GPU };\n  for (const p in GPU) {\n    if (p === 'GPU') continue;\n    browserGPU[p] = GPU[p];\n  }\n  window.process = {\n    cwd: () => {\n      return '../..';\n    }\n  };\n  window.require = (module) => {\n    if (module.match(/[.][.]\\/src$/)) return browserGPU;\n    if (module === 'sinon') return sinon;\n    if (module === 'qunit') return QUnit;\n    if (module === 'acorn') return acorn;\n    if (module.match(/[.][.]\\/browser-test-utils$/)) return browserTestUtils;\n    throw new Error('cannot find ' + module);\n  };\n</script>\n<script src=\"../node_modules/sinon/pkg/sinon.js\"></script>\n<script src=\"../node_modules/qunit/qunit/qunit.js\"></script>\n<script src=\"../node_modules/acorn/dist/acorn.js\"></script>\n{{test-files}}\n</body>\n</html>\n"
  },
  {
    "path": "test/all.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>GPU.JS : Test All</title>\n  <link rel=\"stylesheet\" href=\"../node_modules/qunit/qunit/qunit.css\">\n\n  <script src=\"browser-test-utils.js\"></script>\n  <!-- gpu.js scripts -->\n  <script src=\"../dist/gpu-browser.js\"></script>\n</head>\n<body>\n<div id=\"qunit\"></div>\n<div id=\"qunit-fixture\"></div>\n<script>\n  const isBrowser = true;\n  const browserGPU = { GPU };\n  for (const p in GPU) {\n    if (p === 'GPU') continue;\n    browserGPU[p] = GPU[p];\n  }\n  window.process = {\n    cwd: () => {\n      return '../..';\n    }\n  };\n  window.require = (module) => {\n    if (module.match(/[.][.]\\/src$/)) return browserGPU;\n    if (module === 'sinon') return sinon;\n    if (module === 'qunit') return QUnit;\n    if (module === 'acorn') return acorn;\n    if (module.match(/[.][.]\\/browser-test-utils$/)) return browserTestUtils;\n    throw new Error('cannot find ' + module);\n  };\n</script>\n<script src=\"../node_modules/sinon/pkg/sinon.js\"></script>\n<script src=\"../node_modules/qunit/qunit/qunit.js\"></script>\n<script src=\"../node_modules/acorn/dist/acorn.js\"></script>\n<!-- the following list of javascript files is built automatically -->\n<script type=\"module\" src=\"features/add-custom-function.js\"></script>\n<script type=\"module\" src=\"features/add-custom-native-function.js\"></script>\n<script type=\"module\" src=\"features/add-typed-functions.js\"></script>\n<script type=\"module\" src=\"features/argument-array-types.js\"></script>\n<script type=\"module\" src=\"features/argument-array1d-types.js\"></script>\n<script type=\"module\" src=\"features/argument-array2d-types.js\"></script>\n<script type=\"module\" src=\"features/argument-array3d-types.js\"></script>\n<script type=\"module\" src=\"features/arithmetic-operators.js\"></script>\n<script type=\"module\" src=\"features/assignment-operators.js\"></script>\n<script type=\"module\" src=\"features/basic-math.js\"></script>\n<script type=\"module\" src=\"features/bitwise-operators.js\"></script>\n<script type=\"module\" src=\"features/boolean-from-expression.js\"></script>\n<script type=\"module\" src=\"features/canvas.js\"></script>\n<script type=\"module\" src=\"features/clear-textures.js\"></script>\n<script type=\"module\" src=\"features/clone-textures.js\"></script>\n<script type=\"module\" src=\"features/combine-kernels.js\"></script>\n<script type=\"module\" src=\"features/constants-array.js\"></script>\n<script type=\"module\" src=\"features/constants-bool.js\"></script>\n<script type=\"module\" src=\"features/constants-canvas.js\"></script>\n<script type=\"module\" src=\"features/constants-float.js\"></script>\n<script type=\"module\" src=\"features/constants-image-array.js\"></script>\n<script type=\"module\" src=\"features/constants-image.js\"></script>\n<script type=\"module\" src=\"features/constants-integer.js\"></script>\n<script type=\"module\" src=\"features/constants-texture.js\"></script>\n<script type=\"module\" src=\"features/cpu-with-textures.js\"></script>\n<script type=\"module\" src=\"features/create-kernel-map.js\"></script>\n<script type=\"module\" src=\"features/demo.js\"></script>\n<script type=\"module\" src=\"features/destroy.js\"></script>\n<script type=\"module\" src=\"features/destructured-assignment.js\"></script>\n<script type=\"module\" src=\"features/dev-mode.js\"></script>\n<script type=\"module\" src=\"features/dynamic-arguments.js\"></script>\n<script type=\"module\" src=\"features/dynamic-output.js\"></script>\n<script type=\"module\" src=\"features/function-return.js\"></script>\n<script type=\"module\" src=\"features/get-canvas.js\"></script>\n<script type=\"module\" src=\"features/get-pixels.js\"></script>\n<script type=\"module\" src=\"features/if-else.js\"></script>\n<script type=\"module\" src=\"features/image-array.js\"></script>\n<script type=\"module\" src=\"features/image.js\"></script>\n<script type=\"module\" src=\"features/infinity.js\"></script>\n<script type=\"module\" src=\"features/inject-native.js\"></script>\n<script type=\"module\" src=\"features/input.js\"></script>\n<script type=\"module\" src=\"features/internally-defined-matrices.js\"></script>\n<script type=\"module\" src=\"features/json.js\"></script>\n<script type=\"module\" src=\"features/legacy-encoder.js\"></script>\n<script type=\"module\" src=\"features/loops.js\"></script>\n<script type=\"module\" src=\"features/math-object.js\"></script>\n<script type=\"module\" src=\"features/nested-function.js\"></script>\n<script type=\"module\" src=\"features/offscreen-canvas.js\"></script>\n<script type=\"module\" src=\"features/optimize-float-memory.js\"></script>\n<script type=\"module\" src=\"features/output.js\"></script>\n<script type=\"module\" src=\"features/promise-api.js\"></script>\n<script type=\"module\" src=\"features/raw-output.js\"></script>\n<script type=\"module\" src=\"features/read-color-texture.js\"></script>\n<script type=\"module\" src=\"features/read-from-texture.js\"></script>\n<script type=\"module\" src=\"features/read-image-bitmap.js\"></script>\n<script type=\"module\" src=\"features/read-image-data.js\"></script>\n<script type=\"module\" src=\"features/read-offscreen-canvas.js\"></script>\n<script type=\"module\" src=\"features/return-arrays.js\"></script>\n<script type=\"module\" src=\"features/single-precision-textures.js\"></script>\n<script type=\"module\" src=\"features/single-precision.js\"></script>\n<script type=\"module\" src=\"features/switches.js\"></script>\n<script type=\"module\" src=\"features/tactic.js\"></script>\n<script type=\"module\" src=\"features/ternary.js\"></script>\n<script type=\"module\" src=\"features/type-management.js\"></script>\n<script type=\"module\" src=\"features/unsigned-precision-textures.js\"></script>\n<script type=\"module\" src=\"features/video.js\"></script>\n<script type=\"module\" src=\"internal/argument-texture-switching.js\"></script>\n<script type=\"module\" src=\"internal/boolean.js\"></script>\n<script type=\"module\" src=\"internal/casting.js\"></script>\n<script type=\"module\" src=\"internal/constants-texture-switching.js\"></script>\n<script type=\"module\" src=\"internal/constructor-features.js\"></script>\n<script type=\"module\" src=\"internal/context-inheritance.js\"></script>\n<script type=\"module\" src=\"internal/deep-types.js\"></script>\n<script type=\"module\" src=\"internal/deprecated.js\"></script>\n<script type=\"module\" src=\"internal/different-texture-cloning.js\"></script>\n<script type=\"module\" src=\"internal/function-builder.js\"></script>\n<script type=\"module\" src=\"internal/function-composition.js\"></script>\n<script type=\"module\" src=\"internal/function-node.js\"></script>\n<script type=\"module\" src=\"internal/function-return-type-detection.js\"></script>\n<script type=\"module\" src=\"internal/function-tracer.js\"></script>\n<script type=\"module\" src=\"internal/gpu-methods.js\"></script>\n<script type=\"module\" src=\"internal/implied-else.js\"></script>\n<script type=\"module\" src=\"internal/kernel-run-shortcut.js\"></script>\n<script type=\"module\" src=\"internal/kernel.js\"></script>\n<script type=\"module\" src=\"internal/loop-int.js\"></script>\n<script type=\"module\" src=\"internal/loop-max.js\"></script>\n<script type=\"module\" src=\"internal/math.random.js\"></script>\n<script type=\"module\" src=\"internal/matrix-multiply-precision.js\"></script>\n<script type=\"module\" src=\"internal/mixed-memory-optimize.js\"></script>\n<script type=\"module\" src=\"internal/modes.js\"></script>\n<script type=\"module\" src=\"internal/overloading.js\"></script>\n<script type=\"module\" src=\"internal/precision.js\"></script>\n<script type=\"module\" src=\"internal/recycling.js\"></script>\n<script type=\"module\" src=\"internal/texture-index.js\"></script>\n<script type=\"module\" src=\"internal/underscores.js\"></script>\n<script type=\"module\" src=\"internal/utils.js\"></script>\n<script type=\"module\" src=\"issues/114-create-kernel-map-run-second-time.js\"></script>\n<script type=\"module\" src=\"issues/116-multiple-kernels-run-again.js\"></script>\n<script type=\"module\" src=\"issues/130-typed-array.js\"></script>\n<script type=\"module\" src=\"issues/147-missing-constant.js\"></script>\n<script type=\"module\" src=\"issues/152-for-vars.js\"></script>\n<script type=\"module\" src=\"issues/159-3d.js\"></script>\n<script type=\"module\" src=\"issues/174-webgl-context-warning.js\"></script>\n<script type=\"module\" src=\"issues/195-read-from-texture2d.js\"></script>\n<script type=\"module\" src=\"issues/207-same-function-reuse.js\"></script>\n<script type=\"module\" src=\"issues/212-funky-function-support.js\"></script>\n<script type=\"module\" src=\"issues/233-kernel-map-single-precision.js\"></script>\n<script type=\"module\" src=\"issues/241-CPU-vs-GPU-maps-output-differently.js\"></script>\n<script type=\"module\" src=\"issues/259-atan2.js\"></script>\n<script type=\"module\" src=\"issues/263-to-string.js\"></script>\n<script type=\"module\" src=\"issues/267-immutable-sub-kernels.js\"></script>\n<script type=\"module\" src=\"issues/270-cache.js\"></script>\n<script type=\"module\" src=\"issues/279-wrong-canvas-size.js\"></script>\n<script type=\"module\" src=\"issues/300-nested-array-index.js\"></script>\n<script type=\"module\" src=\"issues/31-nested-var-declare-test.js\"></script>\n<script type=\"module\" src=\"issues/313-variable-lookup.js\"></script>\n<script type=\"module\" src=\"issues/314-large-input-array-addressing.js\"></script>\n<script type=\"module\" src=\"issues/335-missing-z-index-issue.js\"></script>\n<script type=\"module\" src=\"issues/346-uint8array-converted.js\"></script>\n<script type=\"module\" src=\"issues/349-division-by-factors-of-3.js\"></script>\n<script type=\"module\" src=\"issues/357-modulus-issue.js\"></script>\n<script type=\"module\" src=\"issues/359-addfunction-params-wrong.js\"></script>\n<script type=\"module\" src=\"issues/378-only-first-iteration.js\"></script>\n<script type=\"module\" src=\"issues/382-bad-constant.js\"></script>\n<script type=\"module\" src=\"issues/390-thread-assignment.js\"></script>\n<script type=\"module\" src=\"issues/396-combine-kernels-example.js\"></script>\n<script type=\"module\" src=\"issues/399-double-definition.js\"></script>\n<script type=\"module\" src=\"issues/401-cpu-canvas-check.js\"></script>\n<script type=\"module\" src=\"issues/410-if-statement.js\"></script>\n<script type=\"module\" src=\"issues/422-warnings.js\"></script>\n<script type=\"module\" src=\"issues/470-modulus-wrong.js\"></script>\n<script type=\"module\" src=\"issues/471-canvas-issue.js\"></script>\n<script type=\"module\" src=\"issues/472-compilation-issue.js\"></script>\n<script type=\"module\" src=\"issues/473-4-pixels.js\"></script>\n<script type=\"module\" src=\"issues/487-dynamic-arguments.js\"></script>\n<script type=\"module\" src=\"issues/493-strange-literal.js\"></script>\n<script type=\"module\" src=\"issues/500-sticky-arrays.js\"></script>\n<script type=\"module\" src=\"issues/519-sanitize-names.js\"></script>\n<script type=\"module\" src=\"issues/553-permanent-flip.js\"></script>\n<script type=\"module\" src=\"issues/556-minify-for-loop.js\"></script>\n<script type=\"module\" src=\"issues/560-minification-madness.js\"></script>\n<script type=\"module\" src=\"issues/564-boolean.js\"></script>\n<script type=\"module\" src=\"issues/567-wrong-modulus.js\"></script>\n<script type=\"module\" src=\"issues/585-inaccurate-lookups.js\"></script>\n<script type=\"module\" src=\"issues/586-unable-to-resize.js\"></script>\n<script type=\"module\" src=\"issues/608-rewritten-arrays.js\"></script>\n<script type=\"module\" src=\"issues/91-create-kernel-map-array.js\"></script>\n<script type=\"module\" src=\"issues/96-param-names.js\"></script>\n<script type=\"module\" src=\"features/to-string/as-file.js\"></script>\n<script type=\"module\" src=\"internal/backend/cpu-kernel.js\"></script>\n<script type=\"module\" src=\"internal/backend/gl-kernel.js\"></script>\n<script type=\"module\" src=\"internal/backend/function-node/isSafe.js\"></script>\n<script type=\"module\" src=\"internal/backend/function-node/isSafeDependencies.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/graphical.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/graphical.js\"></script>\n<script type=\"module\" src=\"internal/backend/headless-gl/kernel/index.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/astBinaryExpression.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/astCallExpression.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/astForStatement.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/astVariableDeclaration.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/contexts.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/firstAvailableTypeFromAst.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/getVariableSignature.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/function-node/getVariableType.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel/index.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel/setupArguments.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel/setupConstants.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-html-image.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-number-texture.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-single-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-single-array1d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-single-array2d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-single-array3d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-single-input.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-unsigned-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/dynamic-unsigned-input.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/html-image.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/number-texture.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/single-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/single-array1d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/single-array2d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/single-array3d-i.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/single-input.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/unsigned-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl/kernel-value/unsigned-input.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel/index.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel/setupArguments.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel/setupConstants.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel-value/dynamic-html-image-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel-value/dynamic-single-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel-value/dynamic-single-input.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel-value/html-image-array.js\"></script>\n<script type=\"module\" src=\"internal/backend/web-gl2/kernel-value/single-input.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array2.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array2d2.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array2d3.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array3.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/array4.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/boolean.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/float.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/html-canvas.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/html-image-array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/html-image.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/html-video.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/input.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/integer.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/arguments/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array2.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array3.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/array4.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/boolean.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/float.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/html-canvas.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/html-image-array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/html-image.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/input.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/integer.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/constants/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/returns/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/returns/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/returns/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/returns/texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array2.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array3.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/array4.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/boolean.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/float.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/html-canvas.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/html-image-array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/html-image.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/html-video.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/input.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/integer.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/arguments/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array2.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array3.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/array4.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/boolean.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/float.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/html-canvas.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/html-image-array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/html-image.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/input.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/integer.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/constants/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/returns/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/returns/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/returns/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/returns/texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/array/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/array/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/array/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/array/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/array/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/object/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/object/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/object/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/object/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/single/kernel-map/object/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/array/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/array/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/array/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/array/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/array/number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/object/array.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/object/array2d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/object/array3d.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/object/memory-optimized-number-texture.js\"></script>\n<script type=\"module\" src=\"features/to-string/precision/unsigned/kernel-map/object/number-texture.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "test/benchmark-faster.js",
    "content": "const { GPU } = require('../src/index.js');\nconst Benchmark = require('benchmark');\n\nconst suite = new Benchmark.Suite;\n\nconst size = 512;\n\nconst gpu = new GPU({ mode: 'gpu' });\nconst cpu = new GPU({ mode: 'cpu' });\n\nconst gpuKernel = gpu\n  .createKernel(function(i, j) {\n    return i[this.thread.x] + j[this.thread.x];\n  })\n  .setOutput([size, size]);\nconst gpuArg1 = gpu\n  .createKernel(function() {\n    return 0.89;\n  })\n  .setPipeline(true)\n  .setOutput([size, size])();\nconst gpuArg2 = gpu\n  .createKernel(function() {\n    return this.thread.x;\n  })\n  .setPipeline(true)\n  .setOutput([size, size])();\n\nconst cpuKernel = cpu\n  .createKernel(function(i, j) {\n    return i[this.thread.x] + j[this.thread.x];\n  })\n  .setOutput([size, size]);\nconst cpuArg1 = cpu\n  .createKernel(function() {\n    return 0.89;\n  })\n  .setOutput([size, size])();\nconst cpuArg2 = cpu\n  .createKernel(function() {\n    return this.thread.x;\n  })\n  .setOutput([size, size])();\n\nsuite\n  .add('gpu', () => {\n    gpuKernel(gpuArg1, gpuArg2);\n  })\n  .add('cpu', () => {\n    cpuKernel(cpuArg1, cpuArg2);\n  })\n  .on('cycle', (event) => {\n    console.log(String(event.target));\n  })\n  .on('complete', function () {\n    gpu.destroy();\n    cpu.destroy();\n    console.log('Fastest is ' + this.filter('fastest').map('name'));\n  })\n  .run();\n"
  },
  {
    "path": "test/benchmark.js",
    "content": "const { GPU } = require('../src/index.js');\nconst Benchmark = require('benchmark');\n\nconst suite = new Benchmark.Suite();\n\nconst gpu = new GPU({ mode: 'gpu' });\nconst cpu = new GPU({ mode: 'cpu' });\n\nconst size = 1024;\nconst a = randomMatrix(size, size);\nconst b = randomMatrix(size, size);\nfunction randomMatrix(width, height) {\n  const matrix = new Array(height);\n  for (let y = 0; y < height; y++) {\n    const row = matrix[y] = new Float32Array(width);\n    for (let x = 0; x < width; x++) {\n      row[x] = Math.random();\n    }\n  }\n  return matrix;\n}\n\nconst gpuKernel = gpu\n  .createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.size; i++) {\n      sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n  })\n  .setConstants({\n    size\n  })\n  .setPipeline(false)\n  .setPrecision('unsigned')\n  .setOutput([size, size]);\n\nconst cpuKernel = cpu\n  .createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.size; i++) {\n      sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n  })\n  .setConstants({\n    size\n  })\n  .setOutput([size, size]);\n\n// go ahead and build\ngpuKernel(a, b);\ncpuKernel(a, b);\n\n// add tests\nsuite\n  .add('gpu', () => {\n    gpuKernel(a, b);\n  })\n  .add('cpu', () => {\n    cpuKernel(a, b);\n  })\n  // add listeners\n  .on('cycle', (event) => {\n    console.log(String(event.target));\n  })\n  .on('complete', function () {\n    gpu.destroy();\n    cpu.destroy();\n    console.log('Fastest is ' + this.filter('fastest').map('name'));\n  })\n  .run({ async: false });\n"
  },
  {
    "path": "test/browser-test-utils.js",
    "content": "function imageToArray(image) {\n  const canvas = document.createElement('canvas');\n  canvas.width = image.width;\n  canvas.height = image.height;\n  document.body.appendChild(canvas);\n  document.body.appendChild(image);\n  const ctx = canvas.getContext('2d');\n  ctx.drawImage(image, 0, 0);\n  const { data } = ctx.getImageData(0, 0, image.width, image.height);\n  document.body.removeChild(canvas);\n  document.body.removeChild(image);\n  let i = 0;\n  const result = [];\n  for (let y = 0; y < image.height; y++) {\n    const row = [];\n    result.unshift(row);\n    for (let x = 0; x < image.width; x++) {\n      const pixel = new Float32Array([\n        data[i++],\n        data[i++],\n        data[i++],\n        data[i++],\n      ]);\n      row.push(pixel);\n    }\n  }\n  return result;\n}\n\nfunction loadImage(image) {\n  return new Promise((resolve) => {\n    if (typeof image === 'string') {\n      const src = image;\n      image = new Image();\n      image.src = src;\n    }\n    image.onload = () => {\n      resolve(image);\n    };\n  });\n}\n\nfunction loadImages(images) {\n  return Promise.all(images.map(image => loadImage(image)));\n}\n\nfunction check2DImage(result, expected, channel) {\n  const height = result.length;\n  const width = result[0].length;\n  for (let y = 0; y < height; y++) {\n    for (let x = 0; x < width; x++) {\n      if (result[y][x] !== expected[y][x][channel]) {\n        throw new Error(`result[${y}][${x}] value does not match expected value of ${expected[y][x][channel]}`);\n      }\n    }\n  }\n  return true;\n}\n\nfunction greenCanvas(mode, width, height) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    this.color(0, 1, 0, 1);\n  }, { output: [width, height], graphical: true });\n  kernel();\n  const canvas = kernel.canvas;\n  gpu.destroy();\n  return canvas;\n}\n\nconst _exports = {\n  greenCanvas,\n  imageToArray,\n  loadImage,\n  check2DImage,\n};\n\nif (typeof window !== 'undefined') {\n  window.browserTestUtils = _exports;\n} else {\n  module.exports = _exports;\n}\n\n"
  },
  {
    "path": "test/features/add-custom-function.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: add custom function');\n\nfunction inGPUInstanceSettings(mode) {\n  function customAdder(a, b) {\n    return a + b;\n  }\n  const gpu = new GPU({mode, functions: [customAdder] });\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6]\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('in GPU instance settings auto', () => {\n  inGPUInstanceSettings(null);\n});\n\ntest('in GPU instance settings gpu', () => {\n  inGPUInstanceSettings('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('in GPU instance settings webgl', () => {\n  inGPUInstanceSettings('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('in GPU instance settings webgl2', () => {\n  inGPUInstanceSettings('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('in GPU instance settings headlessgl', () => {\n  inGPUInstanceSettings('headlessgl');\n});\n\ntest('in GPU instance settings cpu', () => {\n  inGPUInstanceSettings('cpu');\n});\n\n\nfunction withGPUAddFunctionMethod(mode) {\n  function customAdder(a, b) {\n    return a + b;\n  }\n  const gpu = new GPU({ mode })\n    .addFunction(customAdder);\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6]\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('with GPU addFunction method auto', () => {\n  withGPUAddFunctionMethod(null);\n});\n\ntest('with GPU addFunction method gpu', () => {\n  withGPUAddFunctionMethod('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('with GPU addFunction method webgl', () => {\n  withGPUAddFunctionMethod('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with GPU addFunction method webgl2', () => {\n  withGPUAddFunctionMethod('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('with GPU addFunction method headlessgl', () => {\n  withGPUAddFunctionMethod('headlessgl');\n});\n\ntest('with GPU addFunction method cpu', () => {\n  withGPUAddFunctionMethod('cpu');\n});\n\nfunction inKernelInstanceSettings(mode) {\n  function customAdder(a, b) {\n    return a + b;\n  }\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6],\n    functions: [\n      customAdder\n    ],\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('in Kernel instance settings auto', () => {\n  inKernelInstanceSettings(null);\n});\n\ntest('in Kernel instance settings gpu', () => {\n  inKernelInstanceSettings('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('in Kernel instance settings webgl', () => {\n  inKernelInstanceSettings('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('in Kernel instance settings webgl2', () => {\n  inKernelInstanceSettings('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('in Kernel instance settings headlessgl', () => {\n  inKernelInstanceSettings('headlessgl');\n});\n\ntest('in Kernel instance settings cpu', () => {\n  inKernelInstanceSettings('cpu');\n});\n\nfunction withKernelAddFunctionMethod(mode) {\n  function customAdder(a, b) {\n    return a + b;\n  }\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6]\n  })\n    .addFunction(customAdder);\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('with Kernel addFunction method auto', () => {\n  withKernelAddFunctionMethod(null);\n});\n\ntest('with Kernel addFunction method gpu', () => {\n  withKernelAddFunctionMethod('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('with Kernel addFunction method webgl', () => {\n  withKernelAddFunctionMethod('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Kernel addFunction method webgl2', () => {\n  withKernelAddFunctionMethod('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('with Kernel addFunction method headlessgl', () => {\n  withKernelAddFunctionMethod('headlessgl');\n});\n\ntest('with Kernel addFunction method cpu', () => {\n  withKernelAddFunctionMethod('cpu');\n});\n\ndescribe('features: add custom function with `this.constants.width` in loop');\n\nfunction sumAB(mode) {\n  const gpu = new GPU({mode});\n\n  function customAdder(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.width; i++) {\n      sum += a[this.thread.x] + b[this.thread.x];\n    }\n    return sum;\n  }\n\n  gpu.addFunction(customAdder);\n\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a, b);\n  }, {\n    output: [6],\n    constants: {width: 6},\n    precision: 'unsigned',\n  });\n\n  assert.ok(kernel !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [1, 1, 1, 1, 1, 1];\n\n  const result = kernel(a, b);\n  const expected = [12, 18, 24, 36, 42, 48];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('sumAB auto', () => {\n  sumAB(null);\n});\n\ntest('sumAB gpu', () => {\n  sumAB('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('sumAB webgl', () => {\n  sumAB('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('sumAB webgl2', () => {\n  sumAB('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('sumAB headlessgl', () => {\n  sumAB('headlessgl');\n});\n\ntest('sumAB cpu', () => {\n  sumAB('cpu');\n});\n\ndescribe('features: add custom function with `this.output.x` in loop');\nfunction sumABThisOutputX(mode) {\n  const gpu = new GPU({ mode, functions: [customAdder] });\n\n  function customAdder(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.output.x; i++) {\n      sum += a[this.thread.x] + b[this.thread.x];\n    }\n    return sum;\n  }\n\n  const kernel = gpu.createKernel(function(a, b) {\n    return customAdder(a, b);\n  }, {\n    output : [6],\n  });\n\n  assert.ok(kernel !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [1, 1, 1, 1, 1, 1];\n\n  const result = kernel(a,b);\n  const expected = [12, 18, 24, 36, 42, 48];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('sumABThisOutputX auto', () => {\n  sumABThisOutputX(null);\n});\n\ntest('sumABThisOutputX gpu', () => {\n  sumABThisOutputX('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('sumABThisOutputX webgl', () => {\n  sumABThisOutputX('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('sumABThisOutputX webgl2', () => {\n  sumABThisOutputX('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('sumABThisOutputX headlessgl', () => {\n  sumABThisOutputX('headlessgl');\n});\n\ntest('sumABThisOutputX cpu', () => {\n  sumABThisOutputX('cpu');\n});\n\n\ndescribe('features: add custom private');\nfunction addCustomPrivate(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel = gpu.createKernel(function(a, b) {\n    function customAdder(a, b) {\n      let sum = 0;\n      for (let i = 0; i < this.output.x; i++) {\n        sum += a[this.thread.x] + b[this.thread.x];\n      }\n      return sum;\n    }\n    return customAdder(a, b);\n  }, {\n    output : [6],\n  });\n\n  assert.ok(kernel !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [1, 1, 1, 1, 1, 1];\n\n  const result = kernel(a,b);\n  const expected = [12, 18, 24, 36, 42, 48];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  addCustomPrivate(null);\n});\n\ntest('gpu', () => {\n  addCustomPrivate('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  addCustomPrivate('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  addCustomPrivate('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  addCustomPrivate('headlessgl');\n});\n\ntest('cpu', () => {\n  addCustomPrivate('cpu');\n});\n\ndescribe('features: setFunctions from array on kernel');\n\nfunction testSetFunctionsFromArrayOnKernel(mode) {\n  const gpu = new GPU({ mode });\n  function custom() {\n    return 1;\n  }\n  const kernel = gpu.createKernel(function() {\n    return custom();\n  }, { output: [1] });\n  kernel.setFunctions([custom]);\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testSetFunctionsFromArrayOnKernel();\n});\n\ntest('gpu', () => {\n  testSetFunctionsFromArrayOnKernel('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testSetFunctionsFromArrayOnKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testSetFunctionsFromArrayOnKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testSetFunctionsFromArrayOnKernel('headlessgl');\n});\n\ntest('cpu', () => {\n  testSetFunctionsFromArrayOnKernel('cpu');\n});\n\ndescribe('features: setFunctions from array on kernel');\n\nfunction testSetFunctionsFromArrayOnGPU(mode) {\n  const gpu = new GPU({ mode });\n  assert.equal(gpu.setFunctions([function custom() {\n    return 1;\n  }]), gpu);\n  const kernel = gpu.createKernel(function() {\n    return custom();\n  }, { output: [1] });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testSetFunctionsFromArrayOnGPU();\n});\n\ntest('gpu', () => {\n  testSetFunctionsFromArrayOnGPU('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testSetFunctionsFromArrayOnGPU('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testSetFunctionsFromArrayOnGPU('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testSetFunctionsFromArrayOnGPU('headlessgl');\n});\n\ntest('cpu', () => {\n  testSetFunctionsFromArrayOnGPU('cpu');\n});\n\ndescribe('features: setFunctions from array on kernel');\n\nfunction testAddIGPUFunction(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return custom(value);\n  })\n    .setOutput([1])\n    .addFunction({\n      name: 'custom',\n      argumentTypes: { value: 'Number' },\n      source: `function custom(value) {\n      return value + 1.0;\n    }`,\n      returnType: 'Number',\n    });\n  assert.equal(kernel(1)[0], 2);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testAddIGPUFunction();\n});\n\ntest('gpu', () => {\n  testAddIGPUFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testAddIGPUFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testAddIGPUFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testAddIGPUFunction('headlessgl');\n});\n\ntest('cpu', () => {\n  testAddIGPUFunction('cpu');\n});"
  },
  {
    "path": "test/features/add-custom-native-function.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: add native');\n\nconst glslDivide = `float divide(float a, float b) {\n  return a / b;\n}`;\nconst jsDivide = `function divide(a, b) {\n  return a / b;\n}`;\n\nfunction nativeDivide(mode, fn) {\n  const gpu = new GPU({ mode });\n\n  gpu.addNativeFunction('divide', fn, { returnType: 'Number' });\n\n  const f = gpu.createKernel(function(a, b) {\n    return divide(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  assert.ok(f !== null, 'function generated test');\n\n  const a = [1, 4, 3, 5, 6, 3];\n  const b = [4, 2, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [0.25, 2, 0.5, 5, 3, 1];\n\n  for(let i = 0; i < exp.length; ++i) {\n    assert.equal(res[i], exp[i], 'Result arr idx: '+i);\n  }\n  gpu.destroy();\n}\n\ntest('nativeDivide auto', () => {\n  nativeDivide(null, glslDivide);\n});\n\ntest('nativeDivide gpu', () => {\n  nativeDivide('gpu', glslDivide);\n});\n\n(GPU.isWebGLSupported ? test : skip)('nativeDivide webgl', () => {\n  nativeDivide('webgl', glslDivide);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('nativeDivide webgl2', () => {\n  nativeDivide('webgl2', glslDivide);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('nativeDivide headlessgl', () => {\n  nativeDivide('headlessgl', glslDivide);\n});\n\ntest('nativeDivide cpu', () => {\n  nativeDivide('cpu', jsDivide);\n});\n\n\ndescribe('features: instantiate native and override');\n\nfunction divideOverride(mode) {\n  const gpu = new GPU({\n    mode,\n    functions: [divide],\n    nativeFunctions: [{\n      name: 'divide',\n      // deliberately add, rather than divide, to ensure native functions are treated as more important than regular ones\n      source: `float divide(float a, float b) {\n        return a + b;\n      }`\n    }]\n  });\n\n  function divide(a,b) {\n    return a / b;\n  }\n\n  const kernel = gpu.createKernel(function(a, b) {\n    return divide(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  const a = [1, 4, 3, 5, 6, 3];\n  const b = [4, 2, 6, 1, 2, 3];\n\n  const res = kernel(a,b);\n  const exp = [5, 6, 9, 6, 8, 6];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('divideOverride (GPU only) auto', () => {\n  divideOverride(null);\n});\n\ntest('divideOverride (GPU only) gpu', () => {\n  divideOverride('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('divideOverride (GPU only) webgl', () => {\n  divideOverride('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('divideOverride (GPU only) webgl2', () => {\n  divideOverride('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('divideOverride (GPU only) headlessgl', () => {\n  divideOverride('headlessgl');\n});\n\ndescribe('features: argument casting');\n\nfunction argumentCasting(mode) {\n  const gpu = new GPU({\n    mode,\n    functions: [divide],\n    nativeFunctions: [{\n      // deliberately add, rather than divide, to ensure native functions are treated as more important than regular ones\n      name: 'divide',\n      source: `float divide(int a, int b) {\n        return float(a + b);\n      }`\n    }]\n  });\n\n  function divide(a,b) {\n    return a / b;\n  }\n\n  const kernel = gpu.createKernel(function(a, b) {\n    return divide(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  const a = [1, 4, 3, 5, 6, 3];\n  const b = [4, 2, 6, 1, 2, 3];\n\n  const res = kernel(a,b);\n  const exp = [5, 6, 9, 6, 8, 6];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('argumentCasting (GPU only) auto', () => {\n  argumentCasting(null);\n});\n\ntest('argumentCasting (GPU only) gpu', () => {\n  argumentCasting('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('argumentCasting (GPU only) webgl', () => {\n  argumentCasting('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('argumentCasting (GPU only) webgl2', () => {\n  argumentCasting('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('argumentCasting (GPU only) headlessgl', () => {\n  argumentCasting('headlessgl');\n});\n\n\ndescribe('features: mixed argument casting');\n\nfunction mixedArgumentCasting(mode) {\n  const gpu = new GPU({\n    mode,\n    functions: [divide],\n    nativeFunctions: [{\n      // deliberately add, rather than divide, to ensure native functions are treated as more important than regular ones\n      name: 'divide',\n      source: `float divide(int a, float b) {\n        return float(a + int(b));\n      }`\n    }]\n  });\n\n  function divide(a,b) {\n    return a / b;\n  }\n\n  const kernel = gpu.createKernel(function(a, b) {\n    return divide(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  const a = [1, 4, 3, 5, 6, 3];\n  const b = [4, 2, 6, 1, 2, 3];\n\n  const res = kernel(a,b);\n  const exp = [5, 6, 9, 6, 8, 6];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('mixedArgumentCasting (GPU only) auto', () => {\n  mixedArgumentCasting(null);\n});\n\ntest('mixedArgumentCasting (GPU only) gpu', () => {\n  mixedArgumentCasting('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('mixedArgumentCasting (GPU only) webgl', () => {\n  mixedArgumentCasting('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('mixedArgumentCasting (GPU only) webgl2', () => {\n  mixedArgumentCasting('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('mixedArgumentCasting (GPU only) headlessgl', () => {\n  mixedArgumentCasting('headlessgl');\n});\n\ndescribe('features: return type casting');\n\nfunction returnTypeCasting(mode) {\n  const gpu = new GPU({\n    mode,\n    functions: [divide],\n    nativeFunctions: [{\n      // deliberately add, rather than divide, to ensure native functions are treated as more important than regular ones\n      name: 'divide',\n      source: `int divide(float a, float b) {\n        return int(a + b);\n      }`\n    }]\n  });\n\n  function divide(a,b) {\n    return a / b;\n  }\n\n  const kernel = gpu.createKernel(function(a, b) {\n    return divide(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  const a = [1, 4, 3, 5, 6, 3];\n  const b = [4, 2, 6, 1, 2, 3];\n\n  const res = kernel(a,b);\n  const exp = [5, 6, 9, 6, 8, 6];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('returnTypeCasting (GPU only) auto', () => {\n  returnTypeCasting(null);\n});\n\ntest('returnTypeCasting (GPU only) gpu', () => {\n  returnTypeCasting('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('returnTypeCasting (GPU only) webgl', () => {\n  returnTypeCasting('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('returnTypeCasting (GPU only) webgl2', () => {\n  returnTypeCasting('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('returnTypeCasting (GPU only) headlessgl', () => {\n  returnTypeCasting('headlessgl');\n});\n\ndescribe('features: Adding nativeFunctions directly on kernel');\n\nfunction testDirectlyOnKernelViaSettings(nativeFunctions, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (v) {\n    return native(v[this.thread.x]);\n  }, {\n    output: [1],\n    nativeFunctions\n  });\n  assert.equal(kernel([1])[0], 2);\n  gpu.destroy();\n}\n\ntest('via settings auto', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ])\n});\n\ntest('via settings gpu', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'gpu')\n});\n\n(GPU.isWebGLSupported ? test : skip)('via settings webgl', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'webgl')\n});\n\n(GPU.isWebGL2Supported ? test : skip)('via settings webgl2', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'webgl2')\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('via settings headlessgl', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'headlessgl')\n});\n\ntest('via settings cpu', () => {\n  testDirectlyOnKernelViaSettings([\n    {\n      name: 'native',\n      source: `function native(value) {\n        return 1.0 + value;\n      }`,\n      returnType: 'Float'\n    }\n  ], 'cpu')\n});\n\n\ndescribe('features: Adding nativeFunctions directly on kernel');\n\n/**\n *\n * @param {IGPUNativeFunction[]} nativeFunctions\n * @param {GPUMode|GPUInternalMode} [mode]\n */\nfunction testDirectlyOnKernelViaMethod(nativeFunctions, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (v) {\n    return native(v[this.thread.x]);\n  }, {\n    output: [1]\n  })\n    .setNativeFunctions(nativeFunctions);\n  assert.equal(kernel([1])[0], 2);\n  gpu.destroy();\n}\n\ntest('via method auto', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ])\n});\n\ntest('via method gpu', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'gpu')\n});\n\n(GPU.isWebGLSupported ? test : skip)('via method webgl', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'webgl')\n});\n\n(GPU.isWebGL2Supported ? test : skip)('via method webgl2', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'webgl2')\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('via method headlessgl', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `float native(float value) {\n        return 1.0 + value;\n      }`\n    }\n  ], 'headlessgl')\n});\n\ntest('via method cpu', () => {\n  testDirectlyOnKernelViaMethod([\n    {\n      name: 'native',\n      source: `function native(value) {\n        return 1.0 + value;\n      }`,\n      returnType: 'Float'\n    }\n  ], 'cpu')\n});\n\n\nfunction testSetNativeFunctionsFromArrayOnGPU(nativeFunction, mode) {\n  const gpu = new GPU({ mode });\n  assert.equal(gpu.setNativeFunctions([nativeFunction]), gpu);\n  const kernel = gpu.createKernel(function() {\n    return custom();\n  }, { output: [1] });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `float custom() {\n      return 1.0;\n    }`\n  });\n});\n\ntest('gpu', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `float custom() {\n      return 1.0;\n    }`\n  },'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `float custom() {\n      return 1.0;\n    }`\n  },'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `float custom() {\n      return 1.0;\n    }`\n  }, 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `float custom() {\n      return 1.0;\n    }`\n  },'headlessgl');\n});\n\ntest('cpu', () => {\n  testSetNativeFunctionsFromArrayOnGPU({\n    name: 'custom',\n    source: `function custom() {\n      return 1.0;\n    }`,\n    returnType: 'Number'\n  },'cpu');\n});"
  },
  {
    "path": "test/features/add-typed-functions.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: add typed functions vec2Test');\nfunction vec2Test(mode) {\n  const gpu = new GPU({ mode });\n  function typedFunction() {\n    return [1, 2];\n  }\n  gpu.addFunction(typedFunction, {\n    returnType: 'Array(2)'\n  });\n  const kernel = gpu.createKernel(function() {\n    const result = typedFunction();\n    return result[0] + result[1];\n  })\n    .setOutput([1]);\n\n  const result = kernel();\n  assert.equal(result[0], 3);\n  gpu.destroy();\n}\n\ntest('Array(2) - auto', () => {\n  vec2Test(null);\n});\ntest('Array(2) - gpu', () => {\n  vec2Test('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('Array(2) - webgl', () => {\n  vec2Test('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('Array(2) - webgl2', () => {\n  vec2Test('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Array(2) - headlessgl', () => {\n  vec2Test('headlessgl');\n});\n\n\ndescribe('features: add typed functions vec3Test');\nfunction vec3Test(mode) {\n  const gpu = new GPU({ mode });\n  function typedFunction() {\n    return [1, 2, 3];\n  }\n  gpu.addFunction(typedFunction, {\n    returnType: 'Array(3)'\n  });\n  const kernel = gpu.createKernel(function() {\n    const result = typedFunction();\n    return result[0] + result[1] + result[2];\n  })\n    .setOutput([1]);\n  const result = kernel();\n  assert.equal(result[0], 6);\n  gpu.destroy();\n}\n\ntest('Array(3) - auto', () => {\n  vec3Test(null);\n});\ntest('Array(3) - gpu', () => {\n  vec3Test('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('Array(3) - webgl', () => {\n  vec3Test('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('Array(3) - webgl2', () => {\n  vec3Test('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Array(3) - headlessgl', () => {\n  vec3Test('headlessgl');\n});\n\ndescribe('features: add typed functions vec4Test');\nfunction vec4Test(mode) {\n  const gpu = new GPU({ mode });\n  function typedFunction() {\n    return [1, 2, 3, 4];\n  }\n  gpu.addFunction(typedFunction, {\n    returnType: 'Array(4)'\n  });\n  const kernel = gpu.createKernel(function() {\n    const result = typedFunction();\n    return result[0] + result[1] + result[2] + result[3];\n  })\n    .setOutput([1]);\n  const result = kernel();\n  assert.equal(result[0], 10);\n  gpu.destroy();\n}\n\ntest('Array(4) - auto', () => {\n  vec4Test(null);\n});\ntest('Array(4) - gpu', () => {\n  vec4Test('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('Array(4) - webgl', () => {\n  vec4Test('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('Array(4) - webgl2', () => {\n  vec4Test('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Array(4) - headlessgl', () => {\n  vec4Test('headlessgl');\n});\n"
  },
  {
    "path": "test/features/argument-array-types.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('argument array types');\n\nfunction testSinglePrecisionArray2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(2)' },\n    precision: 'single',\n  });\n  const result = kernel(new Float32Array([1,2]));\n  assert.equal(result[0], 3);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(2) auto', () => {\n  testSinglePrecisionArray2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(2) gpu', () => {\n  testSinglePrecisionArray2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array(2) webgl', () => {\n  testSinglePrecisionArray2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array(2) webgl2', () => {\n  testSinglePrecisionArray2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array(2) headlessgl', () => {\n  testSinglePrecisionArray2('headlessgl');\n});\n\ntest('single precision Array(2) cpu', () => {\n  testSinglePrecisionArray2('cpu');\n});\n\nfunction testUnsignedPrecisionArray2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(2)' },\n    precision: 'unsigned',\n  });\n  const result = kernel(new Float32Array([1,2]));\n  assert.equal(result[0], 3);\n  gpu.destroy();\n}\n\ntest('unsigned precision Array(2) auto', () => {\n  testUnsignedPrecisionArray2();\n});\n\ntest('unsigned precision Array(2) gpu', () => {\n  testUnsignedPrecisionArray2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Array(2) webgl', () => {\n  testUnsignedPrecisionArray2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Array(2) webgl2', () => {\n  testUnsignedPrecisionArray2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Array(2) headlessgl', () => {\n  testUnsignedPrecisionArray2('headlessgl');\n});\n\ntest('unsigned precision Array(2) cpu', () => {\n  testUnsignedPrecisionArray2('cpu');\n});\n\nfunction testSinglePrecisionArray3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1] + value[2];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(3)' },\n    precision: 'single',\n  });\n  const result = kernel(new Float32Array([1,2,3]));\n  assert.equal(result[0], 6);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(3) auto', () => {\n  testSinglePrecisionArray3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(3) gpu', () => {\n  testSinglePrecisionArray3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array(3) webgl', () => {\n  testSinglePrecisionArray3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array(3) webgl2', () => {\n  testSinglePrecisionArray3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array(3) headlessgl', () => {\n  testSinglePrecisionArray3('headlessgl');\n});\n\ntest('single precision Array(3) cpu', () => {\n  testSinglePrecisionArray3('cpu');\n});\n\nfunction testUnsignedPrecisionArray3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1] + value[2];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(3)' },\n    precision: 'unsigned',\n  });\n  const result = kernel(new Float32Array([1,2,3]));\n  assert.equal(result[0], 6);\n  gpu.destroy();\n}\n\ntest('unsigned precision Array(3) auto', () => {\n  testUnsignedPrecisionArray3();\n});\n\ntest('unsigned precision Array(3) gpu', () => {\n  testUnsignedPrecisionArray3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Array(3) webgl', () => {\n  testUnsignedPrecisionArray3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Array(3) webgl2', () => {\n  testUnsignedPrecisionArray3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Array(3) headlessgl', () => {\n  testUnsignedPrecisionArray3('headlessgl');\n});\n\ntest('unsigned precision Array(3) cpu', () => {\n  testUnsignedPrecisionArray3('cpu');\n});\n\nfunction testSinglePrecisionArray4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1] + value[2] + value[3];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(4)' },\n    precision: 'single',\n  });\n  const result = kernel(new Float32Array([1,2,3,4]));\n  assert.equal(result[0], 10);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(4) auto', () => {\n  testSinglePrecisionArray4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array(4) gpu', () => {\n  testSinglePrecisionArray4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array(4) webgl', () => {\n  testSinglePrecisionArray4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array(4) webgl2', () => {\n  testSinglePrecisionArray4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array(4) headlessgl', () => {\n  testSinglePrecisionArray4('headlessgl');\n});\n\ntest('single precision Array(4) cpu', () => {\n  testSinglePrecisionArray4('cpu');\n});\n\nfunction testUnsignedPrecisionArray4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + value[1] + value[2] + value[3];\n  }, {\n    output: [1],\n    argumentTypes: { value: 'Array(4)' },\n    precision: 'unsigned',\n  });\n  const result = kernel(new Float32Array([1,2,3,4]));\n  assert.equal(result[0], 10);\n  gpu.destroy();\n}\n\ntest('unsigned precision Array(4) auto', () => {\n  testUnsignedPrecisionArray4();\n});\n\ntest('unsigned precision Array(4) gpu', () => {\n  testUnsignedPrecisionArray4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Array(4) webgl', () => {\n  testUnsignedPrecisionArray4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Array(4) webgl2', () => {\n  testUnsignedPrecisionArray4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Array(4) headlessgl', () => {\n  testUnsignedPrecisionArray4('headlessgl');\n});\n\ntest('unsigned precision Array(4) cpu', () => {\n  testUnsignedPrecisionArray4('cpu');\n});\n"
  },
  {
    "path": "test/features/argument-array1d-types.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('argument array 1 types');\n\nfunction testSinglePrecisionArray1D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n    argumentTypes: { value: 'Array1D(2)' },\n    precision: 'single',\n  });\n  const value = [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n    new Float32Array([5,6]),\n    new Float32Array([7,8]),\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array1D(2) auto', () => {\n  testSinglePrecisionArray1D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array1D(2) gpu', () => {\n  testSinglePrecisionArray1D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array1D(2) webgl', () => {\n  testSinglePrecisionArray1D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array1D(2) webgl2', () => {\n  testSinglePrecisionArray1D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array1D(2) headlessgl', () => {\n  testSinglePrecisionArray1D2('headlessgl');\n});\n\ntest('single precision Array1D(2) cpu', () => {\n  testSinglePrecisionArray1D2('cpu');\n});\n\nfunction testUnsignedPrecisionArray1D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n    argumentTypes: { value: 'Array1D(2)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n    new Float32Array([5,6]),\n    new Float32Array([7,8]),\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array1D(2) auto', () => {\n  testUnsignedPrecisionArray1D2();\n});\n\ntest('fallback unsigned precision Array1D(2) gpu', () => {\n  testUnsignedPrecisionArray1D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array1D(2) webgl', () => {\n  testUnsignedPrecisionArray1D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array1D(2) webgl2', () => {\n  testUnsignedPrecisionArray1D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array1D(2) headlessgl', () => {\n  testUnsignedPrecisionArray1D2('headlessgl');\n});\n\ntest('fallback unsigned precision Array1D(2) cpu', () => {\n  testUnsignedPrecisionArray1D2('cpu');\n});\n\nfunction testSinglePrecisionArray1D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n    argumentTypes: { value: 'Array1D(3)' },\n    precision: 'single',\n  });\n  const value = [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n    new Float32Array([10,11,12]),\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array1D(3) auto', () => {\n  testSinglePrecisionArray1D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array1D(3) gpu', () => {\n  testSinglePrecisionArray1D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array1D(3) webgl', () => {\n  testSinglePrecisionArray1D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array1D(3) webgl2', () => {\n  testSinglePrecisionArray1D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array1D(3) headlessgl', () => {\n  testSinglePrecisionArray1D3('headlessgl');\n});\n\ntest('single precision Array1D(3) cpu', () => {\n  testSinglePrecisionArray1D3('cpu');\n});\n\nfunction testUnsignedPrecisionArray1D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n    argumentTypes: { value: 'Array1D(3)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n    new Float32Array([10,11,12]),\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array1D(3) auto', () => {\n  testUnsignedPrecisionArray1D3();\n});\n\ntest('fallback unsigned precision Array1D(3) gpu', () => {\n  testUnsignedPrecisionArray1D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array1D(3) webgl', () => {\n  testUnsignedPrecisionArray1D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array1D(3) webgl2', () => {\n  testUnsignedPrecisionArray1D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array1D(3) headlessgl', () => {\n  testUnsignedPrecisionArray1D3('headlessgl');\n});\n\ntest('fallback unsigned precision Array1D(3) cpu', () => {\n  testUnsignedPrecisionArray1D3('cpu');\n});\n\nfunction testUnsignedPrecisionArray1D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n    argumentTypes: { value: 'Array1D(4)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array1D(4) auto', () => {\n  testUnsignedPrecisionArray1D4();\n});\n\ntest('fallback unsigned precision Array1D(4) gpu', () => {\n  testUnsignedPrecisionArray1D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array1D(4) webgl', () => {\n  testUnsignedPrecisionArray1D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array1D(4) webgl2', () => {\n  testUnsignedPrecisionArray1D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array1D(4) headlessgl', () => {\n  testUnsignedPrecisionArray1D4('headlessgl');\n});\n\ntest('fallback unsigned precision Array1D(4) cpu', () => {\n  testUnsignedPrecisionArray1D4('cpu');\n});\n"
  },
  {
    "path": "test/features/argument-array2d-types.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('argument array 2 types');\n\nfunction testSinglePrecisionArray2D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(2)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      new Float32Array([1,2]),\n      new Float32Array([3,4]),\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ], [\n      new Float32Array([9,10]),\n      new Float32Array([11,12]),\n      new Float32Array([13,14]),\n      new Float32Array([15,16]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(2) auto', () => {\n  testSinglePrecisionArray2D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(2) gpu', () => {\n  testSinglePrecisionArray2D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array2D(2) webgl', () => {\n  testSinglePrecisionArray2D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array2D(2) webgl2', () => {\n  testSinglePrecisionArray2D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array2D(2) headlessgl', () => {\n  testSinglePrecisionArray2D2('headlessgl');\n});\n\ntest('single precision Array2D(2) cpu', () => {\n  testSinglePrecisionArray2D2('cpu');\n});\n\nfunction testUnsignedPrecisionArray2D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(2)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      new Float32Array([1,2]),\n      new Float32Array([3,4]),\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ], [\n      new Float32Array([9,10]),\n      new Float32Array([11,12]),\n      new Float32Array([13,14]),\n      new Float32Array([15,16]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array2D(2) auto', () => {\n  testUnsignedPrecisionArray2D2();\n});\n\ntest('fallback unsigned precision Array2D(2) gpu', () => {\n  testUnsignedPrecisionArray2D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array2D(2) webgl', () => {\n  testUnsignedPrecisionArray2D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array2D(2) webgl2', () => {\n  testUnsignedPrecisionArray2D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array(2) headlessgl', () => {\n  testUnsignedPrecisionArray2D2('headlessgl');\n});\n\ntest('fallback unsigned precision Array2D(2) cpu', () => {\n  testUnsignedPrecisionArray2D2('cpu');\n});\n\nfunction testSinglePrecisionArray2D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(3)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      new Float32Array([1,2,3]),\n      new Float32Array([4,5,6]),\n      new Float32Array([7,8,9]),\n      new Float32Array([10,11,12]),\n    ], [\n      new Float32Array([13,14,15]),\n      new Float32Array([16,17,18]),\n      new Float32Array([19,20,21]),\n      new Float32Array([22,23,25]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(3) auto', () => {\n  testSinglePrecisionArray2D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(3) gpu', () => {\n  testSinglePrecisionArray2D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array2D(3) webgl', () => {\n  testSinglePrecisionArray2D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array2D(3) webgl2', () => {\n  testSinglePrecisionArray2D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array2D(3) headlessgl', () => {\n  testSinglePrecisionArray2D3('headlessgl');\n});\n\ntest('single precision Array2D(3) cpu', () => {\n  testSinglePrecisionArray2D3('cpu');\n});\n\nfunction testUnsignedPrecisionArray2D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(3)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      new Float32Array([1,2,3]),\n      new Float32Array([4,5,6]),\n      new Float32Array([7,8,9]),\n      new Float32Array([10,11,12]),\n    ], [\n      new Float32Array([13,14,15]),\n      new Float32Array([16,17,18]),\n      new Float32Array([19,20,21]),\n      new Float32Array([22,23,25]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array2D(3) auto', () => {\n  testUnsignedPrecisionArray2D3();\n});\n\ntest('fallback unsigned precision Array2D(3) gpu', () => {\n  testUnsignedPrecisionArray2D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array2D(3) webgl', () => {\n  testUnsignedPrecisionArray2D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array2D(3) webgl2', () => {\n  testUnsignedPrecisionArray2D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array(3) headlessgl', () => {\n  testUnsignedPrecisionArray2D3('headlessgl');\n});\n\ntest('fallback unsigned precision Array2D(3) cpu', () => {\n  testUnsignedPrecisionArray2D3('cpu');\n});\n\nfunction testSinglePrecisionArray2D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(4)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ], [\n      new Float32Array([17,18,19,20]),\n      new Float32Array([21,22,23,24]),\n      new Float32Array([25,26,27,28]),\n      new Float32Array([29,30,31,32]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(4) auto', () => {\n  testSinglePrecisionArray2D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array2D(4) gpu', () => {\n  testSinglePrecisionArray2D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array2D(4) webgl', () => {\n  testSinglePrecisionArray2D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array2D(4) webgl2', () => {\n  testSinglePrecisionArray2D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array2D(4) headlessgl', () => {\n  testSinglePrecisionArray2D4('headlessgl');\n});\n\ntest('single precision Array2D(4) cpu', () => {\n  testSinglePrecisionArray2D4('cpu');\n});\n\nfunction testUnsignedPrecisionArray2D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, {\n    output: [4, 2],\n    argumentTypes: { value: 'Array2D(4)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ], [\n      new Float32Array([17,18,19,20]),\n      new Float32Array([21,22,23,24]),\n      new Float32Array([25,26,27,28]),\n      new Float32Array([29,30,31,32]),\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array2D(4) auto', () => {\n  testUnsignedPrecisionArray2D4();\n});\n\ntest('fallback unsigned precision Array2D(4) gpu', () => {\n  testUnsignedPrecisionArray2D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array2D(4) webgl', () => {\n  testUnsignedPrecisionArray2D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array2D(4) webgl2', () => {\n  testUnsignedPrecisionArray2D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array(4) headlessgl', () => {\n  testUnsignedPrecisionArray2D4('headlessgl');\n});\n\ntest('fallback unsigned precision Array2D(4) cpu', () => {\n  testUnsignedPrecisionArray2D4('cpu');\n});\n"
  },
  {
    "path": "test/features/argument-array3d-types.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('argument array 3 types');\n\nfunction testSinglePrecisionArray3D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array3D(2)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2]),\n        new Float32Array([3,4]),\n      ],[\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ]\n    ],[\n      [\n        new Float32Array([9,10]),\n        new Float32Array([11,12]),\n      ],[\n        new Float32Array([13,14]),\n        new Float32Array([15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18]),\n        new Float32Array([19,20]),\n      ],[\n        new Float32Array([21,22]),\n        new Float32Array([23,24]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(2) auto', () => {\n  testSinglePrecisionArray3D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(2) gpu', () => {\n  testSinglePrecisionArray3D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array3D(2) webgl', () => {\n  testSinglePrecisionArray3D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array3D(2) webgl2', () => {\n  testSinglePrecisionArray3D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array3D(2) headlessgl', () => {\n  testSinglePrecisionArray3D2('headlessgl');\n});\n\ntest('single precision Array3D(2) cpu', () => {\n  testSinglePrecisionArray3D2('cpu');\n});\n\nfunction testUnsignedPrecisionArray3D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array2D(2)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2]),\n        new Float32Array([3,4]),\n      ],[\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ]\n    ],[\n      [\n        new Float32Array([9,10]),\n        new Float32Array([11,12]),\n      ],[\n        new Float32Array([13,14]),\n        new Float32Array([15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18]),\n        new Float32Array([19,20]),\n      ],[\n        new Float32Array([21,22]),\n        new Float32Array([23,24]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array3D(3) auto', () => {\n  testUnsignedPrecisionArray3D2();\n});\n\ntest('fallback unsigned precision Array3D(3) gpu', () => {\n  testUnsignedPrecisionArray3D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array3D(3) webgl', () => {\n  testUnsignedPrecisionArray3D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array3D(3) webgl2', () => {\n  testUnsignedPrecisionArray3D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array3D(3) headlessgl', () => {\n  testUnsignedPrecisionArray3D2('headlessgl');\n});\n\ntest('fallback unsigned precision Array3D(3) cpu', () => {\n  testUnsignedPrecisionArray3D2('cpu');\n});\n\nfunction testSinglePrecisionArray3D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array3D(3)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2,3]),\n        new Float32Array([4,5,6]),\n      ],[\n        new Float32Array([7,8,9]),\n        new Float32Array([10,11,12]),\n      ]\n    ],[\n      [\n        new Float32Array([13,14,15]),\n        new Float32Array([16,17,18]),\n      ],[\n        new Float32Array([19,20,21]),\n        new Float32Array([22,23,24]),\n      ]\n    ],[\n      [\n        new Float32Array([25,26,27]),\n        new Float32Array([28,29,30]),\n      ],[\n        new Float32Array([31,32,33]),\n        new Float32Array([34,35,36]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(3) auto', () => {\n  testSinglePrecisionArray3D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(3) gpu', () => {\n  testSinglePrecisionArray3D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array3D(3) webgl', () => {\n  testSinglePrecisionArray3D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array3D(3) webgl2', () => {\n  testSinglePrecisionArray3D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array3D(3) headlessgl', () => {\n  testSinglePrecisionArray3D3('headlessgl');\n});\n\ntest('single precision Array3D(3) cpu', () => {\n  testSinglePrecisionArray3D3('cpu');\n});\n\nfunction testUnsignedPrecisionArray3D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array2D(3)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2,3]),\n        new Float32Array([4,5,6]),\n      ],[\n      new Float32Array([7,8,9]),\n      new Float32Array([10,11,12]),\n    ]\n    ],[\n      [\n        new Float32Array([13,14,15]),\n        new Float32Array([16,17,18]),\n      ],[\n        new Float32Array([19,20,21]),\n        new Float32Array([22,23,24]),\n      ]\n    ],[\n      [\n        new Float32Array([25,26,27]),\n        new Float32Array([28,29,30]),\n      ],[\n        new Float32Array([31,32,33]),\n        new Float32Array([34,35,36]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array3D(3) auto', () => {\n  testUnsignedPrecisionArray3D3();\n});\n\ntest('fallback unsigned precision Array3D(3) gpu', () => {\n  testUnsignedPrecisionArray3D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array3D(3) webgl', () => {\n  testUnsignedPrecisionArray3D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array3D(3) webgl2', () => {\n  testUnsignedPrecisionArray3D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array3D(3) headlessgl', () => {\n  testUnsignedPrecisionArray3D3('headlessgl');\n});\n\ntest('fallback unsigned precision Array3D(3) cpu', () => {\n  testUnsignedPrecisionArray3D3('cpu');\n});\n\nfunction testSinglePrecisionArray3D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array3D(4)' },\n    precision: 'single',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2,3,4]),\n        new Float32Array([5,6,7,8]),\n      ],[\n        new Float32Array([9,10,11,12]),\n        new Float32Array([13,14,15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18,19,20]),\n        new Float32Array([21,22,23,24]),\n      ],[\n        new Float32Array([25,26,27,28]),\n        new Float32Array([29,30,31,32]),\n      ]\n    ],[\n      [\n        new Float32Array([33,34,35,36]),\n        new Float32Array([37,38,39,40]),\n      ],[\n        new Float32Array([41,42,43,44]),\n        new Float32Array([45,46,47,48]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(4) auto', () => {\n  testSinglePrecisionArray3D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array3D(4) gpu', () => {\n  testSinglePrecisionArray3D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array3D(4) webgl', () => {\n  testSinglePrecisionArray3D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array3D(4) webgl2', () => {\n  testSinglePrecisionArray3D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array3D(4) headlessgl', () => {\n  testSinglePrecisionArray3D4('headlessgl');\n});\n\ntest('single precision Array3D(4) cpu', () => {\n  testSinglePrecisionArray3D4('cpu');\n});\n\nfunction testUnsignedPrecisionArray3D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [2,2,3],\n    argumentTypes: { value: 'Array2D(3)' },\n    precision: 'unsigned',\n  });\n  const value = [\n    [\n      [\n        new Float32Array([1,2,3,4]),\n        new Float32Array([5,6,7,8]),\n      ],[\n        new Float32Array([9,10,11,12]),\n        new Float32Array([13,14,15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18,19,20]),\n        new Float32Array([21,22,23,24]),\n      ],[\n        new Float32Array([25,26,27,28]),\n        new Float32Array([29,30,31,32]),\n      ]\n    ],[\n      [\n        new Float32Array([33,34,35,36]),\n        new Float32Array([37,38,39,40]),\n      ],[\n        new Float32Array([41,42,43,44]),\n        new Float32Array([45,46,47,48]),\n      ]\n    ]\n  ];\n  const result = kernel(value);\n  assert.deepEqual(result, value);\n  gpu.destroy();\n}\n\ntest('fallback unsigned precision Array3D(4) auto', () => {\n  testUnsignedPrecisionArray3D4();\n});\n\ntest('fallback unsigned precision Array3D(4) gpu', () => {\n  testUnsignedPrecisionArray3D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fallback unsigned precision Array3D(4) webgl', () => {\n  testUnsignedPrecisionArray3D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fallback unsigned precision Array3D(4) webgl2', () => {\n  testUnsignedPrecisionArray3D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fallback unsigned precision Array3D(4) headlessgl', () => {\n  testUnsignedPrecisionArray3D4('headlessgl');\n});\n\ntest('fallback unsigned precision Array3D(4) cpu', () => {\n  testUnsignedPrecisionArray3D4('cpu');\n});\n"
  },
  {
    "path": "test/features/arithmetic-operators.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: arithmetic operators');\n\nfunction addition(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 3 + 2;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3 + 2);\n  gpu.destroy();\n}\n\ntest('addition auto', () => {\n  addition();\n});\n\n(GPU.isGPUSupported ? test : skip)('addition gpu', () => {\n  addition('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('addition webgl', () => {\n  addition('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('addition webgl2', () => {\n  addition('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('addition headlessgl', () => {\n  addition('headlessgl');\n});\n\ntest('addition cpu', () => {\n  addition('cpu');\n});\n\n\nfunction subtraction(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 3 - 2;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3 - 2);\n  gpu.destroy();\n}\n\ntest('subtraction auto', () => {\n  subtraction();\n});\n\n(GPU.isGPUSupported ? test : skip)('subtraction gpu', () => {\n  subtraction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('subtraction webgl', () => {\n  subtraction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('subtraction webgl2', () => {\n  subtraction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('subtraction headlessgl', () => {\n  subtraction('headlessgl');\n});\n\ntest('subtraction cpu', () => {\n  subtraction('cpu');\n});\n\nfunction multiplication(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 3 * 2;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3 * 2);\n  gpu.destroy();\n}\n\ntest('multiplication auto', () => {\n  multiplication();\n});\n\n(GPU.isGPUSupported ? test : skip)('multiplication gpu', () => {\n  multiplication('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('multiplication webgl', () => {\n  multiplication('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('multiplication webgl2', () => {\n  multiplication('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('multiplication headlessgl', () => {\n  multiplication('headlessgl');\n});\n\ntest('multiplication cpu', () => {\n  multiplication('cpu');\n});\n\nfunction exponential(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 3 ** 2;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3 ** 2);\n  gpu.destroy();\n}\n\ntest('exponential auto', () => {\n  exponential();\n});\n\n(GPU.isGPUSupported ? test : skip)('exponential gpu', () => {\n  exponential('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('exponential webgl', () => {\n  exponential('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('exponential webgl2', () => {\n  exponential('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('exponential headlessgl', () => {\n  exponential('headlessgl');\n});\n\ntest('exponential cpu', () => {\n  exponential('cpu');\n});\n\nfunction division(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 3 / 2;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3 / 2);\n  gpu.destroy();\n}\n\ntest('division auto', () => {\n  division();\n});\n\n(GPU.isGPUSupported ? test : skip)('division gpu', () => {\n  division('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('division webgl', () => {\n  division('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('division webgl2', () => {\n  division('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('division headlessgl', () => {\n  division('headlessgl');\n});\n\ntest('division cpu', () => {\n  division('cpu');\n});\n\nfunction modulus(mode) {\n  const gpu = new GPU({ mode });\n  const kernel1 = gpu.createKernel(function() {\n    return 3 % 2;\n  }, { output: [1] });\n  assert.equal(kernel1()[0], 3 % 2);\n\n  const kernel2 = gpu.createKernel(function() {\n    return -126 % 63.5;\n  }, { output: [1] });\n  assert.equal(kernel2()[0], -126 % 63.5);\n\n  const kernel3 = gpu.createKernel(function() {\n    return 126 % -63.5;\n  }, { output: [1] });\n  assert.equal(kernel3()[0], 126 % -63.5);\n\n  gpu.destroy();\n}\n\ntest('modulus auto', () => {\n  modulus();\n});\n\n(GPU.isGPUSupported ? test : skip)('modulus gpu', () => {\n  modulus('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('modulus webgl', () => {\n  modulus('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('modulus webgl2', () => {\n  modulus('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('modulus headlessgl', () => {\n  modulus('headlessgl');\n});\n\ntest('modulus cpu', () => {\n  modulus('cpu');\n});\n\nfunction modulusVariable(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return 91 % 7;\n  }, { output: [1] });\n  assert.equal(kernel(7)[0], 0);\n  gpu.destroy();\n}\n\ntest('modulus variable auto', () => {\n  modulusVariable();\n});\n\n\nfunction increment(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    i++;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 3;\n  i++;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('increment auto', () => {\n  increment();\n});\n\n(GPU.isGPUSupported ? test : skip)('increment gpu', () => {\n  increment('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('increment webgl', () => {\n  increment('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('increment webgl2', () => {\n  increment('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('increment headlessgl', () => {\n  increment('headlessgl');\n});\n\ntest('increment cpu', () => {\n  increment('cpu');\n});\n\nfunction incrementEarlyReturn(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    return i++;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 3;\n  assert.equal(result[0], i++);\n  gpu.destroy();\n}\n\ntest('increment early return auto', () => {\n  incrementEarlyReturn();\n});\n\n(GPU.isGPUSupported ? test : skip)('increment early return gpu', () => {\n  incrementEarlyReturn('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('increment early return webgl', () => {\n  incrementEarlyReturn('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('increment early return webgl2', () => {\n  incrementEarlyReturn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('increment early return headlessgl', () => {\n  incrementEarlyReturn('headlessgl');\n});\n\ntest('increment early return cpu', () => {\n  incrementEarlyReturn('cpu');\n});\n\nfunction decrement(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    i--;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 3;\n  i--;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('decrement auto', () => {\n  decrement();\n});\n\n(GPU.isGPUSupported ? test : skip)('decrement gpu', () => {\n  decrement('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('decrement webgl', () => {\n  decrement('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('decrement webgl2', () => {\n  decrement('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('decrement headlessgl', () => {\n  decrement('headlessgl');\n});\n\ntest('decrement cpu', () => {\n  decrement('cpu');\n});\n\nfunction decrementEarlyReturn(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    return i--;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 3;\n  assert.equal(result[0], i--);\n  gpu.destroy();\n}\n\ntest('decrement early return auto', () => {\n  decrementEarlyReturn();\n});\n\n(GPU.isGPUSupported ? test : skip)('decrement early return gpu', () => {\n  decrementEarlyReturn('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('decrement early return webgl', () => {\n  decrementEarlyReturn('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('decrement early return webgl2', () => {\n  decrementEarlyReturn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('decrement early return headlessgl', () => {\n  decrementEarlyReturn('headlessgl');\n});\n\ntest('decrement early return cpu', () => {\n  decrementEarlyReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/assignment-operators.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: assignment operators');\n\nfunction equal(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  assert.equal(result[0], 3);\n  gpu.destroy();\n}\n\ntest('equal auto', () => {\n  equal();\n});\n\n(GPU.isGPUSupported ? test : skip)('equal gpu', () => {\n  equal('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('equal webgl', () => {\n  equal('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('equal webgl2', () => {\n  equal('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('equal headlessgl', () => {\n  equal('headlessgl');\n});\n\ntest('equal cpu', () => {\n  equal('cpu');\n});\n\nfunction plusEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 3;\n    i += 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 3;\n  i += 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('plus equal auto', () => {\n  plusEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('plus equal gpu', () => {\n  plusEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('plus equal webgl', () => {\n  plusEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('plus equal webgl2', () => {\n  plusEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('plus equal headlessgl', () => {\n  plusEqual('headlessgl');\n});\n\ntest('plus equal cpu', () => {\n  plusEqual('cpu');\n});\n\nfunction minusEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 6;\n    i -= 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 6;\n  i -= 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('minus equal auto', () => {\n  minusEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('minus equal gpu', () => {\n  minusEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('minus equal webgl', () => {\n  minusEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('minus equal webgl2', () => {\n  minusEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('minus equal headlessgl', () => {\n  minusEqual('headlessgl');\n});\n\ntest('minus equal cpu', () => {\n  minusEqual('cpu');\n});\n\nfunction multiplyEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 6;\n    i *= 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 6;\n  i *= 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('multiply equal auto', () => {\n  multiplyEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('multiply equal gpu', () => {\n  multiplyEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('multiply equal webgl', () => {\n  multiplyEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('multiply equal webgl2', () => {\n  multiplyEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('multiply equal headlessgl', () => {\n  multiplyEqual('headlessgl');\n});\n\ntest('multiply equal cpu', () => {\n  multiplyEqual('cpu');\n});\n\nfunction divideEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 6;\n    i /= 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 6;\n  i /= 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('divide equal auto', () => {\n  divideEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('divide equal gpu', () => {\n  divideEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('divide equal webgl', () => {\n  divideEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('divide equal webgl2', () => {\n  divideEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('divide equal headlessgl', () => {\n  divideEqual('headlessgl');\n});\n\ntest('divide equal cpu', () => {\n  divideEqual('cpu');\n});\n\n\nfunction modulusEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 2;\n    i %= 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 2;\n  i %= 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('modulus equal auto', () => {\n  modulusEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('modulus equal gpu', () => {\n  modulusEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('modulus equal webgl', () => {\n  modulusEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('modulus equal webgl2', () => {\n  divideEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('modulus equal headlessgl', () => {\n  modulusEqual('headlessgl');\n});\n\ntest('modulus equal cpu', () => {\n  modulusEqual('cpu');\n});\n\nfunction exponentialEqual(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    let i = 2;\n    i **= 3;\n    return i;\n  }, { output: [1] });\n  const result = kernel();\n  let i = 2;\n  i **= 3;\n  assert.equal(result[0], i);\n  gpu.destroy();\n}\n\ntest('exponential equal auto', () => {\n  exponentialEqual();\n});\n\n(GPU.isGPUSupported ? test : skip)('exponential equal gpu', () => {\n  exponentialEqual('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('exponential equal webgl', () => {\n  exponentialEqual('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('exponential equal webgl2', () => {\n  exponentialEqual('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('exponential equal headlessgl', () => {\n  exponentialEqual('headlessgl');\n});\n\ntest('exponential equal cpu', () => {\n  exponentialEqual('cpu');\n});\n"
  },
  {
    "path": "test/features/basic-math.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: basic math');\n\nfunction sumABTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    return (a[this.thread.x] + b[this.thread.x]);\n  }, {\n    output : [6],\n    mode : mode,\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [5, 7, 9, 6, 8, 10];\n\n  for(let i = 0; i < exp.length; ++i) {\n    assert.equal(res[i], exp[i], 'Result arr idx: '+i);\n  }\n  gpu.destroy();\n}\n\ntest('sumAB auto', () => {\n  sumABTest(null);\n});\n\ntest('sumAB gpu', () => {\n  sumABTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('sumAB webgl', () => {\n  sumABTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('sumAB webgl2', () => {\n  sumABTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('sumAB headlessgl', () => {\n  sumABTest('headlessgl');\n});\n\ntest('sumAB cpu', () => {\n  sumABTest('cpu');\n});\n\n\nfunction multABTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    sum += a[this.thread.y][0] * b[0][this.thread.x];\n    sum += a[this.thread.y][1] * b[1][this.thread.x];\n    sum += a[this.thread.y][2] * b[2][this.thread.x];\n    return sum;\n  }, {\n    output : [3, 3]\n  });\n\n  assert.ok(f !== null, 'function generated test');\n  assert.deepEqual(f(\n    [\n      [1, 2, 3],\n      [4, 5, 6],\n      [7, 8, 9]\n    ],\n    [\n      [1, 2, 3],\n      [4, 5, 6],\n      [7, 8, 9]\n    ]).map((object) => { return Array.from(object); }),\n    [\n      [30, 36, 42],\n      [66, 81, 96],\n      [102, 126, 150]\n    ],\n    'basic mult function test'\n  );\n  gpu.destroy();\n}\n\ntest('multAB auto', () => {\n  multABTest(null);\n});\ntest('multAB gpu', () => {\n  multABTest('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('multAB webgl', () => {\n  multABTest('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('multAB webgl2', () => {\n  multABTest('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('multAB headlessgl', () => {\n  multABTest('headlessgl');\n});\ntest('multAB cpu', () => {\n  multABTest('cpu');\n});\n\n"
  },
  {
    "path": "test/features/bitwise-operators.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('feature: bitwise operators');\n\nfunction testBitwiseAndSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 & v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i & j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise AND single precision auto', () => {\n  testBitwiseAndSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise AND single precision gpu', () => {\n  testBitwiseAndSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise AND single precision webgl', () => {\n  testBitwiseAndSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise AND single precision webgl2', () => {\n  testBitwiseAndSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise AND single precision headlessgl', () => {\n  testBitwiseAndSinglePrecision('headlessgl');\n});\n\ntest('bitwise AND single precision cpu', () => {\n  testBitwiseAndSinglePrecision('cpu');\n});\n\nfunction testBitwiseAndUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 & v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i & j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise AND unsigned precision auto', () => {\n  testBitwiseAndUnsignedPrecision();\n});\n\ntest('bitwise AND unsigned precision gpu', () => {\n  testBitwiseAndUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise AND unsigned precision webgl', () => {\n  testBitwiseAndUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise AND unsigned precision webgl2', () => {\n  testBitwiseAndUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise AND unsigned precision headlessgl', () => {\n  testBitwiseAndUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise AND unsigned precision cpu', () => {\n  testBitwiseAndUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseOrSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 | v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i | j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise OR single precision auto', () => {\n  testBitwiseOrSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise OR single precision gpu', () => {\n  testBitwiseOrSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise OR single precision webgl', () => {\n  testBitwiseOrSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise OR single precision webgl2', () => {\n  testBitwiseOrSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported? test : skip)('bitwise OR single precision headlessgl', () => {\n  testBitwiseOrSinglePrecision('headlessgl');\n});\n\ntest('bitwise OR single precision cpu', () => {\n  testBitwiseOrSinglePrecision('cpu');\n});\n\nfunction testBitwiseOrUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 | v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i | j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise OR unsigned precision auto', () => {\n  testBitwiseOrUnsignedPrecision();\n});\n\ntest('bitwise OR unsigned precision gpu', () => {\n  testBitwiseOrUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise OR unsigned precision webgl', () => {\n  testBitwiseOrUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise OR unsigned precision webgl2', () => {\n  testBitwiseOrUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise OR unsigned precision headlessgl', () => {\n  testBitwiseOrUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise OR unsigned precision cpu', () => {\n  testBitwiseOrUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseXORSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 ^ v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i ^ j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise XOR single precision auto', () => {\n  testBitwiseXORSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise XOR single precision gpu', () => {\n  testBitwiseXORSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise XOR single precision webgl', () => {\n  testBitwiseXORSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise XOR single precision webgl2', () => {\n  testBitwiseXORSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise XOR single precision headlessgl', () => {\n  testBitwiseXORSinglePrecision('headlessgl');\n});\n\ntest('bitwise XOR single precision cpu', () => {\n  testBitwiseXORSinglePrecision('cpu');\n});\n\nfunction testBitwiseXORUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 ^ v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i ^ j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise XOR unsigned precision auto', () => {\n  testBitwiseXORUnsignedPrecision();\n});\n\ntest('bitwise XOR unsigned precision gpu', () => {\n  testBitwiseXORUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise XOR unsigned precision webgl', () => {\n  testBitwiseXORUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise XOR unsigned precision webgl2', () => {\n  testBitwiseXORUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise XOR unsigned precision headlessgl', () => {\n  testBitwiseXORUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise XOR unsigned precision cpu', () => {\n  testBitwiseXORUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseNotSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1) {\n    return ~v1;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    assert.equal(kernel(i)[0], ~i);\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise NOT single precision auto', () => {\n  testBitwiseNotSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise NOT single precision gpu', () => {\n  testBitwiseNotSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise NOT single precision webgl', () => {\n  testBitwiseNotSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise NOT single precision webgl2', () => {\n  testBitwiseNotSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise NOT single precision headlessgl', () => {\n  testBitwiseNotSinglePrecision('headlessgl');\n});\n\ntest('bitwise NOT single precision cpu', () => {\n  testBitwiseNotSinglePrecision('cpu');\n});\n\nfunction testBitwiseNotUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1) {\n    return ~v1;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    assert.equal(kernel(i)[0], ~i);\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise NOT unsigned precision auto', () => {\n  testBitwiseNotUnsignedPrecision();\n});\n\ntest('bitwise NOT unsigned precision gpu', () => {\n  testBitwiseNotUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise NOT unsigned precision webgl', () => {\n  testBitwiseNotUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise NOT unsigned precision webgl2', () => {\n  testBitwiseNotUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise NOT unsigned precision headlessgl', () => {\n  testBitwiseNotUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise NOT unsigned precision cpu', () => {\n  testBitwiseNotUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseZeroFillLeftShiftSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 << v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i << j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill left shift single precision auto', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill left shift single precision gpu', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill left shift single precision webgl', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise zero fill left shift single precision webgl2', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill left shift single precision headlessgl', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision('headlessgl');\n});\n\ntest('bitwise zero fill left shift single precision cpu', () => {\n  testBitwiseZeroFillLeftShiftSinglePrecision('cpu');\n});\n\nfunction testBitwiseZeroFillLeftShiftUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 << v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i << j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise zero fill left shift unsigned precision auto', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision();\n});\n\ntest('bitwise zero fill left shift unsigned precision gpu', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise zero fill left shift unsigned precision webgl', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise zero fill left shift unsigned precision webgl2', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise zero fill left shift unsigned precision headlessgl', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise zero fill left shift unsigned precision cpu', () => {\n  testBitwiseZeroFillLeftShiftUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseSignedRightShiftSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 >> v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i >> j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise signed fill right shift single precision auto', () => {\n  testBitwiseSignedRightShiftSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise signed fill right shift single precision gpu', () => {\n  testBitwiseSignedRightShiftSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise signed fill right shift single precision webgl', () => {\n  testBitwiseSignedRightShiftSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise signed fill right shift single precision webgl2', () => {\n  testBitwiseSignedRightShiftSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise signed fill right shift single precision headlessgl', () => {\n  testBitwiseSignedRightShiftSinglePrecision('headlessgl');\n});\n\ntest('bitwise signed fill right shift single precision cpu', () => {\n  testBitwiseSignedRightShiftSinglePrecision('cpu');\n});\n\nfunction testBitwiseSignedRightShiftUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 >> v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i >> j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise signed fill right shift unsigned precision auto', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision();\n});\n\ntest('bitwise signed fill right shift unsigned precision gpu', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise signed fill right shift unsigned precision webgl', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise signed fill right shift unsigned precision webgl2', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise signed fill right shift unsigned precision headlessgl', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise signed fill right shift unsigned precision cpu', () => {\n  testBitwiseSignedRightShiftUnsignedPrecision('cpu');\n});\n\nfunction testBitwiseZeroFillRightShiftSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 >>> v2;\n  })\n    .setOutput([1])\n    .setPrecision('single');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i >>> j);\n    }\n  }\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill right shift single precision auto', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill right shift single precision gpu', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill right shift single precision webgl', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise zero fill right shift single precision webgl2', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('bitwise zero fill right shift single precision headlessgl', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision('headlessgl');\n});\n\ntest('bitwise zero fill right shift single precision cpu', () => {\n  testBitwiseZeroFillRightShiftSinglePrecision('cpu');\n});\n\nfunction testBitwiseZeroFillRightShiftUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return v1 >>> v2;\n  })\n    .setOutput([1])\n    .setPrecision('unsigned');\n\n  for (let i = 0; i < 10; i++) {\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(i, j)[0], i >>> j);\n    }\n  }\n\n  gpu.destroy();\n}\n\ntest('bitwise zero fill right shift unsigned precision auto', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision();\n});\n\ntest('bitwise zero fill right shift unsigned precision gpu', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('bitwise zero fill right shift unsigned precision webgl', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('bitwise zero fill right shift unsigned precision webgl2', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('bitwise zero fill right shift unsigned precision headlessgl', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision('headlessgl');\n});\n\ntest('bitwise zero fill right shift unsigned precision cpu', () => {\n  testBitwiseZeroFillRightShiftUnsignedPrecision('cpu');\n});\n"
  },
  {
    "path": "test/features/boolean-from-expression.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('feature: bitwise operators');\n\nfunction testBooleanFromExpression(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const result = 1 === 1 && 2 === 2;\n    return result ? 1 : 0;\n  }, { output: [1] });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testBooleanFromExpression();\n});\n\ntest('gpu', () => {\n  testBooleanFromExpression('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testBooleanFromExpression('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testBooleanFromExpression('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testBooleanFromExpression('headlessgl');\n});\n\ntest('cpu', () => {\n  testBooleanFromExpression('cpu');\n});"
  },
  {
    "path": "test/features/canvas.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\nconst { greenCanvas } = require('../browser-test-utils');\n\ndescribe('features: canvas argument');\nfunction canvasArgumentTest(mode) {\n  const gpu = new GPU({ mode });\n  const canvas = greenCanvas(mode, 1, 1);\n  const kernel = gpu.createKernel(function(canvas) {\n    const pixel = canvas[this.thread.y][this.thread.x];\n    return pixel[1];\n  }, {\n    output : [canvas.width, canvas.height]\n  });\n  const result = kernel(canvas);\n  assert.equal(result[0][0], 1);\n  gpu.destroy();\n}\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('auto', () => {\n  canvasArgumentTest();\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('gpu', () => {\n  canvasArgumentTest('gpu');\n});\n\n(GPU.isWebGLSupported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('webgl', () => {\n  canvasArgumentTest('webgl');\n});\n\n(GPU.isWebGL2Supported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('webgl2', () => {\n  canvasArgumentTest('webgl2');\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('cpu', () => {\n  canvasArgumentTest('cpu');\n});\n\nfunction canvasManuallyDefinedArgumentTest(mode) {\n  const gpu = new GPU({ mode });\n  const canvas = greenCanvas(mode, 1, 1);\n  const kernel = gpu.createKernel(function(canvas) {\n    const pixel = canvas[this.thread.y][this.thread.x];\n    return pixel[1];\n  }, {\n    output : [canvas.width, canvas.height],\n    argumentTypes: { canvas: 'HTMLCanvas' }\n  });\n  const result = kernel(canvas);\n  assert.equal(result[0][0], 1);\n  gpu.destroy();\n}\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('manually defined auto', () => {\n  canvasManuallyDefinedArgumentTest();\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('manually defined gpu', () => {\n  canvasManuallyDefinedArgumentTest('gpu');\n});\n\n(GPU.isWebGLSupported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('manually defined webgl', () => {\n  canvasManuallyDefinedArgumentTest('webgl');\n});\n\n(GPU.isWebGL2Supported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('manually defined webgl2', () => {\n  canvasManuallyDefinedArgumentTest('webgl2');\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('manually defined cpu', () => {\n  canvasManuallyDefinedArgumentTest('cpu');\n});"
  },
  {
    "path": "test/features/clear-textures.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: clear textures');\n\nfunction clearTexture(precision, mode) {\n  const gpu = new GPU({ mode });\n  function makeTexture() {\n    return (gpu.createKernel(function() {\n      return this.thread.x;\n    }, {\n      output: [5],\n      pipeline: true,\n      precision\n    }))();\n  }\n  const texture = makeTexture();\n  assert.deepEqual(texture.toArray(), new Float32Array([0,1,2,3,4]));\n  texture.clear();\n  const texture2 = makeTexture(); // put another texture in the way\n  assert.deepEqual(texture.toArray(), new Float32Array([0,0,0,0,0]));\n  assert.deepEqual(texture2.toArray(), new Float32Array([0,1,2,3,4]));\n  gpu.destroy();\n}\n\ntest('unsigned precision auto', () => {\n  clearTexture('unsigned');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision webgl', () => {\n  clearTexture('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision webgl2', () => {\n  clearTexture('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision headlessgl', () => {\n  clearTexture('unsigned', 'headlessgl');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision auto', () => {\n  clearTexture('single');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision webgl', () => {\n  clearTexture('single', 'webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision webgl2', () => {\n  clearTexture('single', 'webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision headlessgl', () => {\n  clearTexture('single', 'headlessgl');\n});\n\n\nfunction clearClonedTexture(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, { output: [1], pipeline: true, immutable: true });\n  const result = kernel();\n  assert.equal(result.toArray()[0], 1);\n  const result2 = result.clone();\n  const result3 = result2.clone();\n  assert.equal(result2.toArray()[0], 1);\n  assert.equal(result3.toArray()[0], 1);\n  result2.clear();\n  assert.equal(result2.toArray()[0], 0);\n  assert.equal(result3.toArray()[0], 1);\n  gpu.destroy();\n}\n\ntest('clear cloned texture auto', () => {\n  clearClonedTexture();\n});\n\ntest('clear cloned texture gpu', () => {\n  clearClonedTexture('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('clear cloned texture webgl', () => {\n  clearClonedTexture('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('clear cloned texture webgl2', () => {\n  clearClonedTexture('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('clear cloned texture headlessgl', () => {\n  clearClonedTexture('headlessgl');\n});"
  },
  {
    "path": "test/features/clone-textures.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: clone textures');\n\nfunction copy1DTexture(precision, mode) {\n  const gpu = new GPU({ mode });\n  function makeTexture() {\n    return (gpu.createKernel(function() {\n      return this.thread.x;\n    }, {\n      output: [5],\n      pipeline: true,\n      precision\n    }))();\n  }\n  const texture = makeTexture();\n  const clone = texture.clone();\n  assert.notEqual(texture, clone);\n  assert.equal(texture.texture, clone.texture);\n  assert.deepEqual(texture.toArray(), clone.toArray());\n  gpu.destroy();\n}\n\ntest('1D unsigned precision auto', () => {\n  copy1DTexture('unsigned');\n});\n\n(GPU.isWebGLSupported ? test : skip)('1D unsigned precision webgl', () => {\n  copy1DTexture('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('1D unsigned precision webgl2', () => {\n  copy1DTexture('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('1D unsigned precision headlessgl', () => {\n  copy1DTexture('unsigned', 'headlessgl');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('1D single precision auto', () => {\n  copy1DTexture('single');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('1D single precision webgl', () => {\n  copy1DTexture('single', 'webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('1D single precision webgl2', () => {\n  copy1DTexture('single', 'webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('1D single precision headlessgl', () => {\n  copy1DTexture('single', 'headlessgl');\n});\n\nfunction copy2DTexture(precision, mode) {\n  const gpu = new GPU({ mode });\n  function makeTexture() {\n    return (gpu.createKernel(function() {\n      return this.thread.x + (this.thread.y * this.output.x);\n    }, {\n      output: [5, 5],\n      pipeline: true,\n      precision\n    }))();\n  }\n  const texture = makeTexture();\n  const clone = texture.clone();\n  assert.notEqual(texture, clone);\n  assert.equal(texture.texture, clone.texture);\n  assert.deepEqual(texture.toArray(), clone.toArray());\n  gpu.destroy();\n}\n\ntest('2D unsigned precision auto', () => {\n  copy2DTexture('unsigned');\n});\n\n(GPU.isWebGLSupported ? test : skip)('2D unsigned precision webgl', () => {\n  copy2DTexture('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('2D unsigned precision webgl2', () => {\n  copy2DTexture('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('2D unsigned precision headlessgl', () => {\n  copy2DTexture('unsigned', 'headlessgl');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('2D single precision auto', () => {\n  copy2DTexture('single');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('2D single precision webgl', () => {\n  copy2DTexture('single', 'webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('2D single precision webgl2', () => {\n  copy2DTexture('single', 'webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('2D single precision headlessgl', () => {\n  copy2DTexture('single', 'headlessgl');\n});\n\nfunction copy3DTexture(precision, mode) {\n  const gpu = new GPU({ mode });\n  function makeTexture() {\n    return (gpu.createKernel(function() {\n      return this.thread.x + (this.thread.y * this.output.x) * (this.output.x * this.output.y * this.thread.z);\n    }, {\n      output: [5, 5, 5],\n      pipeline: true,\n      precision\n    }))();\n  }\n  const texture = makeTexture();\n  const clone = texture.clone();\n  assert.notEqual(texture, clone);\n  assert.equal(texture.texture, clone.texture);\n  assert.deepEqual(texture.toArray(), clone.toArray());\n  gpu.destroy();\n}\n\ntest('3D unsigned precision auto', () => {\n  copy3DTexture('unsigned');\n});\n\n(GPU.isWebGLSupported ? test : skip)('3D unsigned precision webgl', () => {\n  copy3DTexture('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('3D unsigned precision webgl2', () => {\n  copy3DTexture('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('3D unsigned precision headlessgl', () => {\n  copy3DTexture('unsigned', 'headlessgl');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('3D single precision auto', () => {\n  copy3DTexture('single');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('3D single precision webgl', () => {\n  copy3DTexture('single', 'webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('3D single precision webgl2', () => {\n  copy3DTexture('single', 'webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('3D single precision headlessgl', () => {\n  copy3DTexture('single', 'headlessgl');\n});\n"
  },
  {
    "path": "test/features/combine-kernels.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: combine kernels');\nfunction combineKernels(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel1 = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.x];\n  }, { output: [5] });\n\n  const kernel2 = gpu.createKernel(function(c, d) {\n    return c[this.thread.x] * d[this.thread.x];\n  }, { output: [5] });\n\n  const superKernel = gpu.combineKernels(kernel1, kernel2, function(array1, array2, array3) {\n    return kernel2(kernel1(array1, array2), array3);\n  });\n\n  const result = superKernel([1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]);\n  assert.deepEqual(Array.from(result), [2, 8, 18, 32, 50]);\n  gpu.destroy()\n}\n\ntest('combine kernel auto', () => {\n  combineKernels();\n});\n\ntest('combine kernel gpu', () => {\n  combineKernels('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('combine kernel webgl', () => {\n  combineKernels('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('combine kernel webgl2', () => {\n  combineKernels('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('combine kernel headlessgl', () => {\n  combineKernels('headlessgl');\n});\n\ntest('combine kernel cpu', () => {\n  combineKernels('cpu');\n});\n\n\nfunction combineKernelsSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel1 = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.x];\n  }, { output: [5], precision: 'single' });\n\n  const kernel2 = gpu.createKernel(function(c, d) {\n    return c[this.thread.x] * d[this.thread.x];\n  }, { output: [5], precision: 'single' });\n\n  const superKernel = gpu.combineKernels(kernel1, kernel2, function(array1, array2, array3) {\n    return kernel2(kernel1(array1, array2), array3);\n  });\n\n  const result = superKernel([1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]);\n  assert.deepEqual(Array.from(result), [2, 8, 18, 32, 50]);\n  gpu.destroy()\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('combine kernel single precision auto', () => {\n  combineKernelsSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('combine kernel single precision gpu', () => {\n  combineKernelsSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel single precision webgl', () => {\n  combineKernelsSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel single precision webgl2', () => {\n  combineKernelsSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel single precision headlessgl', () => {\n  combineKernelsSinglePrecision('headlessgl');\n});\n\ntest('combine kernel single precision cpu', () => {\n  combineKernelsSinglePrecision('cpu');\n});\n\n\nfunction combineKernelsOptimizeFloatMemory(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel1 = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.x];\n  }, {\n    output: [5],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n\n  const kernel2 = gpu.createKernel(function(c, d) {\n    return c[this.thread.x] * d[this.thread.x];\n  }, {\n    output: [5],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n\n  const superKernel = gpu.combineKernels(kernel1, kernel2, function(array1, array2, array3) {\n    return kernel2(kernel1(array1, array2), array3);\n  });\n\n  const result = superKernel([1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]);\n  assert.deepEqual(Array.from(result), [2, 8, 18, 32, 50]);\n  gpu.destroy()\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('combine kernel float textures auto', () => {\n  combineKernelsOptimizeFloatMemory();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('combine kernel float textures gpu', () => {\n  combineKernelsOptimizeFloatMemory('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel float textures webgl', () => {\n  combineKernelsOptimizeFloatMemory('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel float textures webgl2', () => {\n  combineKernelsOptimizeFloatMemory('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('combine kernel float textures headlessgl', () => {\n  combineKernelsOptimizeFloatMemory('headlessgl');\n});\n\ntest('combine kernel float textures cpu', () => {\n  combineKernelsOptimizeFloatMemory('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants array');\n\nfunction feature(mode) {\n  const gpu = new GPU({ mode });\n  const array = [200, 200];\n  const tryConst = gpu.createKernel(function() {\n    return this.constants.array[this.thread.x];\n  }, {\n    constants: { array },\n    output: [2]\n  });\n  const result = tryConst();\n  assert.deepEqual(Array.from(result), [200, 200], 'array constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  feature(null);\n});\n\ntest('gpu', () => {\n  feature('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  feature('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  feature('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  feature('headlessgl');\n});\n\ntest('arrayConstantTest cpu', () => {\n  feature('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-bool.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants bool');\n\nfunction boolTrueConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const bool = true;\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.bool ? 1 : 0;\n    },\n    {\n      constants: { bool },\n      output: [1]\n    },\n  );\n  const result = tryConst();\n  assert.equal(result[0], 1, 'bool constant passed test');\n  gpu.destroy();\n}\n\ntest('true auto', () => {\n  boolTrueConstantTest(null);\n});\n\ntest('true gpu', () => {\n  boolTrueConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('true webgl', () => {\n  boolTrueConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('true webgl2', () => {\n  boolTrueConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('true headlessgl', () => {\n  boolTrueConstantTest('headlessgl');\n});\n\ntest('true cpu', () => {\n  boolTrueConstantTest('cpu');\n});\n\n\nfunction boolFalseConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const bool = false;\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.bool ? 1 : 0;\n    },\n    {\n      constants: { bool },\n      output: [1]\n    },\n  );\n  const result = tryConst();\n  assert.equal(result[0], 0, 'bool constant passed test');\n  gpu.destroy();\n}\n\ntest('false auto', () => {\n  boolFalseConstantTest(null);\n});\n\ntest('false gpu', () => {\n  boolFalseConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('false webgl', () => {\n  boolFalseConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('false webgl2', () => {\n  boolFalseConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('false headlessgl', () => {\n  boolFalseConstantTest('headlessgl');\n});\n\ntest('false cpu', () => {\n  boolFalseConstantTest('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-canvas.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\nconst { greenCanvas } = require('../browser-test-utils');\n\ndescribe('features: constants canvas');\nfunction canvasConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const canvas = greenCanvas(mode, 1, 1);\n  const kernel = gpu.createKernel(\n    function() {\n      const pixel = this.constants.canvas[this.thread.y][this.thread.x];\n      return pixel.g;\n    },\n    {\n      constants: { canvas },\n      output: [1, 1],\n    }\n  );\n  const result = kernel();\n  const test = result[0][0] > 0;\n  assert.ok(test, 'image constant passed test');\n  gpu.destroy();\n}\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('auto', () => {\n  canvasConstantTest(null);\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('gpu', () => {\n  canvasConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('webgl', () => {\n  canvasConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported && typeof HTMLCanvasElement !== 'undefined' ? test : skip)('webgl2', () => {\n  canvasConstantTest('webgl2');\n});\n\n(typeof HTMLCanvasElement !== 'undefined' ? test : skip)('cpu', () => {\n  canvasConstantTest('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-float.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants float');\nfunction floatConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const float = 200.01;\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.float;\n    },\n    {\n      constants: { float },\n      output: [2]\n    },\n  );\n  const result = tryConst();\n  const match = new Float32Array([200.01, 200.01]);\n  const test = (\n    result[0].toFixed(1) === match[0].toFixed(1)\n    && result[1].toFixed(1) === match[1].toFixed(1)\n  );\n  assert.ok(test, 'float constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  floatConstantTest(null);\n});\n\ntest('gpu', () => {\n  floatConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  floatConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  floatConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  floatConstantTest('headlessgl');\n});\n\ntest('cpu', () => {\n  floatConstantTest('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-image-array.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, WebGLKernel } = require('../../src');\n\ndescribe('features: constants image array');\nfunction feature(mode, done) {\n  const gpu = new GPU({ mode });\n  const image = new Image();\n  const imageArray = [image, image];\n  function fn() {\n    const pixel1 = this.constants.imageArray[0][this.thread.y][this.thread.x];\n    const pixel2 = this.constants.imageArray[1][this.thread.y][this.thread.x];\n    let color = 0;\n    if (this.thread.z === 0) {\n      color = (pixel1.r + pixel2.r) / 2;\n    }\n    if (this.thread.z === 1) {\n      color = (pixel1.g + pixel2.g) / 2;\n    }\n    if (this.thread.z === 2) {\n      color = (pixel1.b + pixel2.b) / 2;\n    }\n    if (this.thread.z === 3) {\n      color = 1;\n    }\n    return Math.floor(255 * color);\n  }\n  const settings = {\n    constants: { imageArray },\n    output: [1, 1, 4]\n  };\n\n  if (mode === 'webgl' || gpu.Kernel === WebGLKernel) {\n    // make fail early in this exact scenario\n    gpu.createKernel(fn, settings)();\n  }\n\n  image.src = 'jellyfish-1.jpeg';\n  image.onload = () => {\n    settings[0] = image.width;\n    settings[1] = image.height;\n    const tryConst = gpu.createKernel(fn, settings);\n    const result = tryConst();\n    assert.ok(result[0][0][0] > 0, 'image array constant passed test');\n    gpu.destroy();\n    done();\n  };\n}\n\n(GPU.isGPUHTMLImageArraySupported && typeof Image !== 'undefined' ? test : skip)('auto', t => {\n  feature(null, t.async());\n});\n\n(GPU.isGPUHTMLImageArraySupported && typeof Image !== 'undefined' ? test : skip)('gpu', t => {\n  feature('gpu', t.async());\n});\n\n(GPU.isWebGLSupported && typeof Image !== 'undefined' ? test : skip)('webgl', t => {\n  assert.throws(() => {\n    feature('webgl')\n  }, 'imageArray are not compatible with webgl');\n});\n\n(GPU.isWebGL2Supported && typeof Image !== 'undefined' ? test : skip)('webgl2', t => {\n  feature('webgl2', t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('cpu', t => {\n  feature('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/constants-image.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants image');\nfunction imageConstantTest(mode, done) {\n  const gpu = new GPU({ mode });\n  const image = new Image();\n  image.src = 'jellyfish-1.jpeg';\n  image.onload = function() {\n    const width = image.width;\n    const height = image.height;\n    const tryConst = gpu.createKernel(\n      function() {\n        const pixel = this.constants.image[this.thread.y][this.thread.x];\n        let color = 0;\n        if (this.thread.z === 0) {\n          color = pixel.r;\n        }\n        if (this.thread.z === 1) {\n          color = pixel.g;\n        }\n        if (this.thread.z === 2) {\n          color = pixel.b;\n        }\n        return 255 * color;\n      },\n      {\n        constants: { image },\n        output: [width, height, 3],\n      }\n    );\n    const result = tryConst();\n    const test = result[0][0][0] > 0;\n    assert.ok(test, 'image constant passed test');\n    gpu.destroy();\n    done();\n  }\n}\n\n(typeof Image !== 'undefined' ? test : skip)('auto', t => {\n  imageConstantTest(null, t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('gpu', t => {\n  imageConstantTest('gpu', t.async());\n});\n\n(GPU.isWebGLSupported && typeof Image !== 'undefined' ? test : skip)('webgl', t => {\n  imageConstantTest('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported && typeof Image !== 'undefined' ? test : skip)('webgl2', t => {\n  imageConstantTest('webgl2', t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('cpu', t => {\n  imageConstantTest('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/constants-integer.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants integer');\n\nfunction integerConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const int = 200;\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.int;\n    },\n    {\n      constants: { int }\n    }\n  ).setOutput([2]);\n  const result = tryConst();\n  const match = new Float32Array([200, 200]);\n  const test = (result[0] === match[0] && result[1] === match[1]);\n  assert.ok(test, 'int constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  integerConstantTest(null);\n});\n\ntest('gpu', () => {\n  integerConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  integerConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  integerConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  integerConstantTest('headlessgl');\n});\n\ntest('cpu', () => {\n  integerConstantTest('cpu');\n});\n"
  },
  {
    "path": "test/features/constants-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: constants texture 1d');\nfunction test1D(mode) {\n  const gpu = new GPU({ mode });\n  const createTexture = gpu\n    .createKernel(function() {\n      return 200;\n    })\n    .setOutput([2])\n    .setPipeline(true);\n  const texture = createTexture();\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.texture[this.thread.x];\n    },\n    {\n      constants: { texture }\n    }\n  ).setOutput([2]);\n  const result = tryConst();\n  const expected = new Float32Array([200, 200]);\n  assert.deepEqual(result, expected, 'texture constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  test1D(null);\n});\n\ntest('gpu', () => {\n  test1D('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', function () {\n  test1D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', function () {\n  test1D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', function () {\n  test1D('headlessgl');\n});\n\ntest('cpu', () => {\n  test1D('cpu');\n});\n\n\n\ndescribe('features: constants texture 2d');\nfunction test2D(mode) {\n  const gpu = new GPU({ mode });\n  const createTexture = gpu\n    .createKernel(function() {\n      return 200;\n    })\n    .setOutput([2, 2])\n    .setPipeline(true);\n  const texture = createTexture();\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.texture[this.thread.y][this.thread.x];\n    },\n    {\n      constants: { texture }\n    }\n  ).setOutput([2, 2]);\n  const result = tryConst();\n  const expected = [new Float32Array([200, 200]), new Float32Array([200, 200])];\n  assert.deepEqual(result, expected, 'texture constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  test2D(null);\n});\n\ntest('gpu', () => {\n  test2D('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', function () {\n  test2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', function () {\n  test2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', function () {\n  test2D('headlessgl');\n});\n\ntest('cpu', () => {\n  test2D('cpu');\n});\n\n\ndescribe('features: constants texture 3d');\nfunction test3D(mode) {\n  const gpu = new GPU({ mode });\n  const createTexture = gpu\n    .createKernel(function() {\n      return 200;\n    })\n    .setOutput([2, 2, 2])\n    .setPipeline(true);\n  const texture = createTexture();\n  const tryConst = gpu.createKernel(\n    function() {\n      return this.constants.texture[this.thread.z][this.thread.y][this.thread.x];\n    },\n    {\n      constants: { texture }\n    }\n  ).setOutput([2, 2, 2]);\n  const result = tryConst();\n  const expected = [[new Float32Array([200, 200]), new Float32Array([200, 200])],[new Float32Array([200, 200]), new Float32Array([200, 200])]];\n  assert.deepEqual(result, expected, 'texture constant passed test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  test3D(null);\n});\n\ntest('gpu', () => {\n  test3D('cpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', function () {\n  test3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', function () {\n  test3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', function () {\n  test3D('headlessgl');\n});\n\ntest('cpu', () => {\n  test3D('cpu');\n});\n"
  },
  {
    "path": "test/features/cpu-with-textures.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: CPU with Textures');\n\nfunction cpuWithTexturesNumberWithSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'single',\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(Array.from(texture.toArray()), [0, 1]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(Array.from(result), [0, 1]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(Array.from(cpuKernel(texture)), [0, 1]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with single precision auto', () => {\n  cpuWithTexturesNumberWithSinglePrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with single precision gpu', () => {\n  cpuWithTexturesNumberWithSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with single precision webgl', () => {\n  cpuWithTexturesNumberWithSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('number with single precision webgl2', () => {\n  cpuWithTexturesNumberWithSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with single precision headlessgl', () => {\n  cpuWithTexturesNumberWithSinglePrecision('headlessgl');\n});\n\nfunction cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(Array.from(texture.toArray()), [0, 1]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(Array.from(result), [0, 1]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(Array.from(cpuKernel(texture)), [0, 1]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('memory optimized number with single precision auto', () => {\n  cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('memory optimized number with single precision gpu', () => {\n  cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('memory optimized number with single precision webgl', () => {\n  cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('memory optimized number with single precision webgl2', () => {\n  cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('memory optimized number with single precision headlessgl', () => {\n  cpuWithTexturesMemoryOptimizedNumberWithSinglePrecision('headlessgl');\n});\n\nfunction cpuWithTexturesArray2WithSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.x];\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'single',\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(texture.toArray().map(value => Array.from(value)), [[0,0], [1,1]]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(result.map(value => Array.from(value)), [[0,0], [1,1]]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(cpuKernel(texture).map(value => Array.from(value)), [[0,0], [1,1]]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) with single precision auto', () => {\n  cpuWithTexturesArray2WithSinglePrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) with single precision gpu', () => {\n  cpuWithTexturesArray2WithSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) with single precision webgl', () => {\n  cpuWithTexturesArray2WithSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) with single precision webgl2', () => {\n  cpuWithTexturesArray2WithSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) with single precision headlessgl', () => {\n  cpuWithTexturesArray2WithSinglePrecision('headlessgl');\n});\n\nfunction cpuWithTexturesArray3WithSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.x, this.thread.x];\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'single',\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(texture.toArray().map(value => Array.from(value)), [[0,0,0], [1,1,1]]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(result.map(value => Array.from(value)), [[0,0,0], [1,1,1]]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(cpuKernel(texture).map(value => Array.from(value)), [[0,0,0], [1,1,1]]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) with single precision auto', () => {\n  cpuWithTexturesArray3WithSinglePrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) with single precision gpu', () => {\n  cpuWithTexturesArray3WithSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) with single precision webgl', () => {\n  cpuWithTexturesArray3WithSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) with single precision webgl2', () => {\n  cpuWithTexturesArray3WithSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) with single precision headlessgl', () => {\n  cpuWithTexturesArray3WithSinglePrecision('headlessgl');\n});\n\nfunction cpuWithTexturesArray4WithSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.x, this.thread.x, this.thread.x];\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'single',\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(texture.toArray().map(value => Array.from(value)), [[0,0,0,0], [1,1,1,1]]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(result.map(value => Array.from(value)), [[0,0,0,0], [1,1,1,1]]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(cpuKernel(texture).map(value => Array.from(value)), [[0,0,0,0], [1,1,1,1]]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) with single precision auto', () => {\n  cpuWithTexturesArray4WithSinglePrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) with single precision gpu', () => {\n  cpuWithTexturesArray4WithSinglePrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) with single precision webgl', () => {\n  cpuWithTexturesArray4WithSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) with single precision webgl2', () => {\n  cpuWithTexturesArray4WithSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) with single precision headlessgl', () => {\n  cpuWithTexturesArray4WithSinglePrecision('headlessgl');\n});\n\nfunction cpuWithTexturesNumberWithUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [2],\n    pipeline: true,\n    precision: 'unsigned',\n  });\n  const texture = kernel();\n  assert.ok(texture.toArray);\n  assert.deepEqual(Array.from(texture.toArray()), [0, 1]);\n  const cpu = new GPU({ mode: 'cpu' });\n  const cpuKernel = cpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, { output: [2] });\n  assert.notOk(cpuKernel.kernel.textureCache);\n  const result = cpuKernel(texture);\n  assert.ok(cpuKernel.kernel.textureCache);\n  assert.deepEqual(Array.from(result), [0, 1]);\n  let calledTwice = false;\n  texture.toArray = () => {\n    calledTwice = true;\n  };\n  assert.deepEqual(Array.from(cpuKernel(texture)), [0, 1]);\n  assert.equal(calledTwice, false);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with unsigned precision auto', () => {\n  cpuWithTexturesNumberWithUnsignedPrecision();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with unsigned precision gpu', () => {\n  cpuWithTexturesNumberWithUnsignedPrecision('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with unsigned precision webgl', () => {\n  cpuWithTexturesNumberWithUnsignedPrecision('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('number with unsigned precision webgl2', () => {\n  cpuWithTexturesNumberWithUnsignedPrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('number with unsigned precision headlessgl', () => {\n  cpuWithTexturesNumberWithUnsignedPrecision('headlessgl');\n});\n"
  },
  {
    "path": "test/features/create-kernel-map.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, alias } = require('../../src');\n\ndescribe('features: create kernel map');\nfunction createPropertyKernels(gpu, output) {\n  function divide(d1, d2) {\n    return d1 / d2;\n  }\n  const adder = alias('adder', function add(a1, a2) {\n    return a1 + a2;\n  });\n  return gpu.createKernelMap({\n    addResult: adder,\n    divideResult: divide\n  }, function (k1, k2, k3) {\n    return divide(adder(k1[this.thread.x], k2[this.thread.x]), k3[this.thread.x]);\n  }).setOutput(output);\n}\n\nfunction createArrayKernels(gpu, output) {\n  function add(a1, a2) {\n    return a1 + a2;\n  }\n  function divide(d1, d2) {\n    return d1 / d2;\n  }\n  return gpu.createKernelMap([\n    add, divide\n  ], function (k1, k2, k3) {\n    return divide(add(k1[this.thread.x], k2[this.thread.x]), k3[this.thread.x]);\n  }).setOutput(output)\n}\n\n\nfunction createKernel(gpu, output) {\n  return gpu.createKernel(function (a) {\n    return a[this.thread.x];\n  }).setOutput(output);\n}\n\nfunction createKernelMapObject1Dimension1Length(mode) {\n  const gpu = new GPU({ mode });\n  const superKernel = createPropertyKernels(gpu, [1]);\n  const kernel = createKernel(gpu, [1]);\n  const output = superKernel([2], [2], [0.5]);\n  const result = Array.from(output.result);\n  const addResult = Array.from(kernel(output.addResult));\n  const divideResult = Array.from(kernel(output.divideResult));\n  assert.deepEqual(result, [8]);\n  assert.deepEqual(addResult, [4]);\n  assert.deepEqual(divideResult, [8]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 1 length auto', () => {\n  createKernelMapObject1Dimension1Length();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 1 length gpu', () => {\n  createKernelMapObject1Dimension1Length('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap object 1 dimension 1 length webgl', () => {\n  createKernelMapObject1Dimension1Length('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap object 1 dimension 1 length webgl2', () => {\n  createKernelMapObject1Dimension1Length('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 1 length headlessgl', () => {\n  createKernelMapObject1Dimension1Length('headlessgl');\n});\n\ntest('createKernelMap object 1 dimension 1 length cpu', () => {\n  createKernelMapObject1Dimension1Length('cpu');\n});\n\n\nfunction createKernelMapArray1Dimension1Length(mode) {\n  const gpu = new GPU({ mode });\n  const superKernel = createArrayKernels(gpu, [1]);\n  const kernel = createKernel(gpu, [1]);\n  const output = superKernel([2], [2], [0.5]);\n  const result = Array.from(output.result);\n  const addResult = Array.from(kernel(output[0]));\n  const divideResult = Array.from(kernel(output[1]));\n  assert.deepEqual(result, [8]);\n  assert.deepEqual(addResult, [4]);\n  assert.deepEqual(divideResult, [8]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap array 1 dimension 1 length auto', () => {\n  createKernelMapArray1Dimension1Length();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap array 1 dimension 1 length gpu', () => {\n  createKernelMapArray1Dimension1Length('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap array 1 dimension 1 length webgl', () => {\n  createKernelMapArray1Dimension1Length('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap array 1 dimension 1 length webgl2', () => {\n  createKernelMapArray1Dimension1Length('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap array 1 dimension 1 length headlessgl', () => {\n  createKernelMapArray1Dimension1Length('headlessgl');\n});\n\ntest('createKernelMap array 1 dimension 1 length cpu', () => {\n  createKernelMapArray1Dimension1Length('cpu');\n});\n\n\nfunction createKernelMapObject1Dimension5Length(mode) {\n  const gpu = new GPU({ mode });\n  const superKernel = createPropertyKernels(gpu, [5]);\n  const kernel = createKernel(gpu, [5]);\n  const output = superKernel([1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]);\n  const result = Array.from(output.result);\n  const addResult = Array.from(kernel(output.addResult));\n  const divideResult = Array.from(kernel(output.divideResult));\n  assert.deepEqual(result, [2, 2, 2, 2, 2]);\n  assert.deepEqual(addResult, [2, 4, 6, 8, 10]);\n  assert.deepEqual(divideResult, [2, 2, 2, 2, 2]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 5 length auto', () => {\n  createKernelMapObject1Dimension5Length();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 5 length gpu', () => {\n  createKernelMapObject1Dimension5Length('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap object 1 dimension 5 length webgl', () => {\n  createKernelMapObject1Dimension5Length('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap object 1 dimension 5 length webgl2', () => {\n  createKernelMapObject1Dimension5Length('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap object 1 dimension 5 length headlessgl', () => {\n  createKernelMapObject1Dimension5Length('headlessgl');\n});\n\ntest('createKernelMap object 1 dimension 5 length cpu', () => {\n  createKernelMapObject1Dimension5Length('cpu');\n});\n\n\nfunction createKernelMapArrayAuto(mode) {\n  const gpu = new GPU({mode});\n  const superKernel = createArrayKernels(gpu, [5]);\n  const kernel = createKernel(gpu, [5]);\n  const output = superKernel([1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]);\n  const result = Array.from(output.result);\n  const addResult = Array.from(kernel(output[0]));\n  const divideResult = Array.from(kernel(output[1]));\n  assert.deepEqual(result, [2, 2, 2, 2, 2]);\n  assert.deepEqual(addResult, [2, 4, 6, 8, 10]);\n  assert.deepEqual(divideResult, [2, 2, 2, 2, 2]);\n  gpu.destroy();\n}\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap array auto', () => {\n  createKernelMapArrayAuto();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap array gpu', () => {\n  createKernelMapArrayAuto('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap array webgl', () => {\n  createKernelMapArrayAuto('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap array webgl2', () => {\n  createKernelMapArrayAuto('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap array headlessgl', () => {\n  createKernelMapArrayAuto('headlessgl');\n});\n\ntest('createKernelMap array cpu', () => {\n  createKernelMapArrayAuto('cpu');\n});\n\nfunction createKernelMap3DAuto(mode) {\n  const gpu = new GPU({ mode });\n  function saveTarget(value) {\n    return value;\n  }\n  const kernel = gpu.createKernelMap({\n    target: saveTarget\n  }, function(value) {\n    return saveTarget(value);\n  }).setOutput([3,3,3]);\n  const result = kernel(1);\n  const target = createKernel(gpu, [3,3,3])(result.target);\n  assert.equal(result.result.length, 3);\n  assert.equal(result.result[0].length, 3);\n  assert.equal(result.result[0][0].length, 3);\n\n  assert.equal(target.length, 3);\n  assert.equal(target[0].length, 3);\n  assert.equal(target[0][0].length, 3);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap 3d auto', () => {\n  createKernelMap3DAuto();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('createKernelMap 3d gpu', () => {\n  createKernelMap3DAuto('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap 3d webgl', () => {\n  createKernelMap3DAuto('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap 3d webgl2', () => {\n  createKernelMap3DAuto('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('createKernelMap 3d headlessgl', () => {\n  createKernelMap3DAuto('headlessgl');\n});\n\ntest('createKernelMap 3d cpu', () => {\n  createKernelMap3DAuto('cpu');\n});\n\nfunction createKernelMapArray2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap(\n    {\n      mapFunc: function mapFunc(mapFuncVal) {\n        return mapFuncVal;\n      }\n    },\n    function main() {\n      const mapFuncVal = [1, 2];\n      mapFunc(mapFuncVal);\n      const returnValue = [3, 4];\n      return returnValue;\n    },\n    {\n      output: [1],\n      returnType: 'Array(2)',\n    }\n  );\n  const { result, mapFunc } = kernel();\n  assert.deepEqual(Array.from(mapFunc[0]), [1, 2]);\n  assert.deepEqual(Array.from(result[0]), [3, 4]);\n  gpu.destroy();\n}\n\ntest('createKernelMap Array(2) auto', () => {\n  createKernelMapArray2();\n});\n\ntest('createKernelMap Array(2) gpu', () => {\n  createKernelMapArray2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap Array(2) webgl', () => {\n  createKernelMapArray2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap Array(2) webgl2', () => {\n  createKernelMapArray2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('createKernelMap Array(2) headlessgl', () => {\n  createKernelMapArray2('headlessgl');\n});\n\ntest('createKernelMap Array(2) cpu', () => {\n  createKernelMapArray2('cpu');\n});\n\nfunction createKernelMapArray3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap(\n    {\n      mapFunc: function mapFunc(mapFuncVal) {\n        return mapFuncVal;\n      }\n    },\n    function main() {\n      const mapFuncVal = [1, 2, 3];\n      mapFunc(mapFuncVal);\n      const returnValue = [4, 5, 6];\n      return returnValue;\n    },\n    {\n      output: [1],\n      returnType: 'Array(3)',\n    }\n  );\n  const { result, mapFunc } = kernel();\n  assert.deepEqual(Array.from(mapFunc[0]), [1, 2, 3]);\n  assert.deepEqual(Array.from(result[0]), [4, 5, 6]);\n  gpu.destroy();\n}\n\ntest('createKernelMap Array(3) auto', () => {\n  createKernelMapArray3();\n});\n\ntest('createKernelMap Array(3) gpu', () => {\n  createKernelMapArray3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap Array(3) webgl', () => {\n  createKernelMapArray3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap Array(3) webgl2', () => {\n  createKernelMapArray3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('createKernelMap Array(3) headlessgl', () => {\n  createKernelMapArray3('headlessgl');\n});\n\ntest('createKernelMap Array(3) cpu', () => {\n  createKernelMapArray3('cpu');\n});\n\nfunction createKernelMapArray4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap(\n    {\n      mapFunc: function mapFunc(mapFuncVal) {\n        return mapFuncVal;\n      }\n    },\n    function main() {\n      const mapFuncVal = [1, 2, 3, 4];\n      mapFunc(mapFuncVal);\n      const returnValue = [5, 6, 7, 8];\n      return returnValue;\n    },\n    {\n      output: [1],\n      returnType: 'Array(4)',\n    }\n  );\n  const { result, mapFunc } = kernel();\n  assert.deepEqual(Array.from(mapFunc[0]), [1, 2, 3, 4]);\n  assert.deepEqual(Array.from(result[0]), [5, 6, 7, 8]);\n  gpu.destroy();\n}\n\ntest('createKernelMap Array(4) auto', () => {\n  createKernelMapArray4();\n});\n\ntest('createKernelMap Array(4) gpu', () => {\n  createKernelMapArray4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('createKernelMap Array(4) webgl', () => {\n  createKernelMapArray4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap Array(4) webgl2', () => {\n  createKernelMapArray4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('createKernelMap Array(4) headlessgl', () => {\n  createKernelMapArray4('headlessgl');\n});\n\ntest('createKernelMap Array(4) cpu', () => {\n  createKernelMapArray4('cpu');\n});"
  },
  {
    "path": "test/features/demo.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: demo');\n\nfunction demo(mode) {\n  const matrixSize = 6;\n  let a = new Array(matrixSize * matrixSize);\n  let b = new Array(matrixSize * matrixSize);\n  a = splitArray(fillArrayRandom(a), matrixSize);\n  b = splitArray(fillArrayRandom(b), matrixSize);\n  function fillArrayRandom(array) {\n    for(let i = 0; i < array.length; i++) {\n      array[i] = Math.random();\n    }\n    return array;\n  }\n\n  function splitArray(array, part) {\n    const result = [];\n    for(let i = 0; i < array.length; i += part) {\n      result.push(array.slice(i, i + part));\n    }\n    return result;\n  }\n  const gpu = new GPU({ mode });\n  const multiplyMatrix = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < 6; i++) {\n      sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n  })\n    .setOutput([6, 6]);\n\n  assert.ok( multiplyMatrix !== null, \"function generated test\");\n  assert.equal(multiplyMatrix(a, b).length, 6, \"basic return function test\");\n  gpu.destroy();\n}\n\ntest(\"auto\", () => {\n  demo();\n});\n\ntest(\"gpu\", () => {\n  demo('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"webgl\", function () {\n  demo('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"webgl2\", function () {\n  demo('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"headlessgl\", function () {\n  demo('headlessgl');\n});\n\ntest(\"cpu\", () => {\n  demo('cpu');\n});\n"
  },
  {
    "path": "test/features/destroy.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, WebGLKernel } = require('../../src');\nconst sinon = require('sinon');\n\ndescribe('features: destroy');\n\nfunction testWithoutDestroyContext(done, mode) {\n  const gpu = new GPU({ mode });\n  const destroyKernel = sinon.spy();\n  gpu.kernels.push({\n    kernel: {\n      constructor: {\n        destroyContext: null\n      }\n    },\n    destroy: destroyKernel\n  });\n  gpu.destroy();\n  gpu.destroy()\n    .then(() => {\n      assert.equal(destroyKernel.callCount, 2);\n      assert.ok(true);\n      done();\n    });\n}\n\ntest('without destroy context', (t) => {\n  const done = t.async();\n  testWithoutDestroyContext(done);\n});\n\nfunction testWithDestroyContext(done, mode) {\n  const gpu = new GPU({ mode });\n  const destroyKernel = sinon.spy();\n  const destroyContextSpy = sinon.spy();\n  gpu.kernels.push({\n    kernel: {\n      constructor: {\n        destroyContext: destroyContextSpy\n      }\n    },\n    destroy: destroyKernel\n  });\n  gpu.destroy();\n  gpu.destroy()\n    .then(() => {\n      assert.equal(destroyKernel.callCount, 2);\n      assert.equal(destroyContextSpy.callCount, 2);\n      assert.ok(true);\n      done();\n    });\n}\n\ntest('with destroy context', (t) => {\n  const done = t.async();\n  testWithDestroyContext(done);\n});\n\n\nfunction testTexturesAreDestroyed(done, mode) {\n  const mockTexture1 = {};\n  const mockTexture2 = {};\n  const mockTexture3 = {};\n  const deleteTextureMock = sinon.spy();\n  const mockContext = {\n    deleteTexture: deleteTextureMock,\n  };\n  const mockKernelInstance = {\n    textureCache: [mockTexture1, mockTexture2, mockTexture3],\n    context: mockContext,\n    destroyExtensions: () => {},\n  };\n  mockKernelInstance.destroy = WebGLKernel.prototype.destroy.bind(mockKernelInstance);\n  GPU.prototype.destroy.call({ kernels: [mockKernelInstance] })\n    .then(() => {\n      assert.equal(deleteTextureMock.callCount, 3);\n      assert.ok(true);\n      done();\n    });\n}\n\ntest('textures are destroyed', (t) => {\n  const done = t.async();\n  testTexturesAreDestroyed(done);\n});\n\nfunction testKernelTextureIsDeleted(done) {\n  const webGLTexture = {};\n  const mockTextureDelete = sinon.spy();\n  const kernelTexture = {\n    texture: webGLTexture,\n    delete: mockTextureDelete,\n  };\n  const mockContext = {};\n  const mockKernelInstance = {\n    texture: kernelTexture,\n    textureCache: [],\n    context: mockContext,\n    destroyExtensions: () => {},\n  };\n  mockKernelInstance.destroy = WebGLKernel.prototype.destroy.bind(mockKernelInstance);\n  GPU.prototype.destroy.call({ kernels: [mockKernelInstance] })\n    .then(() => {\n      assert.equal(mockTextureDelete.callCount, 1);\n      assert.ok(true);\n      done();\n    });\n}\n\ntest('kernel.texture is deleted', (t) => {\n  const done = t.async();\n  testKernelTextureIsDeleted(done);\n});\n\nfunction testKernelMappedTexturesAreDeleted(done) {\n  const mockGPU = {\n    kernels: []\n  };\n  const webGLTexture1 = {};\n  const mockTextureDelete1 = sinon.spy();\n  const kernelTexture1 = {\n    texture: webGLTexture1,\n    delete: mockTextureDelete1,\n  };\n  const webGLTexture2 = {};\n  const mockTextureDelete2 = sinon.spy();\n  const kernelTexture2 = {\n    texture: webGLTexture2,\n    delete: mockTextureDelete2,\n  };\n  const mockContext = {};\n  const mockKernelInstance = {\n    gpu: mockGPU,\n    mappedTextures: [kernelTexture1, kernelTexture2],\n    textureCache: [],\n    context: mockContext,\n    destroyExtensions: () => {},\n  };\n  mockGPU.kernels.push(mockKernelInstance);\n  mockKernelInstance.destroy = WebGLKernel.prototype.destroy.bind(mockKernelInstance);\n  GPU.prototype.destroy.call({ kernels: [mockKernelInstance] })\n    .then(() => {\n      assert.equal(mockTextureDelete1.callCount, 1);\n      assert.equal(mockTextureDelete2.callCount, 1);\n      assert.equal(mockGPU.kernels.length, 0);\n      assert.ok(true);\n      done();\n    })\n    .catch((e) => {\n      console.error(e);\n    });\n}\n\ntest('kernel.mappedTextures are deleted', (t) => {\n  const done = t.async();\n  testKernelMappedTexturesAreDeleted(done);\n});\n\ntest('gpu.kernels is populated and removed by kernel', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  });\n  assert.equal(gpu.kernels.length, 1);\n  assert.equal(gpu.kernels.indexOf(kernel.kernel), 0);\n  kernel.destroy();\n  assert.equal(gpu.kernels.length, 0);\n  gpu.destroy();\n});\n\ntest('gpu.kernels is populated and removed by gpu', t => {\n  const done = t.async();\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  });\n  assert.equal(gpu.kernels.length, 1);\n  assert.equal(gpu.kernels.indexOf(kernel.kernel), 0);\n\n  gpu.destroy()\n    .then(() => {\n      assert.equal(gpu.kernels.length, 0);\n      done();\n    });\n});\n"
  },
  {
    "path": "test/features/destructured-assignment.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: destructured assignment');\n\nfunction testObject(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const { thread: { x, y } } = this;\n    return x + y;\n  }, { output: [2, 2] });\n  assert.deepEqual(kernel(), [new Float32Array([0, 1]), new Float32Array([1, 2])]);\n}\n\ntest('object auto', () => {\n  testObject();\n});\n\ntest('object gpu', () => {\n  testObject('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('object webgl', () => {\n  testObject('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('object webgl2', () => {\n  testObject('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('object headlessgl', () => {\n  testObject('headlessgl');\n});\n\ntest('object cpu', () => {\n  testObject('cpu');\n});\n\nfunction testNestedObject(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const { x, y } = this.thread;\n    return x + y;\n  }, { output: [2, 2] });\n  assert.deepEqual(kernel(), [new Float32Array([0, 1]), new Float32Array([1, 2])]);\n}\n\ntest('nested object auto', () => {\n  testNestedObject();\n});\n\ntest('nested object gpu', () => {\n  testNestedObject('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('nested object webgl', () => {\n  testNestedObject('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('nested object webgl2', () => {\n  testNestedObject('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('nested object headlessgl', () => {\n  testNestedObject('headlessgl');\n});\n\ntest('nested object cpu', () => {\n  testNestedObject('cpu');\n});\n\nfunction testArray(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(array) {\n    const [first, second] = array;\n    return first + second;\n  }, { output: [1], argumentTypes: { array: 'Array(2)' } });\n  assert.deepEqual(kernel([1, 2]), new Float32Array([3]));\n}\n\ntest('array auto', () => {\n  testArray();\n});\n\ntest('array gpu', () => {\n  testArray('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('array webgl', () => {\n  testArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('array webgl2', () => {\n  testArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('array headlessgl', () => {\n  testArray('headlessgl');\n});\n\ntest('array cpu', () => {\n  testArray('cpu');\n});"
  },
  {
    "path": "test/features/dev-mode.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../src');\n\ndescribe('features: dev mode');\n\ntest('are added to GPU instance .kernels property', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value;\n  }, { output: [1] });\n  assert.equal(gpu.kernels.length, 1);\n  assert.deepEqual(kernel(1), new Float32Array([1]));\n  gpu.destroy();\n});\n\ntest('works with integer', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value;\n  }, { output: [1] });\n  assert.deepEqual(kernel(1), new Float32Array([1]));\n  gpu.destroy();\n});\n\ntest('works with float', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value;\n  }, { output: [1] });\n  assert.deepEqual(kernel(1.5), new Float32Array([1.5]));\n  gpu.destroy();\n});\n\ntest('works with array', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, { output: [4] });\n  assert.deepEqual(kernel([1,2,3,4]), new Float32Array([1,2,3,4]));\n  gpu.destroy();\n});\n\ntest('works with matrix', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, { output: [4, 2] });\n  assert.deepEqual(kernel(\n    [\n      [1,2,3,4],\n      [5,6,7,8]\n    ]\n  ), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n  ]);\n  gpu.destroy();\n});\n\ntest('works with cube', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, { output: [4, 2, 2] });\n  assert.deepEqual(kernel(\n    [\n      [\n        [1,2,3,4],\n        [5,6,7,8]\n      ],\n      [\n        [9,10,11,12],\n        [13,14,15,16]\n      ]\n    ]\n  ), [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n    ],[\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ]\n  ]);\n  gpu.destroy();\n});\n\ntest('works with input array', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, { output: [4] });\n  assert.deepEqual(kernel(input([1,2,3,4], [4])), new Float32Array([1,2,3,4]));\n  gpu.destroy();\n});\n\ntest('works with input matrix', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  }, { output: [4, 2] });\n  assert.deepEqual(kernel(input([1,2,3,4,5,6,7,8], [4, 2])), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n  ]);\n  gpu.destroy();\n});\n\ntest('works with input cube', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, { output: [4, 2, 2] });\n  assert.deepEqual(kernel(\n    input([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], [4,2,2])\n  ), [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n    ],[\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ]\n  ]);\n  gpu.destroy();\n});\n\ntest('works with texture', () => {\n  const texture = ((new GPU()).createKernel(function (cube) {\n    return cube[this.thread.z][this.thread.y][this.thread.x];\n  }, { output: [4,2,2], pipeline: true }))([\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n    ],[\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ]\n  ]);\n  assert.ok(texture.constructor.name.match('Texture'));\n  const gpu = new GPU({ mode: 'dev' });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  }, { output: [4, 2, 2] });\n  assert.deepEqual(kernel(\n    texture\n  ), [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n    ],[\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ]\n  ]);\n  gpu.destroy();\n});\n\ntest('works with adding functions', () => {\n  const gpu = new GPU({ mode: 'dev' });\n  function addOne(value) {\n    return value + 1;\n  }\n  gpu.addFunction(addOne);\n  const kernel = gpu.createKernel(function(value) {\n    return addOne(value);\n  }, { output: [1] });\n  assert.deepEqual(kernel(1), new Float32Array([2]));\n  gpu.destroy();\n});\n"
  },
  {
    "path": "test/features/dynamic-arguments.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../src');\n\ndescribe('features: dynamic arguments');\n\nfunction testHTMLImage(done, mode) {\n  const image1 = new Image();\n  const image2 = new Image();\n  image1.src = 'jellyfish.jpeg';\n  image2.src = 'jellyfish-1.jpeg';\n  let loadedCount = 0;\n  image1.onload = image2.onload = () => {\n    loadedCount++;\n    if (loadedCount === 2) loaded();\n  };\n\n  function loaded() {\n    const gpu = new GPU({ mode });\n    const kernel = gpu.createKernel(function(image) {\n      const pixel = image[this.thread.y][this.thread.x];\n      return (pixel[0] + pixel[1] + pixel[2]) / 3;\n    })\n      .setDynamicArguments(true)\n      .setDynamicOutput(true)\n      .setOutput([276, 183]);\n\n    const pixels1 = kernel(image1);\n    kernel.setOutput([138, 91]);\n    const pixels2 = kernel(image2);\n\n    assert.ok(pixels1[0][0] > .43);\n    assert.ok(pixels1[0][0] < .45);\n    assert.equal(pixels1.length, image1.height);\n    assert.equal(pixels1[0].length, image1.width);\n    assert.ok(pixels2[0][0] > .82);\n    assert.ok(pixels2[0][0] < .83);\n    assert.equal(pixels2.length, image2.height);\n    assert.equal(pixels2[0].length, image2.width);\n\n    gpu.destroy();\n    done();\n  }\n}\n\n(typeof Image !== 'undefined' ? test : skip)('HTML Image auto', t => {\n  testHTMLImage(t.async());\n});\n\n(typeof Image !== 'undefined' && GPU.isWebGLSupported ? test : skip)('HTML Image webgl', t => {\n  testHTMLImage(t.async(), 'webgl');\n});\n\n(typeof Image !== 'undefined' && GPU.isWebGL2Supported ? test : skip)('HTML Image webgl2', t => {\n  testHTMLImage(t.async(), 'webgl2');\n});\n\n(typeof Image !== 'undefined' ? test : skip)('HTML Image cpu', t => {\n  testHTMLImage(t.async(), 'cpu');\n});\n\nfunction testMemoryOptimizedNumberTexture(mode) {\n  const gpu = new GPU({ mode });\n  const matrix4X4 = [\n    [1,2,3,4],\n    [5,6,7,8],\n    [9,10,11,12],\n    [13,14,15,16],\n  ];\n  const texture4X4 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([4, 4])\n      .setPrecision('single')\n      .setOptimizeFloatMemory(true)\n      .setPipeline(true)\n  )(matrix4X4);\n\n  const matrix3X3 = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  const texture3X3 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([3, 3])\n      .setPrecision('single')\n      .setOptimizeFloatMemory(true)\n      .setPipeline(true)\n  )(matrix3X3);\n\n  const matrix2X2 = [\n    [1,2],\n    [3,4]\n  ];\n  const texture2X2 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([2, 2])\n      .setPrecision('single')\n      .setOptimizeFloatMemory(true)\n      .setPipeline(true)\n  )(matrix2X2);\n\n  const kernel = gpu.createKernel(function(texture) {\n    return texture[this.thread.y][this.thread.x];\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOptimizeFloatMemory(true)\n    .setOutput([4,4]);\n\n  assert.deepEqual(kernel(texture4X4), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ]);\n\n  kernel.setOutput([3, 3]);\n  assert.deepEqual(kernel(texture3X3), [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n  ]);\n\n  kernel.setOutput([2, 2]);\n  assert.deepEqual(kernel(texture2X2), [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n  ]);\n\n  assert.ok(kernel.kernelArguments[0].constructor.name.match('DynamicMemoryOptimizedNumberTexture'));\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('MemoryOptimizedNumberTexture (GPU only) auto', () => {\n  testMemoryOptimizedNumberTexture();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('MemoryOptimizedNumberTexture (GPU only) gpu', () => {\n  testMemoryOptimizedNumberTexture('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('MemoryOptimizedNumberTexture (GPU only) webgl', () => {\n  testMemoryOptimizedNumberTexture('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('MemoryOptimizedNumberTexture (GPU only) webgl2', () => {\n  testMemoryOptimizedNumberTexture('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('MemoryOptimizedNumberTexture (GPU only) headlessgl', () => {\n  testMemoryOptimizedNumberTexture('headlessgl');\n});\n\nfunction testNumberTexture(mode) {\n  const gpu = new GPU({ mode });\n  const matrix4X4 = [\n    [1,2,3,4],\n    [5,6,7,8],\n    [9,10,11,12],\n    [13,14,15,16],\n  ];\n  const texture4X4 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([4, 4])\n      .setPrecision('single')\n      .setPipeline(true)\n  )(matrix4X4);\n\n  const matrix3X3 = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  const texture3X3 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([3, 3])\n      .setPrecision('single')\n      .setPipeline(true)\n  )(matrix3X3);\n\n  const matrix2X2 = [\n    [1,2],\n    [3,4]\n  ];\n  const texture2X2 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([2, 2])\n      .setPrecision('single')\n      .setPipeline(true)\n  )(matrix2X2);\n\n  const kernel = gpu.createKernel(function(texture) {\n    return texture[this.thread.y][this.thread.x];\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  assert.deepEqual(kernel(texture4X4), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ]);\n\n  kernel.setOutput([3, 3]);\n  assert.deepEqual(kernel(texture3X3), [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n  ]);\n\n  kernel.setOutput([2, 2]);\n  assert.deepEqual(kernel(texture2X2), [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n  ]);\n\n  assert.ok(kernel.kernelArguments[0].constructor.name.match('NumberTexture'));\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('NumberTexture (GPU only) auto', () => {\n  testNumberTexture();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('NumberTexture (GPU only) gpu', () => {\n  testNumberTexture('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('NumberTexture (GPU only) webgl', () => {\n  testNumberTexture('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('NumberTexture (GPU only) webgl2', () => {\n  testNumberTexture('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('NumberTexture (GPU only) headlessgl', () => {\n  testNumberTexture('headlessgl');\n});\n\nfunction testMixedNumberTexture(mode) {\n  const gpu = new GPU({ mode });\n  const matrix4X4 = [\n    [1,2,3,4],\n    [5,6,7,8],\n    [9,10,11,12],\n    [13,14,15,16],\n  ];\n  const texture4X4 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([4, 4])\n      .setPrecision('single')\n      .setOptimizeFloatMemory(true)\n      .setPipeline(true)\n  )(matrix4X4);\n\n  const matrix3X3 = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  const texture3X3 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([3, 3])\n      .setPrecision('single')\n      .setOptimizeFloatMemory(true)\n      .setPipeline(true)\n  )(matrix3X3);\n\n  const matrix2X2 = [\n    [1,2],\n    [3,4]\n  ];\n  const texture2X2 = (\n    gpu.createKernel(function(value) {\n      return value[this.thread.y][this.thread.x];\n    })\n      .setOutput([2, 2])\n      .setPrecision('single')\n      .setPipeline(true)\n  )(matrix2X2);\n\n  const kernel = gpu.createKernel(function(texture) {\n    return texture[this.thread.y][this.thread.x];\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  assert.deepEqual(kernel(texture4X4), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ]);\n\n  kernel.setOutput([3, 3]);\n  assert.deepEqual(kernel(texture3X3), [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n  ]);\n\n  kernel.setOutput([2, 2]);\n  assert.deepEqual(kernel(texture2X2), [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n  ]);\n\n  assert.ok(kernel.kernelArguments[0].constructor.name.match('NumberTexture'));\n\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Mixed NumberTexture (GPU only) auto', () => {\n  testMixedNumberTexture();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Mixed NumberTexture (GPU only) gpu', () => {\n  testMixedNumberTexture('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Mixed NumberTexture (GPU only) webgl', () => {\n  testMixedNumberTexture('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Mixed NumberTexture (GPU only) webgl2', () => {\n  testMixedNumberTexture('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Mixed NumberTexture (GPU only) headlessgl', () => {\n  testMixedNumberTexture('headlessgl');\n});\n\nfunction testSingleArray1D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array1D(2)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n  ];\n  kernel.setOutput([expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    new Float32Array([1,2]),\n    new Float32Array([3,4]),\n    new Float32Array([5,6]),\n    new Float32Array([7,8]),\n  ];\n  kernel.setOutput([expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array1D2 auto', () => {\n  testSingleArray1D2();\n});\n\ntest('Single Array1D2 gpu', () => {\n  testSingleArray1D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array1D2 webgl', () => {\n  testSingleArray1D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array1D2 webgl2', () => {\n  testSingleArray1D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array1D2 headlessgl', () => {\n  testSingleArray1D2('headlessgl');\n});\n\ntest('Single Array1D2 cpu', () => {\n  testSingleArray1D2('cpu');\n});\n\nfunction testSingleArray1D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array1D(3)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n  ];\n  kernel.setOutput([expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9]),\n    new Float32Array([10,11,12]),\n  ];\n  kernel.setOutput([expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array1D3 auto', () => {\n  testSingleArray1D3();\n});\n\ntest('Single Array1D3 gpu', () => {\n  testSingleArray1D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array1D3 webgl', () => {\n  testSingleArray1D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array1D3 webgl2', () => {\n  testSingleArray1D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array1D3 headlessgl', () => {\n  testSingleArray1D3('headlessgl');\n});\n\ntest('Single Array1D3 cpu', () => {\n  testSingleArray1D3('cpu');\n});\n\nfunction testSingleArray1D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array1D(4)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n  ];\n  kernel.setOutput([expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ];\n  kernel.setOutput([expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array1D4 auto', () => {\n  testSingleArray1D4();\n});\n\ntest('Single Array1D4 gpu', () => {\n  testSingleArray1D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array1D4 webgl', () => {\n  testSingleArray1D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array1D4 webgl2', () => {\n  testSingleArray1D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array1D4 headlessgl', () => {\n  testSingleArray1D4('headlessgl');\n});\n\ntest('Single Array1D4 cpu', () => {\n  testSingleArray1D4('cpu');\n});\n\nfunction testSingleArray2D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array2D(2)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      new Float32Array([1,2]),\n      new Float32Array([3,4]),\n    ],[\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ]\n  ];\n  kernel.setOutput([expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      new Float32Array([1,2]),\n      new Float32Array([3,4]),\n      new Float32Array([5,6]),\n      new Float32Array([7,8]),\n    ],[\n      new Float32Array([9,10]),\n      new Float32Array([11,12]),\n      new Float32Array([13,14]),\n      new Float32Array([15,16]),\n    ]\n  ];\n  kernel.setOutput([expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array2D2 auto', () => {\n  testSingleArray2D2();\n});\n\ntest('Single Array2D2 gpu', () => {\n  testSingleArray2D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array2D2 webgl', () => {\n  testSingleArray2D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array2D2 webgl2', () => {\n  testSingleArray2D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array2D2 headlessgl', () => {\n  testSingleArray2D2('headlessgl');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array2D2 cpu', () => {\n  testSingleArray2D2('cpu');\n});\n\nfunction testSingleArray2D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array2D(3)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      new Float32Array([1,2,3]),\n      new Float32Array([4,5,6]),\n    ],[\n      new Float32Array([7,8,9]),\n      new Float32Array([10,11,12]),\n    ]\n  ];\n  kernel.setOutput([expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      new Float32Array([1,2,3]),\n      new Float32Array([4,5,6]),\n      new Float32Array([7,8,9]),\n      new Float32Array([10,11,12]),\n    ],[\n      new Float32Array([13,14,15]),\n      new Float32Array([16,17,18]),\n      new Float32Array([19,20,21]),\n      new Float32Array([22,23,24]),\n    ]\n  ];\n  kernel.setOutput([expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array2D3 auto', () => {\n  testSingleArray2D3();\n});\n\ntest('Single Array2D3 gpu', () => {\n  testSingleArray2D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array2D3 webgl', () => {\n  testSingleArray2D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array2D3 webgl2', () => {\n  testSingleArray2D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array2D3 headlessgl', () => {\n  testSingleArray2D3('headlessgl');\n});\n\ntest('Single Array2D3 cpu', () => {\n  testSingleArray2D3('cpu');\n});\n\nfunction testSingleArray2D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array2D(4)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n    ],[\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ]\n  ];\n  kernel.setOutput([expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      new Float32Array([1,2,3,4]),\n      new Float32Array([5,6,7,8]),\n      new Float32Array([9,10,11,12]),\n      new Float32Array([13,14,15,16]),\n    ],[\n      new Float32Array([17,18,19,20]),\n      new Float32Array([21,22,23,24]),\n      new Float32Array([25,26,27,28]),\n      new Float32Array([29,30,31,32]),\n    ]\n  ];\n  kernel.setOutput([expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array2D4 auto', () => {\n  testSingleArray2D4();\n});\n\ntest('Single Array2D4 gpu', () => {\n  testSingleArray2D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array2D4 webgl', () => {\n  testSingleArray2D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array2D4 webgl2', () => {\n  testSingleArray2D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array2D4 headlessgl', () => {\n  testSingleArray2D4('headlessgl');\n});\n\ntest('Single Array2D4 cpu', () => {\n  testSingleArray2D4('cpu');\n});\n\nfunction testSingleArray3D2(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array3D(2)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      [\n        new Float32Array([1,2]),\n        new Float32Array([3,4]),\n      ],[\n        new Float32Array([5,6]),\n        new Float32Array([7,8]),\n      ]\n    ],[\n      [\n        new Float32Array([9,10]),\n        new Float32Array([11,12]),\n      ],[\n        new Float32Array([13,14]),\n        new Float32Array([15,16]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected1[0][0].length, expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      [\n        new Float32Array([1,2]),\n        new Float32Array([3,4]),\n        new Float32Array([5,6]),\n        new Float32Array([7,8]),\n      ],[\n        new Float32Array([9,10]),\n        new Float32Array([11,12]),\n        new Float32Array([13,14]),\n        new Float32Array([15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18]),\n        new Float32Array([19,20]),\n        new Float32Array([21,22]),\n        new Float32Array([23,24]),\n      ],[\n        new Float32Array([25,26]),\n        new Float32Array([27,28]),\n        new Float32Array([29,30]),\n        new Float32Array([31,32]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected2[0][0].length, expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array3D2 auto', () => {\n  testSingleArray3D2();\n});\n\ntest('Single Array3D2 gpu', () => {\n  testSingleArray3D2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array3D2 webgl', () => {\n  testSingleArray3D2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array3D2 webgl2', () => {\n  testSingleArray3D2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array3D2 headlessgl', () => {\n  testSingleArray3D2('headlessgl');\n});\n\ntest('Single Array3D2 cpu', () => {\n  testSingleArray3D2('cpu');\n});\n\nfunction testSingleArray3D3(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array3D(3)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      [\n        new Float32Array([1,2,3]),\n        new Float32Array([4,5,6]),\n      ],[\n        new Float32Array([7,8,9]),\n        new Float32Array([10,11,12]),\n      ]\n    ],[\n      [\n        new Float32Array([13,14,15]),\n        new Float32Array([16,17,18]),\n      ],[\n        new Float32Array([19,20,21]),\n        new Float32Array([22,23,24]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected1[0][0].length, expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      [\n        new Float32Array([1,2,3]),\n        new Float32Array([4,5,6]),\n        new Float32Array([7,8,9]),\n        new Float32Array([10,11,12]),\n      ],[\n        new Float32Array([13,14,15]),\n        new Float32Array([16,17,18]),\n        new Float32Array([19,20,21]),\n        new Float32Array([22,23,24]),\n      ]\n    ],[\n      [\n        new Float32Array([25,26,27]),\n        new Float32Array([28,29,30]),\n        new Float32Array([31,32,33]),\n        new Float32Array([34,35,36]),\n      ],[\n        new Float32Array([37,38,39]),\n        new Float32Array([40,41,42]),\n        new Float32Array([43,44,45]),\n        new Float32Array([46,47,48]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected2[0][0].length, expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array3D3 auto', () => {\n  testSingleArray3D3();\n});\n\ntest('Single Array3D3 gpu', () => {\n  testSingleArray3D3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array3D3 webgl', () => {\n  testSingleArray3D3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array3D3 webgl2', () => {\n  testSingleArray3D3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array3D3 headlessgl', () => {\n  testSingleArray3D3('headlessgl');\n});\n\ntest('Single Array3D3 cpu', () => {\n  testSingleArray3D3('cpu');\n});\n\nfunction testSingleArray3D4(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    argumentTypes: { v: 'Array3D(4)' },\n    dynamicArguments: true,\n    dynamicOutput: true\n  });\n  const expected1 = [\n    [\n      [\n        new Float32Array([1,2,3,4]),\n        new Float32Array([5,6,7,8]),\n      ],[\n        new Float32Array([9,10,11,12]),\n        new Float32Array([13,14,15,16]),\n      ]\n    ],[\n      [\n        new Float32Array([17,18,19,20]),\n        new Float32Array([21,22,23,24]),\n      ],[\n        new Float32Array([25,26,27,28]),\n        new Float32Array([29,30,31,32]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected1[0][0].length, expected1[0].length, expected1.length]);\n  assert.deepEqual(kernel(expected1), expected1);\n  const expected2 = [\n    [\n      [\n        new Float32Array([1,2,3,4]),\n        new Float32Array([5,6,7,8]),\n        new Float32Array([9,10,11,12]),\n        new Float32Array([13,14,15,16]),\n      ],[\n        new Float32Array([17,18,19,20]),\n        new Float32Array([21,22,23,24]),\n        new Float32Array([25,26,27,28]),\n        new Float32Array([29,30,31,32]),\n      ]\n    ],[\n      [\n        new Float32Array([33,34,35,36]),\n        new Float32Array([37,38,39,40]),\n        new Float32Array([41,42,43,44]),\n        new Float32Array([45,46,47,48]),\n      ],[\n        new Float32Array([49,50,51,52]),\n        new Float32Array([53,54,56,57]),\n        new Float32Array([58,59,60,61]),\n        new Float32Array([62,63,64,65]),\n      ]\n    ]\n  ];\n  kernel.setOutput([expected2[0][0].length, expected2[0].length, expected2.length]);\n  assert.deepEqual(kernel(expected2), expected2);\n  gpu.destroy();\n}\n\ntest('Single Array3D4 auto', () => {\n  testSingleArray3D4();\n});\n\ntest('Single Array3D4 gpu', () => {\n  testSingleArray3D4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single Array3D4 webgl', () => {\n  testSingleArray3D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single Array3D4 webgl2', () => {\n  testSingleArray3D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single Array3D4 headlessgl', () => {\n  testSingleArray3D4('headlessgl');\n});\n\ntest('Single Array3D4 cpu', () => {\n  testSingleArray3D4('cpu');\n});\n\nfunction testUnsignedPrecisionArray(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.x];\n  })\n    .setPrecision('unsigned')\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([5]);\n\n  assert.deepEqual(kernel([1,2,3,4,5]), new Float32Array([1,2,3,4,5]));\n  kernel.setOutput([4]);\n  assert.deepEqual(kernel([1,2,3,4]), new Float32Array([1,2,3,4]));\n  kernel.setOutput([3]);\n  assert.deepEqual(kernel([1,2,3]), new Float32Array([1,2,3]));\n  kernel.setOutput([2]);\n  assert.deepEqual(kernel([1,2]), new Float32Array([1,2]));\n  gpu.destroy();\n}\n\ntest('unsigned precision Array auto', () => {\n  testUnsignedPrecisionArray();\n});\n\ntest('unsigned precision Array gpu', () => {\n  testUnsignedPrecisionArray('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Array webgl', () => {\n  testUnsignedPrecisionArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Array webgl2', () => {\n  testUnsignedPrecisionArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Array headlessgl', () => {\n  testUnsignedPrecisionArray('headlessgl');\n});\n\ntest('unsigned precision Array cpu', () => {\n  testUnsignedPrecisionArray('cpu');\n});\n\nfunction testSinglePrecisionArray(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.x];\n  })\n    .setPrecision('single')\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([5]);\n\n  assert.deepEqual(kernel([1,2,3,4,5]), new Float32Array([1,2,3,4,5]));\n  kernel.setOutput([4]);\n  assert.deepEqual(kernel([1,2,3,4]), new Float32Array([1,2,3,4]));\n  kernel.setOutput([3]);\n  assert.deepEqual(kernel([1,2,3]), new Float32Array([1,2,3]));\n  kernel.setOutput([2]);\n  assert.deepEqual(kernel([1,2]), new Float32Array([1,2]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array auto', () => {\n  testSinglePrecisionArray();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Array gpu', () => {\n  testSinglePrecisionArray('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Array webgl', () => {\n  testSinglePrecisionArray('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Array webgl2', () => {\n  testSinglePrecisionArray('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Array headlessgl', () => {\n  testSinglePrecisionArray('headlessgl');\n});\n\ntest('single precision Array cpu', () => {\n  testSinglePrecisionArray('cpu');\n});\n\nfunction testUnsignedPrecisionMatrix(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.y][this.thread.x];\n  })\n    .setPrecision('unsigned')\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  let matrix = [\n    [1,2,3,4],\n    [5,6,7,8],\n    [9,10,11,12],\n    [13,14,15,16]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n\n  kernel.setOutput([3,3]);\n  matrix = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n\n  kernel.setOutput([2,2]);\n  matrix = [\n    [1,2],\n    [3,4]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n  gpu.destroy();\n}\n\ntest('unsigned precision Matrix auto', () => {\n  testUnsignedPrecisionMatrix();\n});\n\ntest('unsigned precision Matrix gpu', () => {\n  testUnsignedPrecisionMatrix('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Matrix webgl', () => {\n  testUnsignedPrecisionMatrix('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Matrix webgl2', () => {\n  testUnsignedPrecisionMatrix('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Matrix headlessgl', () => {\n  testUnsignedPrecisionMatrix('headlessgl');\n});\n\ntest('unsigned precision Matrix cpu', () => {\n  testUnsignedPrecisionMatrix('cpu');\n});\n\nfunction testSinglePrecisionMatrix(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.y][this.thread.x];\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  let matrix = [\n    [1,2,3,4],\n    [5,6,7,8],\n    [9,10,11,12],\n    [13,14,15,16]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n\n  kernel.setOutput([3,3]);\n  matrix = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n\n  kernel.setOutput([2,2]);\n  matrix = [\n    [1,2],\n    [3,4]\n  ];\n  assert.deepEqual(kernel(matrix), matrix.map(row => new Float32Array(row)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Matrix auto', () => {\n  testSinglePrecisionMatrix();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Matrix gpu', () => {\n  testSinglePrecisionMatrix('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Matrix webgl', () => {\n  testSinglePrecisionMatrix('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Matrix webgl2', () => {\n  testSinglePrecisionMatrix('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Matrix headlessgl', () => {\n  testSinglePrecisionMatrix('headlessgl');\n});\n\ntest('single precision Matrix cpu', () => {\n  testSinglePrecisionMatrix('cpu');\n});\n\nfunction testUnsignedPrecisionInputMatrix(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.y][this.thread.x];\n  })\n    .setPrecision('unsigned')\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  let matrix = input([\n    1,2,3,4,\n    5,6,7,8,\n    9,10,11,12,\n    13,14,15,16\n  ], [4, 4]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16]),\n  ]);\n\n  kernel.setOutput([3,3]);\n  matrix = input([\n    1,2,3,\n    4,5,6,\n    7,8,9\n  ], [3,3]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9])\n  ]);\n\n  kernel.setOutput([2,2]);\n  matrix = input([\n    1,2,\n    3,4\n  ], [2,2]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2]),\n    new Float32Array([3,4])\n  ]);\n  gpu.destroy();\n}\n\ntest('unsigned precision Input Matrix auto', () => {\n  testUnsignedPrecisionInputMatrix();\n});\n\ntest('unsigned precision Input Matrix gpu', () => {\n  testUnsignedPrecisionInputMatrix('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision Input Matrix webgl', () => {\n  testUnsignedPrecisionInputMatrix('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision Input Matrix webgl2', () => {\n  testUnsignedPrecisionInputMatrix('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision Input Matrix headlessgl', () => {\n  testUnsignedPrecisionInputMatrix('headlessgl');\n});\n\ntest('unsigned precision Input Matrix cpu', () => {\n  testUnsignedPrecisionInputMatrix('cpu');\n});\n\nfunction testSinglePrecisionInputMatrix(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.y][this.thread.x];\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true)\n    .setOutput([4,4]);\n\n  let matrix = input([\n    1,2,3,4,\n    5,6,7,8,\n    9,10,11,12,\n    13,14,15,16\n  ], [4, 4]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2,3,4]),\n    new Float32Array([5,6,7,8]),\n    new Float32Array([9,10,11,12]),\n    new Float32Array([13,14,15,16])\n  ]);\n\n  kernel.setOutput([3,3]);\n  matrix = input([\n    1,2,3,\n    4,5,6,\n    7,8,9\n  ], [3, 3]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2,3]),\n    new Float32Array([4,5,6]),\n    new Float32Array([7,8,9])\n  ]);\n\n  kernel.setOutput([2,2]);\n  matrix = input([\n    1,2,\n    3,4\n  ], [2,2]);\n  assert.deepEqual(kernel(matrix), [\n    new Float32Array([1,2]),\n    new Float32Array([3,4])\n  ]);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Input Matrix auto', () => {\n  testSinglePrecisionInputMatrix();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('single precision Input Matrix gpu', () => {\n  testSinglePrecisionInputMatrix('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision Input Matrix webgl', () => {\n  testSinglePrecisionInputMatrix('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision Input Matrix webgl2', () => {\n  testSinglePrecisionInputMatrix('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision Input Matrix headlessgl', () => {\n  testSinglePrecisionInputMatrix('headlessgl');\n});\n\ntest('single precision Input Matrix cpu', () => {\n  testSinglePrecisionInputMatrix('cpu');\n});\n"
  },
  {
    "path": "test/features/dynamic-output.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: dynamic output');\n\nfunction dynamicOutput1DGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.thread.x;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([5]);\n  let result = kernel();\n  assert.equal(result.length, 5);\n  assert.deepEqual(Array.from(result), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(kernel.output), [5]);\n\n  kernel.setOutput([10]);\n  result = kernel();\n  assert.equal(result.length, 10);\n  assert.deepEqual(Array.from(result), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(kernel.output), [10]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 1d grows auto', () => {\n  dynamicOutput1DGrows();\n});\n\ntest('dynamic output 1d grows gpu', () => {\n  dynamicOutput1DGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 1d grows webgl', () => {\n  dynamicOutput1DGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 1d grows webgl2', () => {\n  dynamicOutput1DGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 1d grows headlessgl', () => {\n  dynamicOutput1DGrows('headlessgl');\n});\n\ntest('dynamic output 1d grows cpu', () => {\n  dynamicOutput1DGrows('cpu');\n});\n\n\nfunction dynamicOutput1DShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.thread.x;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([10]);\n  let result = kernel();\n  assert.equal(result.length, 10);\n  assert.deepEqual(Array.from(result), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(kernel.output), [10]);\n\n  kernel.setOutput([5]);\n  result = kernel();\n  assert.equal(result.length, 5);\n  assert.deepEqual(Array.from(result), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(kernel.output), [5]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 1d shrinks auto', () => {\n  dynamicOutput1DShrinks();\n});\n\ntest('dynamic output 1d shrinks gpu', () => {\n  dynamicOutput1DShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 1d shrinks webgl', () => {\n  dynamicOutput1DShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 1d shrinks webgl2', () => {\n  dynamicOutput1DShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 1d shrinks headlessgl', () => {\n  dynamicOutput1DShrinks('headlessgl');\n});\n\ntest('dynamic output 1d shrinks cpu', () => {\n  dynamicOutput1DShrinks('cpu');\n});\n\nfunction dynamicOutput1DKernelMapGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result2: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.thread.x);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([5]);\n  let result = kernel();\n  assert.equal(result.result.length, 5);\n  assert.equal(result.result2.length, 5);\n  assert.deepEqual(Array.from(result.result), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(result.result2), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(kernel.output), [5]);\n\n  kernel.setOutput([10]);\n  result = kernel();\n  assert.equal(result.result.length, 10);\n  assert.equal(result.result2.length, 10);\n  assert.deepEqual(Array.from(result.result), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(result.result2), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(kernel.output), [10]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 1d kernel map grows auto', () => {\n  dynamicOutput1DKernelMapGrows();\n});\n\ntest('dynamic output 1d kernel map grows gpu', () => {\n  dynamicOutput1DKernelMapGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 1d kernel map grows webgl', () => {\n  dynamicOutput1DKernelMapGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 1d kernel map grows webgl2', () => {\n  dynamicOutput1DKernelMapGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 1d kernel map grows headlessgl', () => {\n  dynamicOutput1DKernelMapGrows('headlessgl');\n});\n\ntest('dynamic output 1d kernel map grows cpu', () => {\n  dynamicOutput1DKernelMapGrows('cpu');\n});\n\n\nfunction dynamicOutput1DKernelMapShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result2: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.thread.x);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([10]);\n  let result = kernel();\n  assert.equal(result.result.length, 10);\n  assert.equal(result.result2.length, 10);\n  assert.deepEqual(Array.from(result.result), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(result.result2), [10,11,12,13,14,15,16,17,18,19]);\n  assert.deepEqual(Array.from(kernel.output), [10]);\n\n  kernel.setOutput([5]);\n  result = kernel();\n  assert.equal(result.result.length, 5);\n  assert.equal(result.result2.length, 5);\n  assert.deepEqual(Array.from(result.result), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(result.result2), [5,6,7,8,9]);\n  assert.deepEqual(Array.from(kernel.output), [5]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 1d kernel map shrinks auto', () => {\n  dynamicOutput1DKernelMapShrinks();\n});\n\ntest('dynamic output 1d kernel map shrinks gpu', () => {\n  dynamicOutput1DKernelMapShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 1d kernel map shrinks webgl', () => {\n  dynamicOutput1DKernelMapShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 1d kernel map shrinks webgl2', () => {\n  dynamicOutput1DKernelMapShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 1d kernel map shrinks headlessgl', () => {\n  dynamicOutput1DKernelMapShrinks('headlessgl');\n});\n\ntest('dynamic output 1d kernel map shrinks cpu', () => {\n  dynamicOutput1DKernelMapShrinks('cpu');\n});\n\nfunction dynamicOutput2DGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.output.y + this.thread.x + this.thread.y;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([2,2]);\n  let result = kernel();\n  assert.equal(result.length, 2);\n  assert.deepEqual(result.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  kernel.setOutput([3,3]);\n  result = kernel();\n  assert.equal(result.length, 3);\n  assert.deepEqual(result.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d grows auto', () => {\n  dynamicOutput2DGrows();\n});\n\ntest('dynamic output 2d grows gpu', () => {\n  dynamicOutput2DGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d grows webgl', () => {\n  dynamicOutput2DGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d grows webgl2', () => {\n  dynamicOutput2DGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d grows headlessgl', () => {\n  dynamicOutput2DGrows('headlessgl');\n});\n\ntest('dynamic output 2d grows cpu', () => {\n  dynamicOutput2DGrows('cpu');\n});\n\n\nfunction dynamicOutput2DShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.output.y + this.thread.x + this.thread.y;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([3,3]);\n  let result = kernel();\n  assert.equal(result.length, 3);\n  assert.deepEqual(result.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  kernel.setOutput([2,2]);\n  result = kernel();\n  assert.equal(result.length, 2);\n  assert.deepEqual(result.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d shrinks auto', () => {\n  dynamicOutput2DShrinks();\n});\n\ntest('dynamic output 2d shrinks gpu', () => {\n  dynamicOutput2DShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d shrinks webgl', () => {\n  dynamicOutput2DShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d shrinks webgl2', () => {\n  dynamicOutput2DShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d shrinks headlessgl', () => {\n  dynamicOutput2DShrinks('headlessgl');\n});\n\ntest('dynamic output 2d shrinks cpu', () => {\n  dynamicOutput2DShrinks('cpu');\n});\n\nfunction dynamicOutput2DKernelMapGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result1: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.output.y + this.thread.x + this.thread.y);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([2,2]);\n  let result = kernel();\n  assert.equal(result.result.length, 2);\n  assert.equal(result.result1.length, 2);\n  assert.deepEqual(result.result.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(result.result1.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  kernel.setOutput([3,3]);\n  result = kernel();\n  assert.equal(result.result.length, 3);\n  assert.equal(result.result1.length, 3);\n  assert.deepEqual(result.result.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(result.result1.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d kernel map grows auto', () => {\n  dynamicOutput2DKernelMapGrows();\n});\n\ntest('dynamic output 2d kernel map grows gpu', () => {\n  dynamicOutput2DKernelMapGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d kernel map grows webgl', () => {\n  dynamicOutput2DKernelMapGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d kernel map grows webgl2', () => {\n  dynamicOutput2DKernelMapGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d kernel map grows headlessgl', () => {\n  dynamicOutput2DKernelMapGrows('headlessgl');\n});\n\ntest('dynamic output 2d kernel map grows cpu', () => {\n  dynamicOutput2DKernelMapGrows('cpu');\n});\n\n\nfunction dynamicOutput2DKernelMapShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result1: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.output.y + this.thread.x + this.thread.y);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([3,3]);\n  let result = kernel();\n  assert.equal(result.result.length, 3);\n  assert.equal(result.result1.length, 3);\n  assert.deepEqual(result.result.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(result.result1.map(row => Array.from(row)), [[6,7,8],[7,8,9],[8,9,10]]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  kernel.setOutput([2,2]);\n  result = kernel();\n  assert.equal(result.result.length, 2);\n  assert.equal(result.result1.length, 2);\n  assert.deepEqual(result.result.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(result.result1.map(row => Array.from(row)), [[4,5],[5,6]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d shrinks auto', () => {\n  dynamicOutput2DShrinks();\n});\n\ntest('dynamic output 2d shrinks gpu', () => {\n  dynamicOutput2DShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d shrinks webgl', () => {\n  dynamicOutput2DShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d shrinks webgl2', () => {\n  dynamicOutput2DShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d shrinks headlessgl', () => {\n  dynamicOutput2DShrinks('headlessgl');\n});\n\ntest('dynamic output 2d shrinks cpu', () => {\n  dynamicOutput2DShrinks('cpu');\n});\n//TODO:\n\nfunction dynamicOutput2DGraphicalGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    this.color(1,1,1,1);\n  }, { graphical: true, dynamicOutput: true });\n\n  kernel.setOutput([2,2]);\n  kernel();\n  let result = kernel.getPixels();\n  assert.equal(result.length, 2 * 2 * 4);\n  assert.deepEqual(Array.from(result), [\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  kernel.setOutput([3,3]);\n  kernel();\n  result = kernel.getPixels();\n  assert.equal(result.length, 3 * 3 * 4);\n  assert.deepEqual(Array.from(result), [\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d graphical grows auto', () => {\n  dynamicOutput2DGraphicalGrows();\n});\n\ntest('dynamic output 2d graphical grows gpu', () => {\n  dynamicOutput2DGraphicalGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d graphical grows webgl', () => {\n  dynamicOutput2DGraphicalGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d graphical grows webgl2', () => {\n  dynamicOutput2DGraphicalGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d graphical grows headlessgl', () => {\n  dynamicOutput2DGraphicalGrows('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('dynamic output 2d graphical grows cpu', () => {\n  dynamicOutput2DGraphicalGrows('cpu');\n});\n\n\nfunction dynamicOutput2DGraphicalShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    this.color(1,1,1,1);\n  }, { graphical: true, dynamicOutput: true });\n\n  kernel.setOutput([3,3]);\n  kernel();\n  let result = kernel.getPixels();\n  assert.equal(result.length, 3 * 3 * 4);\n  assert.deepEqual(Array.from(result), [\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3]);\n\n  kernel.setOutput([2,2]);\n  kernel();\n  result = kernel.getPixels();\n  assert.equal(result.length, 2 * 2 * 4);\n  assert.deepEqual(Array.from(result), [\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [2,2]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 2d graphical shrinks auto', () => {\n  dynamicOutput2DGraphicalShrinks();\n});\n\ntest('dynamic output 2d graphical shrinks gpu', () => {\n  dynamicOutput2DGraphicalShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 2d graphical shrinks webgl', () => {\n  dynamicOutput2DGraphicalShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 2d graphical shrinks webgl2', () => {\n  dynamicOutput2DGraphicalShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 2d graphical shrinks headlessgl', () => {\n  dynamicOutput2DGraphicalShrinks('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('dynamic output 2d graphical  shrinks cpu', () => {\n  dynamicOutput2DGraphicalShrinks('cpu');\n});\n\nfunction dynamicOutput3DGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.output.y + this.thread.z + this.thread.x + this.thread.y + this.thread.z;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([2,2,2]);\n  let result = kernel();\n  assert.equal(result.length, 2);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2,2]);\n\n  kernel.setOutput([3,3,3]);\n  result = kernel();\n  assert.equal(result.length, 3);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3,3]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 3d grows auto', () => {\n  dynamicOutput3DGrows();\n});\n\ntest('dynamic output 3d grows gpu', () => {\n  dynamicOutput3DGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 3d grows webgl', () => {\n  dynamicOutput3DGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 3d grows webgl2', () => {\n  dynamicOutput3DGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 3d grows headlessgl', () => {\n  dynamicOutput3DGrows('headlessgl');\n});\n\ntest('dynamic output 3d grows cpu', () => {\n  dynamicOutput3DGrows('cpu');\n});\n\n\nfunction dynamicOutput3DShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.output.x + this.output.y + this.thread.z + this.thread.x + this.thread.y + this.thread.z;\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([3,3,3]);\n  let result = kernel();\n  assert.equal(result.length, 3);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3,3]);\n\n  kernel.setOutput([2,2,2]);\n  result = kernel();\n  assert.equal(result.length, 2);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2,2]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 3d shrinks auto', () => {\n  dynamicOutput3DShrinks();\n});\n\ntest('dynamic output 3d shrinks gpu', () => {\n  dynamicOutput3DShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 3d shrinks webgl', () => {\n  dynamicOutput3DShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 3d shrinks webgl2', () => {\n  dynamicOutput3DShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 3d shrinks headlessgl', () => {\n  dynamicOutput3DShrinks('headlessgl');\n});\n\ntest('dynamic output 3d shrinks cpu', () => {\n  dynamicOutput3DShrinks('cpu');\n});\n\nfunction dynamicOutput3DKernelMapGrows(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result1: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.output.y + this.thread.z + this.thread.x + this.thread.y + this.thread.z);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([2,2,2]);\n  let result = kernel();\n  assert.equal(result.result.length, 2);\n  assert.equal(result.result1.length, 2);\n  assert.deepEqual(result.result.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(result.result1.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2,2]);\n\n  kernel.setOutput([3,3,3]);\n  result = kernel();\n  assert.equal(result.result.length, 3);\n  assert.equal(result.result1.length, 3);\n  assert.deepEqual(result.result.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(result.result1.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3,3]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 3d kernel map grows auto', () => {\n  dynamicOutput3DKernelMapGrows();\n});\n\ntest('dynamic output 3d kernel map grows gpu', () => {\n  dynamicOutput3DKernelMapGrows('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 3d kernel map grows webgl', () => {\n  dynamicOutput3DKernelMapGrows('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 3d kernel map grows webgl2', () => {\n  dynamicOutput3DKernelMapGrows('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 3d kernel map grows headlessgl', () => {\n  dynamicOutput3DKernelMapGrows('headlessgl');\n});\n\ntest('dynamic output 3d kernel map grows cpu', () => {\n  dynamicOutput3DKernelMapGrows('cpu');\n});\n\n\nfunction dynamicOutput3DKernelMapShrinks(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    result1: function map(v) {\n      return v;\n    }\n  }, function() {\n    return map(this.output.x + this.output.y + this.thread.z + this.thread.x + this.thread.y + this.thread.z);\n  }, { dynamicOutput: true });\n\n  kernel.setOutput([3,3,3]);\n  let result = kernel();\n  assert.equal(result.result.length, 3);\n  assert.equal(result.result1.length, 3);\n  assert.deepEqual(result.result.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(result.result1.map(matrix => matrix.map(row => Array.from(row))), [\n    [\n      [6,7,8],\n      [7,8,9],\n      [8,9,10]\n    ],\n    [\n      [8,9,10],\n      [9,10,11],\n      [10,11,12]\n    ],\n    [\n      [10,11,12],\n      [11,12,13],\n      [12,13,14]\n    ]\n  ]);\n  assert.deepEqual(Array.from(kernel.output), [3,3,3]);\n\n  kernel.setOutput([2,2,2]);\n  result = kernel();\n  assert.equal(result.result.length, 2);\n  assert.equal(result.result1.length, 2);\n  assert.deepEqual(result.result.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(result.result1.map(matrix => matrix.map(row => Array.from(row))), [[[4,5],[5,6]],[[6,7],[7,8]]]);\n  assert.deepEqual(Array.from(kernel.output), [2,2,2]);\n\n  gpu.destroy();\n}\n\ntest('dynamic output 3d kernel map shrinks auto', () => {\n  dynamicOutput3DKernelMapShrinks();\n});\n\ntest('dynamic output 3d kernel map shrinks gpu', () => {\n  dynamicOutput3DKernelMapShrinks('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('dynamic output 3d kernel map shrinks webgl', () => {\n  dynamicOutput3DKernelMapShrinks('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('dynamic output 3d kernel map shrinks webgl2', () => {\n  dynamicOutput3DKernelMapShrinks('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('dynamic output 3d kernel map shrinks headlessgl', () => {\n  dynamicOutput3DKernelMapShrinks('headlessgl');\n});\n\ntest('dynamic output 3d kernel map shrinks cpu', () => {\n  dynamicOutput3DKernelMapShrinks('cpu');\n});\n"
  },
  {
    "path": "test/features/function-return.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: function return');\n\nfunction functionReturnFloat( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function() {\n    return 42;\n  }, {\n    output : [1]\n  });\n  assert.equal(f()[0], 42);\n  gpu.destroy();\n}\n\ntest('float auto', () => {\n  functionReturnFloat(null);\n});\n\ntest('float gpu', () => {\n  functionReturnFloat('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('float webgl', () => {\n  functionReturnFloat('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('float webgl2', () => {\n  functionReturnFloat('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('float headlessgl', () => {\n  functionReturnFloat('headlessgl');\n});\n\ntest('float cpu', () => {\n  functionReturnFloat('cpu');\n});\n\n\nfunction functionReturnArray2( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function() {\n    return [42, 43];\n  }, {\n    output : [1]\n  });\n  const result = f();\n  assert.equal(result[0].constructor, Float32Array);\n  assert.equal(result[0][0], 42);\n  assert.equal(result[0][1], 43);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(2) auto', () => {\n  functionReturnArray2(null);\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(2) gpu', () => {\n  functionReturnArray2('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) webgl', () => {\n  functionReturnArray2('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) webgl2', () => {\n  functionReturnArray2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(2) headlessgl', () => {\n  functionReturnArray2('headlessgl');\n});\n\ntest('Array(2) cpu', () => {\n  functionReturnArray2('cpu');\n});\n\nfunction functionReturnArray3( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function() {\n    return [42, 43, 44];\n  }, {\n    output : [1]\n  });\n  const result = f();\n  assert.equal(result[0].constructor, Float32Array);\n  assert.equal(result[0][0], 42);\n  assert.equal(result[0][1], 43);\n  assert.equal(result[0][2], 44);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(3) auto', () => {\n  functionReturnArray3(null);\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(3) gpu', () => {\n  functionReturnArray3('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) webgl', () => {\n  functionReturnArray3('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) webgl2', () => {\n  functionReturnArray3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(3) headlessgl', () => {\n  functionReturnArray3('headlessgl');\n});\n\ntest('Array(3) cpu', () => {\n  functionReturnArray3('cpu');\n});\n\n\nfunction functionReturnArray4( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function() {\n    return [42, 43, 44, 45];\n  }, {\n    output : [1]\n  });\n\n  const result = f();\n  assert.equal(result[0].constructor, Float32Array);\n  assert.equal(result[0][0], 42);\n  assert.equal(result[0][1], 43);\n  assert.equal(result[0][2], 44);\n  assert.equal(result[0][3], 45);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(4) auto', () => {\n  functionReturnArray4(null);\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array(4) gpu', () => {\n  functionReturnArray4('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) webgl', () => {\n  functionReturnArray4('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) webgl2', () => {\n  functionReturnArray4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Array(4) headlessgl', () => {\n  functionReturnArray4('headlessgl');\n});\n\ntest('Array(4) cpu', () => {\n  functionReturnArray4('cpu');\n});\n\nfunction functionReturnArray4MemberExpression(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    let pixel = toIntArray4(value[this.thread.y][this.thread.x]);\n    return pixel;\n    function toIntArray4(pixel) {\n      return [pixel[0] * 255, pixel[1] * 255, pixel[2] * 255, pixel[3] * 255];\n    }\n  }, {\n    output: [1, 1],\n    argumentTypes: { value: 'Array2D(4)' },\n  });\n  const result = kernel([[[1,1,1,1]]]);\n  assert.deepEqual(Array.from(result[0][0]), [255,255,255,255]);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  functionReturnArray4MemberExpression();\n});\n\ntest('gpu', () => {\n  functionReturnArray4MemberExpression('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  functionReturnArray4MemberExpression('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  functionReturnArray4MemberExpression('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  functionReturnArray4MemberExpression('headlessgl');\n});\n\ntest('cpu', () => {\n  functionReturnArray4MemberExpression('cpu');\n});"
  },
  {
    "path": "test/features/get-canvas.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('get canvas');\n\nfunction getCanvasTest(mode ) {\n  const gpu = new GPU();\n\n  assert.ok(gpu.context === null, 'context is initially null');\n  assert.ok(gpu.canvas === null, 'canvas is initially null');\n\n  const render = gpu.createKernel(function() {\n    this.color(0, 0, 0, 1);\n  }, {\n    output : [30,30],\n    mode : mode\n  }).setGraphical(true);\n\n  assert.ok(render !== null, 'function generated test');\n  assert.ok(render.canvas, 'testing for canvas after createKernel' );\n  assert.ok(render.context, 'testing for context after createKernel' );\n  assert.ok(gpu.canvas, 'testing for canvas after createKernel' );\n  assert.ok(gpu.context, 'testing for context after createKernel' );\n\n  render();\n\n  assert.ok(render.canvas, 'testing for canvas after render' );\n  assert.ok(render.context, 'testing for context after render' );\n  assert.ok(gpu.canvas, 'testing for canvas after render' );\n  assert.ok(gpu.context, 'testing for context after render' );\n\n  assert.equal(render.canvas, gpu.canvas);\n  assert.equal(render.context, gpu.context);\n\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  getCanvasTest(null);\n});\n\ntest('gpu', () => {\n  getCanvasTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  getCanvasTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  getCanvasTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  getCanvasTest('headlessgl');\n});\n\ntest('cpu', () => {\n  getCanvasTest('cpu');\n});\n"
  },
  {
    "path": "test/features/get-pixels.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: getPixels');\n\nfunction getPixelsStandard(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    this.color(\n      v[this.thread.y][this.thread.x][0],\n      v[this.thread.y][this.thread.x][1],\n      v[this.thread.y][this.thread.x][2]\n    );\n  }, {\n    output: [2,2],\n    graphical: true,\n  });\n  kernel([\n    [\n      [.02,.04,.06,.08],\n      [.10,.12,.14,.16]\n    ],\n    [\n      [.18,.20,.22,.24],\n      [.26,.28,.30,.32]\n    ]\n  ]);\n  const pixels = Array.from(kernel.getPixels());\n  gpu.destroy();\n  return pixels;\n}\n\n\ntest('standard auto', () => {\n  const pixels = getPixelsStandard();\n  assert.deepEqual(pixels, [\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255\n  ]);\n});\n\ntest('standard gpu', () => {\n  const pixels = getPixelsStandard('gpu');\n  assert.deepEqual(pixels, [\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255\n  ]);\n});\n\n(GPU.isWebGLSupported ? test : skip)('standard webgl', () => {\n  const pixels = getPixelsStandard('webgl');\n  assert.deepEqual(pixels, [\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255\n  ]);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('standard webgl2', () => {\n  const pixels = getPixelsStandard('webgl2');\n  assert.deepEqual(pixels, [\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255\n  ]);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('standard headlessgl', () => {\n  const pixels = getPixelsStandard('headlessgl');\n  assert.deepEqual(pixels, [\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255\n  ]);\n});\n\n(GPU.isCanvasSupported ? test : skip)('standard cpu', () => {\n  const pixels = getPixelsStandard('cpu');\n  assert.deepEqual(pixels, [\n    45,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255,\n    5,\n    10,\n    15,\n    255,\n    25,\n    30,\n    35,\n    255\n  ]);\n});\n\n\nfunction getPixelsFlipped(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    this.color(\n      v[this.thread.y][this.thread.x][0],\n      v[this.thread.y][this.thread.x][1],\n      v[this.thread.y][this.thread.x][2]\n    );\n  }, {\n    output: [2,2],\n    graphical: true,\n  });\n  kernel([\n    [\n      [.02,.04,.06,.08],\n      [.10,.12,.14,.16]\n    ],\n    [\n      [.18,.20,.22,.24],\n      [.26,.28,.30,.32]\n    ]\n  ]);\n  const pixels = Array.from(kernel.getPixels(true));\n  gpu.destroy();\n  return pixels;\n}\n\n\ntest('flipped auto', () => {\n  const pixels = getPixelsFlipped();\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255,\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n\ntest('flipped gpu', () => {\n  const pixels = getPixelsFlipped('gpu');\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255,\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n\n(GPU.isWebGLSupported ? test : skip)('flipped webgl', () => {\n  const pixels = getPixelsFlipped('webgl');\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255,\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('flipped webgl2', () => {\n  const pixels = getPixelsFlipped('webgl2');\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255,\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('flipped headlessgl', () => {\n  const pixels = getPixelsFlipped('headlessgl');\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    31,\n    36,\n    255,\n    46,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n\n(GPU.isCanvasSupported ? test : skip)('flipped cpu', () => {\n  const pixels = getPixelsFlipped('cpu');\n  assert.deepEqual(pixels, [\n    5,\n    10,\n    15,\n    255,\n    25,\n    30,\n    35,\n    255,\n    45,\n    51,\n    56,\n    255,\n    66,\n    71,\n    76,\n    255\n  ]);\n});\n"
  },
  {
    "path": "test/features/if-else.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('if else boolean');\nfunction ifElseBooleanTest(mode) {\n  const gpu = new GPU({\n    mode\n  });\n  const f = gpu.createKernel(function() {\n    let result = 0;\n    if (true) {\n      result = 4;\n    } else {\n      result = 2;\n    }\n    return result;\n  }, {\n    output : [1]\n  });\n\n  assert.ok( f !== null, 'function generated test');\n  assert.equal(f()[0], 4, 'basic return function test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  ifElseBooleanTest(null);\n});\n\ntest('gpu', () => {\n  ifElseBooleanTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  ifElseBooleanTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  ifElseBooleanTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  ifElseBooleanTest('headlessgl');\n});\n\ntest('cpu', () => {\n  ifElseBooleanTest('cpu');\n});\n\n\ndescribe('if else lookup');\nfunction ifElseLookupTest( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(x) {\n    if (x[this.thread.x] > 0) {\n      return 0;\n    } else {\n      return 1;\n    }\n  }, {\n    output : [4]\n  });\n\n  assert.ok( f !== null, 'function generated test');\n  assert.deepEqual(Array.from(f([1, 1, 0, 0])), [0, 0, 1, 1], 'basic return function test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  ifElseLookupTest(null);\n});\n\ntest('gpu', () => {\n  ifElseLookupTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  ifElseLookupTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  ifElseLookupTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  ifElseLookupTest('headlessgl');\n});\n\ntest('cpu', () => {\n  ifElseLookupTest('cpu');\n});\n\ndescribe('if else if');\nfunction ifElseIfTest( mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(x) {\n    const v = x[this.thread.x];\n    if (v > 0) {\n      return 0;\n    } else if (v < 1) {\n      return .5;\n    }\n    return 1;\n  }, {\n    output : [2]\n  });\n  assert.deepEqual(Array.from(f([-1, 1])), [.5, 0], 'basic return function test');\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  ifElseIfTest(null);\n});\n\ntest('gpu', () => {\n  ifElseIfTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  ifElseIfTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  ifElseIfTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  ifElseIfTest('headlessgl');\n});\n\ntest('cpu', () => {\n  ifElseIfTest('cpu');\n});"
  },
  {
    "path": "test/features/image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, CPUKernel } = require('../../src');\n\ndescribe('features: image array');\nfunction getImages(callback) {\n  const imageSources = [\n    'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABbAIoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDtjB/vL/wHP8qUBk//AGv8anjEr/7L/wC1z/hUhib+Lb/wHj+dfQOfc893WjK/+8u3/a6f5/OkU7Plb7nZun4Yqx5TJ93/ANB/w4pvkb/67cH8xS5kNW6htpCtRm2b7yv9duf1BNO2/wB75f8AaXj+dO4uVdGBWkI2UpXZ/Ezf72Qf/r1KsS/eXbRzE7Fb/d3Uv/AW/n/KrOyneXRzCKqbX+X/AMd6H8quww76jMKv97/P0qxC2z5W/BvX/wCvWc5aaAONtUDxbKtvLUON9RFvqBGq1JipAlCKzPuocihM06nrFsepNiVDkgKRs4n/AINv+7x/KmeTKn/LVvbdhh/iPzq3HJ/C303difT2PtU+2jna3HzSW5n/AL9PvKrf7uRSM8T/AOsiZf8Aa6/qKumHZ/q/l/2e3/1qbtV/9l/896OdBzLsVBEr/wCrfd/P86Ywli+8m5P5/wBKtPaL97b/AOyn8xTNsqfxbv8AZk4P5iq5gKwVX+aP7v8Ad/wBpEj2fd+51G3t6jB6VK6qn96Dd9Cuf5VG4/iWXa/5girTCxIP++v93r+VPUK/3agllaL5ZEX/AHucfgR0/HFSfvfvSbV9JFbj8Tjp+GKTDke5NspfL/haoVafzPmTcvZl53epA46VaRt393/DPTI7VDdhONiHbs+99z+96fX/ABqZVqbb/eqJJET5V3MvT2B9MmpcriJVjp0UVBbZHuZ4ok/vNz/hUIv7T7v21V/4DgfqKj3nsVYsiOneXTVli8vct1Gyd23KQPyxVT+29MHB1C0z/vCpipS2RSpyeyuUfKn/AIrhW/2vN4+hAUfrSJcT277ZNzJ2aPLAe3DVvgU14ll+WRVb/eXNae27o1VZdUY6ao3/ADyZk6D5GyfXHAx3/KnNqcb/AHopFbt0q81rsj2wttX/AJ5su5fy6j8KhO6L70W3/aVuD+fT86FKD2QOVJ7Rt8yuupx/db7/AF+6w/QrUy3cD/xr/wB9L/LOaZJEv3vs7f8AfJX9QSDVbyf+mUsUq9GVcj+fT2xVpRaIcab2v+BZNxbfd81f93qPyqu/2R/vSqqdmVuP/rfhT992ke1oo2/LP5cGkjWN/mkRWf8A2sqB+GMU1pqCiluQW9xI++NWgliVsDc4BYeuP0/ClS4+zx7Wib5ePlbr+Jx+tPDRyu/z7Zdxxtw2AMDucY46U77b5Ue5pYv9tVz26jB/pVPXoW4puyiPivYn/dq67P7sjBSPT6j3p8vyfeZV7/T15/z+fXOn1LTZZ/IbazbciaFOPx/qKqSXdy8nl2Hn/d5ZWyOuON3GKaotvt6m0MJKT2t67fedLDJE3zM3zc/MzZzjuO1NnuILe3/eN8zNkKq5ZmJyAB3Oa5wJ4it/lWJvK7fKGwPfr/Wov7H1O7f7RLcbZW6qzFcD0yP8MUfV4p+9JWN44CnH3qtVW8tWyxqOptLOvmI3m9Et1+bae+e2ahSy1K4/1zrAjdFVQT/WtLSxY6Z/q5W+0N/E0YbJ7gEdunetq2n1C4j+7Ay/7WVP5ZNVKt7NWgtPMU50qWlKK9WvyT0XzuctZ6TbS3yW0zK0rZKSN82SOcY7cZP4Vq/8I1EON1vx/sf/AF6uXtg1xG3k/u72P50WT2PUH/PXnFU18QQKoEthfrIBhgucA98VDr1Z6wZzTq1amrkzeFLSZozXCcgU1qUmmk1SEQtAv/LNmj/3en5VBI86fet/N/2o8fqDVyirT7jUrbq5nm+g8v8AiXsVkXp6+1RM8cv+p2+zbsEfQdPxP5GrV3brcSRfdV1yQ20Hkdv1P5Uxo28zbG8ay/7K8D684P061onG2hsvZ2utyv5WyPy47hW7/vMce+fWiJInkaHarfxbmYnr1AHIJzzn3qd1kf8Ac/uvvAlmXIb/AOv7fSnppkH3m+ZmxnaoUce3+etHOurHzJLViT2FpcQeXPZfJ1+7nB+oOazDoMtvJ5lle/PtIEMnAxnIHP5fia1xYKnzR7v93eRT/Lb7q3Esbf3ZMOp/E/40o1ZR0i9P69S6eInBcqlp2ev+ZXtNQlidYbuLypePl3AZPsc/596qXl0t7fPpUCSLt5uWXB2L1wMHnPcf1q9cRK8Hk3turRd/Qe4J+6fyrGutMl0+f7bDcSz2rYjkXzCrBeylhzjOBVU1Byvs+na5rh40nJuTs7adrmgkuiW/7mPy1+bDxqpYkHvjB74psWtLFO26KSW328MyZdPw5LD9frVhYrN7RGWw2pt3Iy4baRzkHr9akNnBLI+37/de49x6/wA/rUc0Nea7MZNdE/mWSIru0intLhZEZhsZW3D069vQj3NaCSRuit5mMgHBcAj8O1chPb32j3bzWn325kj/AIJhjrjs3vV3+3NGk+eS3cSNyw9+9TPDSaThqvIajfWJqbqN1V91Lupcpxk2+mFqZmkJppAPzS7qizRmnYRIdr/K1AC+XtX5VqPNKDRYdxQv7xFX+4fvc9SP8DTsyp/DuT9R9PWmj/WbvoPyz/jUgNJjuPR9/wAy0/G/71Q7f4l+V/5/WpFl/hb5W/Q/Q1D8hj1h2f6v/vlun/1qrta7N7W/7p2Uh7duEf6H+E+4/EVcBp/3/lao5mjSLsc9pVxPdR3vk/K0bMGsmwAO3GehOCeuOfrWnDLFcbJFVliZTjdwUx1Xg5GPQ+/pWbqGns2oytC7xy7VeOTqVIHIyTypGePUdQcUmm3Uj3UsckTLKu57iPkkdBuHqcncD1IK9xW00pJzidkoKcXKHT8P815/f3NO5gk8t9v71dvCt1UnsD3z1H/165d7iNXZcKMHGGBz+NdbA2/zVb+HCv8ATkg/1+lYE5fz5N1pEzbjklevNVh52umRSV2y+Gpd1QrThWnKeeS5pM0ykNTYCTdSbqjpafKBJmlBqOlFKwEwNOBqEVIKmwEympAN/wArVCtSCs2UhwLRf7UX5lf8RVlTUK0ltwJVHRTwPTgGoaNER3yr5luzfKrN5b+2eVP/AH0B+dZ0nlJrtr527zZEeEMvthlP5bgfcVpX6hrJlIyPMTj/AIEKxNTZm1OzBJI+XjPq65rSlHm09TqoPX1Vvv2/EtCT+ytZSOT5re4XyQ3XZjkZz2+bH+cVbksJGldlkwCxIFV9SUSW8e4Z4iP6mr8Mj+TH87fdHf2pSm4pS6m0G9Gt+p//2Q==',\n    'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABbAIoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD1ZOlIwpwjKH5ehprSgHB69MelepfsecRkUgXmrBTofWkK01ICAimkVOVppFNSArkUhFSsvNMK1aYEZ60lPxzSEVQCdKM80YpD0pgLmlzTMU4daQEq9aD1pueKXPFSJjWptONNqkI1XYopwM+1ZbTrPqMUKnKqGdztI6cY/WobvxRpaSDM6SqOixHdmp9ClS8lkuVBHyBcHHBJye/T/AVmqcqcXOSOj2copuSNIAMBjtQV4qQL+8agisLmViAik21MVppWqTEV2WmMOKsEVGRzVJiK5FNIqZhzSYrRMCEikxUpWm7aaYEeKMYp+KQincBmeaN1KRSYqgGlqN1JRTsIqXfhsSZRYFPGd6DBH0rJhkuNPu3t5Lp7eKVhIGiG0E+/p7jpXf2kkc8qskqSxmPcsq9GzzzjjtWRrmlrdNIURfMABB9Mc5/Wpo4tuXJU2OyFSSTiyxpF+9yXguAolxuVlz849a0ytcTo98YxFJ/zwcDj+6ex/X8hXdFRj5elc+Kp+znoYNFcimkVORTCKwUiWiAioytTkc0xulaJksgIpmOamNMIq0xDCKbjmpaaasCIimkU800mqTGMIppFPJqMmqQhpFJQTTc1aAmvdNm065e901QV8zf5QOOeCQfY5P0/GtCG+gvXt7tCyjzfLcNwUJXGD75wPzq9Yyx3mno4fzA2cuv16/yrldRl/sW8unLAxylSUK91wcj8MVyQvWfJL4kd9OnKpLkS1M+ytZItSvtKRdzm4ba57KST+g5rvYohFCsakkKAuT14rlvC8iXur3d7MrfaG+58pIVTyeex6V1veqxs25qL6Wv62Lx1P2dVxe+79X28iIjmo261M1RN1rlRwsjbpUZqRqibpWqIYw0w0400mrQhKaaU00mrQDDTG609jxUZq0Ah6VGaeTTD1q0AxutMp7dKZViuW9JZoAzW2N4Yb0B4cHlWGeM9vy5FUdens7/UrON0DYfLg9kwCQfQ5Q/5xUunTJY395A2VDLuT/fX5vyP+etYWqqxuftoYbp5d7D+7jp/n2qKdK9Zy+75n0eXUU6972009Wv0Op8IKg0yVtipI0zFgO3oPpXQGuV0W7ijsoDE8aXRzvhkfb5uScEH1x+fFbC6oNxSS0nVwOV4JH4Z/kPxrjxEJSqyZw4yEpVpNLqXmqJqSK5juY1lifzEbo3+eh7Y60jNWSTOCWmjI2qJjzTnaoWat4ozYrHimE8UwtzTS9aJCHE03PNNL00tVJAOJphNGaQmrSEIaYad3ppqgGt0plONNqhMj0KJtRivL6fDPHCYVPYNjn/PvVLWopI9Qa3ZT5Yyyt65A/rmtLwwxWDVQMAC6bAA6dR/ICqeruxuYWJyTbqD+lSpNVpW2t+iPrcNFxxcoLZbeXYuaalo2jW0N1DFLIqsoS4j4kwx6Ng/5B/HHv7/AM27SztbYxIxWMIH6OxAHI6rzn/gNWoWIvIwDgc8dv4/8BWAzM9/ZsScmRiefdv8BV0IKTbl1uzVYVqdSpe6ScrPbq9vkek2dt9ktI4twcgEs+MbmJyT9Sc5pzmnn7oqN+lecrt3Z8nOTk231IHNQualeq79a3ijMaTUZahqjNapCuPzSZplFVYB+aM0ylHWiwD6aaKQ9KAGmm0402qEz//Z',\n    'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABbAIoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD0hXp4eqSyVIJK91wPKuWt1IXqDfSF/ep5QuT76duqtupd9HKFyxvpQ9Vw9OD0uUZZDVKGqoHqRXqHEC2pqQGqqvUivWUojLamkY+XIJR904V/p61ErVJkFdp+6QQfes2jRMW+kEVo7tx04988VhyLHcC4mkYpA5MSuW6Lv4bPb7zc/wC7VLV9YeRjp8SgiEMZ2J+8AOAD2B459xUU93A0VuTM08QBLRxJsRD0wB/U8/06KdKUUu7PYw+BlyRk9L6/Lf8AHpc0xembS4ZJZf3shViUH3QoLYPpzkYpiapqGxfLmjEePlB647VTd2usxoX2KqbS5yAB6Z6dKP7JRvmJ5PJwTT5Yx+I744OnbXS/lciWWpFkx3rPElPEpr0XTPkLl/zaPNqj5h9ad5lT7MLl7zKXfVIS04Se9LkC5cD04PVMS04SVLgO5dD1Kr+9UBJUokqHAdy8slSq9UFkqRZKylAo0FkqUPWeslTLJWUoDTKusaJaalHK4UR3TKQJk4J4wN394fWuVsgyyLbTR+XMp2lC2fMXoefX+tdur4rK1vQ7fWIw2fKuUHyTL1z7j/J461pTqSUeRu1+vb/gHtZbmfsX7OtrD77enl5EdjLDkW0kmzJxBN0Vz2GTwGH93vj3qx/ZMg/vD2S9mRfwUAgD2HArkptB8SQhlWWO4U8ZD4L+mc4Bx75PvVddL1xVAGnSAAYAEygCuSphJyd1JM+gj9Vqe8q8V80v1RoCTNOD1TV6f5lfRuB+elrzDR5lVfMo8ylyCLokpwlqiJKd5nvR7Mdy6JPepBJ71QElSBz61DgO5fV6kElUBJTxLWbgVc0VkqRZKzllqVZazdMdzRWSpBL71nCanib3rN0x3NIS+9O833rOEvvTxL71Hsyrl7ePp+FLvH91aoed707zvep9mO5xCy0/zazxLUgkr6F0zluXPMpfNqn5lKJaXIFy6JKeHqkHp4kqeQZbElPWSqe+nK9Q4AXhJThJVMS04SVDgVcvCWnrLVASe9SCT3qHAdy+JaeJKoLJ71KJKhwGXRKfWneafWqe+jzPeo5ALnmn1pfO96oiSjzKXswuccstSCWqCye9SCSvecDkUy55lKJKqeZTg9TyFcxeWSnB6piSnCT3qHAq5c8yniSqQkp4epcCrlwPTvMqoJKXzKjkGXA59aeJPeqQk96cJKlwGXlkqUSe9UFkp4lqHAdy/wCbS+Z71S8yl8yp5BlzzKPMqp5opfMpcgHFLIakEtVQaXJr3nFHjqbRcEtOEtVATTgT61DgaKoy8JaUSe9Ugx9akyazcDRVLlwSe9PEnvVME+tPBPrU8pqplsSe9P8AMqmCfWnAn1qXEtSLYc+tOElVQT604E1HKirlsSVIJPeqgJpwJ9ankKuWhL707zaqAn1pcn1qeVDuWxJS+ZVTJ9aXJ9aXIO5//9k=',\n    'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABbAIoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDso4Esrr7RHsZSNrxyLuBx+owO49a0I7TzTuZcBsYwenoMn3oht44pUiuD+7mbYjnsxH3T6Z7fl1xlYrgtp1vCcieK6jgmz2ZXGfzwPzrvxGLV/c37nvQi4LR3emv9dinqK2tgmnsWBAY/Me6iNyST9AaybCQ6l4isYYlVfs4Bk2jhcrl8+vBA+rVjaxrPn+XAmGREIRAfvs5zj/vnaPo9dt4T0Q6VYefOCbudfnLdVGcgfXkk+7H2rSipQp+0n8jbMZrCYV8/xSul+Ov3M6Qtkf0qJzSlqhdhWcUfFEcjVWdqfI4qu710wiSxGamFqQtTC1bJEkm6jNRbqXdTsFyTNAPNR7qN1FguTbhQTUJak30coXHlqTdUZam7qqwmySS7RLV7XV12RkYDn5WHPGcdccHcMjjnb0HLaz4m8tntICJJ5fLBeLncyMNsg92XA9iuPepLfRdeuI2gldkt3Uqy3M5kH5YOfzArc0XwtYaPL9oI+0XZBBmk6jPXA7fz968iGEad5s+5q47BYNOXMpy6JbfN9PlfoUfCfhM2hTUdTTNx96OI8hM9z7+3auy3gZ96g34Hv60xpa62nLc+UxmNq4yq6tZ6/gvJE7SVA8tRNL71A8laRpnK2Pkkquz80xpOetQu9dEYENkxems/FV95z1prSVooE3J99OD1TMlKH96fIFy5vpN9VvMo3+9HIO5OXpPMquZKb5lNQFctb6TeKreZRv8AenyBc6EyZxxjHpSGQVR873pDL71yezNLlxpKheWq5l96ieWrjTE2TtL71C0tQNLULS1tGmS2TtJUTSe9QNLURkraMCWyw0mKYZarmSmb6tQFcsmSgSVVL+9AkquQLlvzKXzKqiSl8z3pcg7lgyc0hkqsXpN9CgJss+ZR5nvVUyUnmGq5BGuZuOtJ5x9ap+ZSebWKpl3LZlPrTGl96rGT3phk96pQAnaWojJzUReomf3rRQE2TNJUZkqJpKiaStFAlk5kppfiq5kpDJV8gix5lAeq2+jzKfIBbD8UGSq3mcUnmUuQZZ8yk8yq++ml+aagBYL0nmVWaSm+YapQIuavmUhkqt5lMMtZchsWjJ70wye9VjJTTJVqAiwZPeo2k96rmSmGSqUBMnZ6jL1C0nFMMlaKBLZMXpu+oC9NL8VagS2WfMo8yqvmUoejkFct+ZRvqrvo8yjkKuWt9NL1X300yUKAXJzJTfMqu0nNM8z3q1AycjTMlNMnvVfJ9aTJ9ajlRvcn8zmkMlV8nPWkJPrT5RNkxkphlqBic9aYSfWrUCHImMlMMlQkn1qNic9atRRm5k5k96aZarkn1ppJ9atQM3ULHm0ol96pkn1pyk+tHIJVC4JPel82quTjrRk0uVFqZZMtMMtQEn1qMk+tNQIlUJ2lpnmmoCTnrTc1ooHPKo2z/9k='\n  ];\n  const images = [];\n  let finished = 0;\n  function onload() {\n    finished++;\n    if (finished === imageSources.length) {\n      callback(images);\n    }\n  }\n  for (let i = 0; i < imageSources.length; i++) {\n    const image = document.createElement('img');\n    image.onload = onload;\n    image.src = imageSources[i];\n    images.push(image);\n  }\n}\n\nfunction imageArrayTest(mode, done) {\n  const gpu = new GPU({\n    mode\n  });\n  const imageKernel = gpu.createKernel(function(images) {\n    const pixel = images[this.thread.z][this.thread.y][this.thread.x];\n    this.color(pixel[0], pixel[1], pixel[2], pixel[3]);\n  }, {\n    graphical: true,\n    output : [138, 91]\n  });\n  getImages(function(images) {\n    imageKernel(images);\n    const pixels = imageKernel.getPixels();\n    assert.equal(pixels.length, 50232);\n    // way too large to test the whole picture, just test the first pixel\n    assert.equal(pixels[0], 147);\n    assert.equal(pixels[1], 168);\n    assert.equal(pixels[2], 251);\n    assert.equal(pixels[3], 255);\n    gpu.destroy();\n    done(imageKernel);\n  });\n}\n\n(typeof Image !== 'undefined' ? test : skip)('image array auto', t => {\n  imageArrayTest(null, t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('image array gpu', t => {\n  imageArrayTest('gpu', t.async());\n});\n\n(GPU.isWebGLSupported ? test : skip)('image array webgl', t => {\n  const done = t.async();\n  imageArrayTest('webgl', kernel => {\n    // They aren't supported, so test that kernel falls back\n    assert.equal(kernel.kernel.constructor, CPUKernel);\n    done();\n  });\n});\n\n(GPU.isWebGL2Supported ? test : skip)('image array webgl2', t => {\n  imageArrayTest('webgl2', t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('image array cpu', t => {\n  imageArrayTest('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/image.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('image');\nfunction imageArgumentTest(mode, done) {\n  const gpu = new GPU({ mode });\n  const image = document.createElement('img');\n  image.src = 'jellyfish-1.jpeg';\n  image.onload = function() {\n    const imageKernel = gpu.createKernel(function(image) {\n      const pixel = image[this.thread.y][this.thread.x];\n      this.color(pixel[0], pixel[1], pixel[2], pixel[3]);\n    }, {\n      graphical: true,\n      output : [image.width, image.height]\n    });\n    imageKernel(image);\n    assert.equal(true, true, 'does not throw');\n    gpu.destroy();\n    done();\n  };\n}\n\n(typeof Image !== 'undefined' ? test : skip)('image argument auto', t => {\n  imageArgumentTest(null, t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('image argument gpu', t => {\n  imageArgumentTest('gpu', t.async());\n});\n\n(GPU.isWebGLSupported && typeof Image !== 'undefined' ? test : skip)('image argument webgl', t => {\n  imageArgumentTest('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported && typeof Image !== 'undefined' ? test : skip)('image argument webgl2', t => {\n  imageArgumentTest('webgl2', t.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('image argument cpu', t => {\n  imageArgumentTest('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/infinity.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('infinity');\nfunction inputWithoutFloat(checks, mode) {\n  const gpu = new GPU({ mode });\n  checks(gpu.createKernel(function() {\n    return Infinity;\n  }, { precision: 'unsigned' })\n    .setOutput([1])());\n  gpu.destroy();\n}\n\ntest(\"Infinity without float auto\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], NaN));\n});\n\ntest(\"Infinity without float cpu\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], Infinity), 'cpu');\n});\n\ntest(\"Infinity without float gpu\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], NaN), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"Infinity without float webgl\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], NaN), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"Infinity without float webgl2\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], NaN), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"Infinity without float headlessgl\", () => {\n  inputWithoutFloat((v) => assert.deepEqual(v[0], NaN), 'headlessgl');\n});\n\n\nfunction inputWithFloat(checks, mode) {\n  const gpu = new GPU({ mode });\n  checks(gpu.createKernel(function() {\n    return Infinity;\n  }, { precision: 'single' })\n    .setOutput([1])());\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)(\"Infinity with float auto\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], 3.4028234663852886e+38));\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)(\"Infinity with float cpu\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], Infinity), 'cpu');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)(\"Infinity with float gpu\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], 3.4028234663852886e+38), 'gpu');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isWebGLSupported ? test : skip)(\"Infinity with float webgl\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], 3.4028234663852886e+38), 'webgl');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isWebGL2Supported ? test : skip)(\"Infinity with float webgl2\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], 3.4028234663852886e+38), 'webgl2');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isHeadlessGLSupported ? test : skip)(\"Infinity with float headlessgl\", () => {\n  inputWithFloat((v) => assert.deepEqual(v[0], 3.4028234663852886e+38), 'headlessgl');\n});\n"
  },
  {
    "path": "test/features/inject-native.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: inject native');\n\nfunction gpuAddAB(mode) {\n  const gpu = new GPU({mode});\n  gpu\n    .injectNative(`\nint customAdder(int a, int b) {\n  return a + b;\n}  \n`)\n    .addNativeFunction('customAdderLink', `int customAdderLink(int a, int b) {\n  return customAdder(a, b);\n}`);\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdderLink(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6],\n    returnType: 'Integer'\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('addAB auto', () => {\n  gpuAddAB(null);\n});\n\ntest('addAB gpu', () => {\n  gpuAddAB('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('addAB webgl', () => {\n  gpuAddAB('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('addAB webgl2', () => {\n  gpuAddAB('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('addAB headlessgl', () => {\n  gpuAddAB('headlessgl');\n});\n\nfunction cpuAddAB(mode) {\n  function customAdder(a, b) {\n    return a + b;\n  }\n  const gpu = new GPU({mode});\n  gpu\n    .injectNative(customAdder.toString());\n  const kernel = gpu.createKernel(function (a, b) {\n    return customAdder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output: [6],\n    returnType: 'Integer'\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const result = kernel(a, b);\n\n  const expected = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(result), expected);\n  gpu.destroy();\n}\n\ntest('addAB cpu', () => {\n  cpuAddAB('cpu');\n});\n"
  },
  {
    "path": "test/features/input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../src');\n\ndescribe('input');\n\nfunction inputX(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  })\n    .setOutput([9]);\n\n  const a = new Float32Array(9);\n  a.set([1,2,3,4,5,6,7,8,9]);\n\n  const result = kernel(input(a, [3, 3]));\n  assert.deepEqual(Array.from(result), [1,2,3,4,5,6,7,8,9]);\n  gpu.destroy();\n}\n\ntest(\"inputX auto\", () => {\n  inputX();\n});\n\ntest(\"inputX gpu\", () => {\n  inputX('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputX webgl\", () => {\n  inputX('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputX webgl2\", () => {\n  inputX('webgl2');\n});\n\ntest(\"inputX cpu\", () => {\n  inputX('cpu');\n});\n\n\nfunction inputXY(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.y][this.thread.x];\n  })\n    .setOutput([9]);\n\n  const a = new Float32Array(9);\n  a.set([1,2,3,4,5,6,7,8,9]);\n\n  const b = new Float32Array(9);\n  b.set([1,2,3,4,5,6,7,8,9]);\n\n  const result = kernel(input(a, [3, 3]));\n  assert.deepEqual(Array.from(result), [1,2,3,4,5,6,7,8,9]);\n  gpu.destroy();\n}\n\ntest(\"inputXY auto\", () => {\n  inputXY();\n});\n\ntest(\"inputXY gpu\", () => {\n  inputXY('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputXY webgl\", () => {\n  inputXY('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputXY webgl2\", () => {\n  inputXY('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputXY headlessgl\", () => {\n  inputXY('headlessgl');\n});\n\ntest(\"inputXY cpu\", () => {\n  inputXY('cpu');\n});\n\nfunction inputYX(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.y][this.thread.x];\n  })\n    .setOutput([3, 3]);\n\n  const a = new Float32Array(9);\n  a.set([1,2,3,4,5,6,7,8,9]);\n\n  const result = kernel(input(a, [3, 3]));\n  assert.deepEqual(result.map(function(v) { return Array.from(v); }), [[1,2,3],[4,5,6],[7,8,9]]);\n  gpu.destroy();\n}\n\ntest(\"inputYX auto\", () => {\n  inputYX();\n});\n\ntest(\"inputYX gpu\", () => {\n  inputYX('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputYX webgl\", () => {\n  inputYX('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputYX webgl2\", () => {\n  inputYX('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputYX headlessgl\", () => {\n  inputYX('headlessgl');\n});\n\ntest(\"inputYX cpu\", () => {\n  inputYX('cpu');\n});\n\nfunction inputYXOffset(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.x][this.thread.y];\n  })\n    .setOutput([8, 2]);\n\n  const a = new Float32Array(16);\n  a.set([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);\n\n  const result = kernel(input(a, [2, 8]));\n  assert.deepEqual(result.map(function(v) { return Array.from(v); }), [[1,3,5,7,9,11,13,15],[2,4,6,8,10,12,14,16]]);\n  gpu.destroy();\n}\n\ntest(\"inputYXOffset auto\", () => {\n  inputYXOffset();\n});\n\ntest(\"inputYXOffset gpu\", () => {\n  inputYXOffset('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputYXOffset webgl\", () => {\n  inputYXOffset('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputYXOffset webgl2\", () => {\n  inputYXOffset('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputYXOffset headlessgl\", () => {\n  inputYXOffset('headlessgl');\n});\n\ntest(\"inputYXOffset cpu\", () => {\n  inputYXOffset('cpu');\n});\n\nfunction inputYXOffsetPlus1(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.x][this.thread.y];\n  })\n    .setOutput([2, 8]);\n\n  const a = new Float32Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);\n\n  const result = kernel(input(a, [8, 2]));\n  assert.deepEqual(result.map(function(v) { return Array.from(v); }), [[1,9],[2,10],[3,11],[4,12],[5,13],[6,14],[7,15],[8,16]]);\n  gpu.destroy();\n}\n\ntest(\"inputYXOffsetPlus1 auto\", () => {\n  inputYXOffsetPlus1();\n});\n\ntest(\"inputYXOffsetPlus1 gpu\", () => {\n  inputYXOffsetPlus1('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputYXOffsetPlus1 webgl\", () => {\n  inputYXOffsetPlus1('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputYXOffsetPlus1 webgl2\", () => {\n  inputYXOffsetPlus1('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputYXOffsetPlus1 headlessgl\", () => {\n  inputYXOffsetPlus1('headlessgl');\n});\n\ntest(\"inputYXOffsetPlus1 cpu\", () => {\n  inputYXOffsetPlus1('cpu');\n});\n\nfunction inputZYX(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setOutput([2, 4, 4]);\n\n  const a = new Float32Array(32);\n  a.set([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]);\n\n  const result = kernel(input(a, [2, 4, 4]));\n  assert.deepEqual(result.map(function(v) { return v.map(function(v) { return Array.from(v); }); }), [[[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]]]);\n  gpu.destroy();\n}\n\ntest(\"inputZYX auto\", () => {\n  inputZYX();\n});\n\ntest(\"inputZYX gpu\", () => {\n  inputZYX('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputZYX webgl\", () => {\n  inputZYX('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputZYX webgl2\", () => {\n  inputZYX('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputZYX headlessgl\", () => {\n  inputZYX('headlessgl');\n});\n\ntest(\"inputZYX cpu\", () => {\n  inputZYX('cpu');\n});\n\n\nfunction inputZYXVariables(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a, x, y, z) {\n    return a[z][y][x];\n  })\n    .setOutput([1]);\n\n  const a = new Float32Array(32);\n  a.set([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]);\n  const aInput = input(a, [2, 4, 4]);\n  assert.deepEqual(Array.from(kernel(aInput, 1, 2, 3)), [30]);\n  assert.deepEqual(Array.from(kernel(aInput, 0, 2, 3)), [29]);\n  assert.deepEqual(Array.from(kernel(aInput, 0, 2, 1)), [13]);\n  assert.deepEqual(Array.from(kernel(aInput, 1, 2, 2)), [22]);\n  assert.deepEqual(Array.from(kernel(aInput, 0, 2, 2)), [21]);\n  gpu.destroy();\n}\n\ntest(\"inputZYXVariables auto\", () => {\n  inputZYXVariables();\n});\n\ntest(\"inputZYXVariables gpu\", () => {\n  inputZYXVariables('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputZYXVariables webgl\", () => {\n  inputZYXVariables('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputZYXVariables webgl2\", () => {\n  inputZYXVariables('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"inputZYXVariables headlessgl\", () => {\n  inputZYXVariables('headlessgl');\n});\n\ntest(\"inputZYXVariables cpu\", () => {\n  inputZYXVariables('cpu');\n});\n\nfunction inputInt32ArrayX(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  })\n    .setPrecision('unsigned')\n    .setOutput([9]);\n\n  const a = new Int32Array([1,2,3,4,5,6,7,8,9]);\n  const result = kernel(input(a, [3, 3]));\n  assert.deepEqual(result, new Float32Array([1,2,3,4,5,6,7,8,9]));\n  gpu.destroy();\n}\n\ntest(\"inputInt32ArrayX auto\", () => {\n  inputInt32ArrayX();\n});\n\ntest(\"inputInt32ArrayX gpu\", () => {\n  inputInt32ArrayX('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"inputInt32ArrayX webgl\", () => {\n  inputInt32ArrayX('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"inputInt32ArrayX webgl2\", () => {\n  inputInt32ArrayX('webgl2');\n});\n\ntest(\"inputInt32ArrayX cpu\", () => {\n  inputInt32ArrayX('cpu');\n});\n\ntest('.toArray() with array', () => {\n  assert.deepEqual(input([1,2,3,4], [4]).toArray(), [1,2,3,4]);\n});\ntest('.toArray() with matrix', () => {\n  assert.deepEqual(input([1,2,3,4,5,6,7,8], [4,2]).toArray(), [new Float32Array([1,2,3,4]), new Float32Array([5,6,7,8])]);\n});\ntest('.toArray() with grid', () => {\n  assert.deepEqual(\n    input([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], [4,2,2]).toArray(),\n    [\n      [\n        new Float32Array([1,2,3,4]),\n        new Float32Array([5,6,7,8]),\n      ],\n      [\n        new Float32Array([9,10,11,12]),\n        new Float32Array([13,14,15,16])\n      ]\n    ]\n  );\n});\n"
  },
  {
    "path": "test/features/internally-defined-matrices.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: internally defined matrices');\n\nfunction testMatrix2(mode) {\n  const gpu = new GPU({ mode });\n  function getMatrix() {\n    const matrix = [\n      [1,2],\n      [3,4]\n    ];\n    return matrix;\n  }\n  gpu.addFunction(getMatrix);\n  const kernel = gpu.createKernel(function(y, x) {\n    return getMatrix()[y][x];\n  }, { output: [1] });\n\n  assert.equal(kernel(0, 0)[0], 1);\n  assert.equal(kernel(0, 1)[0], 2);\n  assert.equal(kernel(1, 0)[0], 3);\n  assert.equal(kernel(1, 1)[0], 4);\n\n  gpu.destroy();\n}\n\ntest('matrix2 auto', () => {\n  testMatrix2();\n});\n\ntest('matrix2 gpu', () => {\n  testMatrix2('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('matrix2 webgl', () => {\n  testMatrix2('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('matrix2 webgl2', () => {\n  testMatrix2('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('matrix2 headlessgl', () => {\n  testMatrix2('headlessgl');\n});\n\ntest('matrix2 cpu', () => {\n  testMatrix2('cpu');\n});\n\nfunction testMatrix3(mode) {\n  const gpu = new GPU({ mode });\n  function getMatrix() {\n    const matrix = [\n      [1,2,3],\n      [4,5,6],\n      [7,8,9],\n    ];\n    return matrix;\n  }\n  gpu.addFunction(getMatrix);\n  const kernel = gpu.createKernel(function(y, x) {\n    return getMatrix()[y][x];\n  }, { output: [1] });\n\n  assert.equal(kernel(0, 0)[0], 1);\n  assert.equal(kernel(0, 1)[0], 2);\n  assert.equal(kernel(0, 2)[0], 3);\n  assert.equal(kernel(1, 0)[0], 4);\n  assert.equal(kernel(1, 1)[0], 5);\n  assert.equal(kernel(1, 2)[0], 6);\n  assert.equal(kernel(2, 0)[0], 7);\n  assert.equal(kernel(2, 1)[0], 8);\n  assert.equal(kernel(2, 2)[0], 9);\n\n  gpu.destroy();\n}\n\ntest('matrix3 auto', () => {\n  testMatrix3();\n});\n\ntest('matrix3 gpu', () => {\n  testMatrix3('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('matrix3 webgl', () => {\n  testMatrix3('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('matrix3 webgl2', () => {\n  testMatrix3('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('matrix3 headlessgl', () => {\n  testMatrix3('headlessgl');\n});\n\ntest('matrix3 cpu', () => {\n  testMatrix3('cpu');\n});\n\nfunction testMatrix4(mode) {\n  const gpu = new GPU({ mode });\n  function getMatrix() {\n    const matrix = [\n      [1,2,3,4],\n      [5,6,7,8],\n      [9,10,11,12],\n      [13,14,15,16],\n    ];\n    return matrix;\n  }\n  gpu.addFunction(getMatrix);\n  const kernel = gpu.createKernel(function(y, x) {\n    return getMatrix()[y][x];\n  }, { output: [1] });\n\n  assert.equal(kernel(0, 0)[0], 1);\n  assert.equal(kernel(0, 1)[0], 2);\n  assert.equal(kernel(0, 2)[0], 3);\n  assert.equal(kernel(0, 3)[0], 4);\n  assert.equal(kernel(1, 0)[0], 5);\n  assert.equal(kernel(1, 1)[0], 6);\n  assert.equal(kernel(1, 2)[0], 7);\n  assert.equal(kernel(1, 3)[0], 8);\n  assert.equal(kernel(2, 0)[0], 9);\n  assert.equal(kernel(2, 1)[0], 10);\n  assert.equal(kernel(2, 2)[0], 11);\n  assert.equal(kernel(2, 3)[0], 12);\n  assert.equal(kernel(3, 0)[0], 13);\n  assert.equal(kernel(3, 1)[0], 14);\n  assert.equal(kernel(3, 2)[0], 15);\n  assert.equal(kernel(3, 3)[0], 16);\n\n  gpu.destroy();\n}\n\ntest('matrix4 auto', () => {\n  testMatrix4();\n});\n\ntest('matrix4 gpu', () => {\n  testMatrix4('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('matrix4 webgl', () => {\n  testMatrix4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('matrix4 webgl2', () => {\n  testMatrix4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('matrix4 headlessgl', () => {\n  testMatrix4('headlessgl');\n});\n\ntest('matrix4 cpu', () => {\n  testMatrix4('cpu');\n});\n"
  },
  {
    "path": "test/features/json.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('json serialize');\n\nfunction testJSONSerialize(mode) {\n  const gpu = new GPU({mode});\n\n  const kernel = gpu.createKernel(function (value) {\n    return value;\n  }, {output: [1]});\n\n  kernel(1);\n\n  const json = kernel.toJSON();\n\n  const jsonKernel = gpu.createKernel(json);\n\n  assert.equal(jsonKernel(3)[0], 3);\n}\n\ntest('auto', () => {\n  testJSONSerialize();\n});\n\ntest('gpu', () => {\n  testJSONSerialize('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testJSONSerialize('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testJSONSerialize('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testJSONSerialize('headlessgl');\n});\n\ntest('cpu', () => {\n  testJSONSerialize('cpu');\n});\n"
  },
  {
    "path": "test/features/legacy-encoder.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, HeadlessGLKernel, WebGLKernel, WebGL2Kernel } = require('../../src');\n\ndescribe('features: legacy encoder');\n\nfunction testLegacyEncoderOff(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, { output: [1], precision: 'unsigned' });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('off auto', () => {\n  testLegacyEncoderOff();\n});\n\n(GPU.isWebGLSupported ? test : skip)('off webgl', () => {\n  testLegacyEncoderOff('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('off webgl2', () => {\n  testLegacyEncoderOff('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('off headlessgl', () => {\n  testLegacyEncoderOff('headlessgl');\n});\n\ntest('off cpu', () => {\n  testLegacyEncoderOff('cpu');\n});\n\nfunction testLegacyEncoderOn(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, {\n    output: [1],\n    precision: 'unsigned',\n    useLegacyEncoder: true,\n  });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('on auto', () => {\n  testLegacyEncoderOn();\n});\n\n(GPU.isWebGLSupported ? test : skip)('on webgl', () => {\n  testLegacyEncoderOn('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('on webgl2', () => {\n  testLegacyEncoderOn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('on headlessgl', () => {\n  testLegacyEncoderOn('headlessgl');\n});\n\ntest('on cpu', () => {\n  testLegacyEncoderOn('cpu');\n});\n\nfunction testSubKernelsLegacyEncoderOff(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const kernel = gpu.createKernelMap([\n    addOne,\n  ], function() {\n    const result = addOne(1);\n    return result + 1;\n  }, { output: [1], precision: 'unsigned' });\n  assert.equal(kernel()[0][0], 2);\n  assert.equal(kernel().result[0], 3);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('subKernels off auto', () => {\n  testSubKernelsLegacyEncoderOff();\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('subKernels off webgl', () => {\n  testSubKernelsLegacyEncoderOff('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isKernelMapSupported ? test : skip)('subKernels off webgl2', () => {\n  testSubKernelsLegacyEncoderOff('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('subKernels off headlessgl', () => {\n  testSubKernelsLegacyEncoderOff('headlessgl');\n});\n\ntest('subKernels off cpu', () => {\n  testSubKernelsLegacyEncoderOff('cpu');\n});\n\nfunction testSubKernelsLegacyEncoderOn(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const kernel = gpu.createKernelMap([\n    addOne,\n  ], function() {\n    const value = addOne(1);\n    return value + 1;\n  }, {\n    output: [1],\n    precision: 'unsigned',\n    useLegacyEncoder: true,\n  });\n  assert.equal(kernel()[0][0], 2);\n  assert.equal(kernel().result[0], 3);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('subKernels on auto', () => {\n  testSubKernelsLegacyEncoderOn();\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('subKernels on webgl', () => {\n  testSubKernelsLegacyEncoderOn('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isKernelMapSupported ? test : skip)('subKernels on webgl2', () => {\n  testSubKernelsLegacyEncoderOn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('subKernels on headlessgl', () => {\n  testSubKernelsLegacyEncoderOn('headlessgl');\n});\n\ntest('subKernels on cpu', () => {\n  testSubKernelsLegacyEncoderOn('cpu');\n});\n\ntest('HeadlessGLKernel.getMainResultKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = HeadlessGLKernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: false\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  gl_FragData[0] = encode32(kernelResult);\n`);\n});\n\ntest('WebGLKernel.getMainResultKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = WebGLKernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: false\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  gl_FragData[0] = encode32(kernelResult);\n`);\n});\n\ntest('WebGL2Kernel.getMainResultKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = WebGL2Kernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: false\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  data0 = encode32(kernelResult);\n`);\n});\n\ntest('HeadlessGLKernel.getMainResultKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = HeadlessGLKernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: true\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  gl_FragData[0] = legacyEncode32(kernelResult);\n`);\n});\n\ntest('WebGLKernel.getMainResultKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = WebGLKernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: true\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  gl_FragData[0] = legacyEncode32(kernelResult);\n`);\n});\n\ntest('WebGL2Kernel.getMainResultKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = WebGL2Kernel.prototype.getMainResultKernelPackedPixels.apply({\n    useLegacyEncoder: true\n  });\n  assert.equal(result, `  threadId = indexTo3D(index, uOutputDim);\n  kernel();\n  data0 = legacyEncode32(kernelResult);\n`);\n});\n\ntest('HeadlessGLKernel.getMainResultSubKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = HeadlessGLKernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: false,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  gl_FragData[1] = encode32(subKernelResult_subKernel1);\n`);\n});\n\ntest('WebGLKernel.getMainResultSubKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = WebGLKernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: false,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  gl_FragData[1] = encode32(subKernelResult_subKernel1);\n`);\n});\n\ntest('WebGL2Kernel.getMainResultSubKernelPackedPixels useLegacyEncoder = false', () => {\n  const result = WebGL2Kernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: false,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  data1 = encode32(subKernelResult_subKernel1);\n`);\n});\n\ntest('HeadlessGLKernel.getMainResultSubKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = HeadlessGLKernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: true,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  gl_FragData[1] = legacyEncode32(subKernelResult_subKernel1);\n`);\n});\n\ntest('WebGLKernel.getMainResultSubKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = WebGLKernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: true,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  gl_FragData[1] = legacyEncode32(subKernelResult_subKernel1);\n`);\n});\n\ntest('WebGL2Kernel.getMainResultSubKernelPackedPixels useLegacyEncoder = true', () => {\n  const result = WebGL2Kernel.prototype.getMainResultSubKernelPackedPixels.apply({\n    useLegacyEncoder: true,\n    subKernels: [{\n      name: 'subKernel1'\n    }]\n  });\n  assert.equal(result, `  data1 = legacyEncode32(subKernelResult_subKernel1);\n`);\n});\n"
  },
  {
    "path": "test/features/loops.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('loops - for');\nfunction forLoopTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    for (let i = 0; i < 10; i++) {\n      x = x + 1;\n    }\n\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6]\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  forLoopTest(null);\n});\n\ntest('gpu', () => {\n  forLoopTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  forLoopTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  forLoopTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  forLoopTest('headlessgl');\n});\n\ntest('cpu', () => {\n  forLoopTest('cpu');\n});\n\n\ndescribe('loops - for with constant');\nfunction forWithConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    for(let i = 0; i < this.constants.max; i++) {\n      x = x + 1;\n    }\n\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6],\n    constants: {\n      max: 10\n    }\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n\n  assert.deepEqual(Array.from(res), exp);\n\n  gpu.destroy();\n}\n\ntest('forConstantLoopTest auto', () => {\n  forWithConstantTest(null);\n});\n\ntest('forConstantLoopTest gpu', () => {\n  forWithConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('forConstantLoopTest webgl', () => {\n  forWithConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('forConstantLoopTest webgl2', () => {\n  forWithConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('forConstantLoopTest headlessgl', () => {\n  forWithConstantTest('headlessgl');\n});\n\ntest('forConstantLoopTest cpu', () => {\n  forWithConstantTest('cpu');\n});\n\n\ndescribe('loops - while');\nfunction whileLoopTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    let i = 0;\n    while (i++ < 10) {\n      x = x + 1;\n    }\n\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6]\n  });\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  whileLoopTest(null);\n});\n\ntest('gpu', () => {\n  whileLoopTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  whileLoopTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  whileLoopTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  whileLoopTest('headlessgl');\n});\n\ntest('cpu', () => {\n  whileLoopTest('cpu');\n});\n\n\n\ndescribe('loops - while with constant');\nfunction whileWithConstantTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    let i = 0;\n    while (i++ < this.constants.max) {\n      x = x + 1;\n    }\n\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6],\n    constants: { max: 10 }\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  whileWithConstantTest(null);\n});\n\ntest('gpu', () => {\n  whileWithConstantTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  whileWithConstantTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  whileWithConstantTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  whileWithConstantTest('headlessgl');\n});\n\ntest('cpu', () => {\n  whileWithConstantTest('cpu');\n});\n\n\ndescribe('loops - evil while loop');\nfunction evilWhileLoopTest(mode ) {\n  function evilWhileKernelFunction(a, b) {\n    let x = 0;\n    let i = 0;\n\n    //10000000 or 10 million is the approx upper limit on a chrome + GTX 780\n    while(i<100) {\n      x = x + 1.0;\n      ++i;\n    }\n\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }\n\n  const evil_while_a = [1, 2, 3, 5, 6, 7];\n  const evil_while_b = [4, 5, 6, 1, 2, 3];\n  const evil_while_cpuRef = new GPU({ mode: 'cpu' });\n  const evil_while_cpuRef_f =  evil_while_cpuRef.createKernel(evilWhileKernelFunction, {\n    output : [6],\n    loopMaxIterations: 10000,\n  });\n\n  const evil_while_exp = evil_while_cpuRef_f(evil_while_a,evil_while_b);\n  const gpu = new GPU({ mode });\n\n  const f = gpu.createKernel(evilWhileKernelFunction, {\n    output : [6]\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const res = f(evil_while_a,evil_while_b);\n\n  for(let i = 0; i < evil_while_exp.length; ++i) {\n    assert.equal(evil_while_exp[i], res[i], 'Result arr idx: '+i);\n  }\n  evil_while_cpuRef.destroy();\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  evilWhileLoopTest(null);\n});\n\ntest('gpu', () => {\n  evilWhileLoopTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  evilWhileLoopTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  evilWhileLoopTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  evilWhileLoopTest('headlessgl');\n});\n\ntest('cpu', () => {\n  evilWhileLoopTest('cpu');\n});\n\ndescribe('loops - do while');\nfunction doWhileLoopTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    let i = 0;\n    do {\n      x = x + 1;\n      i++;\n    } while (i < 10);\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6]\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  doWhileLoopTest(null);\n});\n\ntest('gpu', () => {\n  doWhileLoopTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  doWhileLoopTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  doWhileLoopTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  doWhileLoopTest('headlessgl');\n});\n\ntest('cpu', () => {\n  doWhileLoopTest('cpu');\n});\n\ndescribe('loops - do while with constant');\nfunction doWhileWithConstantLoop(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    let x = 0;\n    let i = 0;\n    do {\n      x = x + 1;\n      i++;\n    } while (i < this.constants.max);\n    return (a[this.thread.x] + b[this.thread.x] + x);\n  }, {\n    output : [6],\n    constants: { max: 10 }\n  });\n\n  assert.ok( f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [15, 17, 19, 16, 18, 20];\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  doWhileWithConstantLoop(null);\n});\n\ntest('gpu', () => {\n  doWhileWithConstantLoop('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  doWhileWithConstantLoop('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  doWhileWithConstantLoop('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  doWhileWithConstantLoop('headlessgl');\n});\n\ntest('cpu', () => {\n  doWhileWithConstantLoop('cpu');\n});\n"
  },
  {
    "path": "test/features/math-object.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: math object');\n\nfunction mathProps(mode) {\n  const props = ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2'];\n  const gpu = new GPU({ mode });\n  for (let i = 0; i < props.length; i++) {\n    const prop = props[i];\n    const kernel = gpu.createKernel(`function() {\n      return Math.${prop};\n    }`, { output: [1] });\n    assert.equal(kernel()[0].toFixed(6), Math[prop].toFixed(6));\n  }\n  gpu.destroy();\n}\n\ntest('All Math properties auto', () => {\n  mathProps();\n});\n\ntest('All Math properties gpu', () => {\n  mathProps('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('All Math properties webgl', () => {\n  mathProps('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('All Math properties webgl2', () => {\n  mathProps('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('All Math properties headlessgl', () => {\n  mathProps('headlessgl');\n});\n\ntest('All Math properties cpu', () => {\n  mathProps('cpu');\n});\n\nfunction singleArgumentMathMethods(mode) {\n  const methods = [\n    'abs',\n    'acos',\n    'acosh',\n    'asin',\n    'asinh',\n    'atan',\n    'atanh',\n    'cbrt',\n    'ceil',\n    // 'clz32', // not supported, bits directly are hard\n    'cos',\n    'cosh',\n    'exp',\n    'expm1',\n    'floor',\n    'fround',\n    // 'hypot', // not supported, dynamically sized\n    'log',\n    'log10',\n    'log1p',\n    'log2',\n    'round',\n    'sign',\n    'sin',\n    'sinh',\n    'sqrt',\n    'tan',\n    'tanh',\n    'trunc',\n  ];\n\n  const gpu = new GPU({ mode });\n  for (let i = 0; i < methods.length; i++) {\n    const method = methods[i];\n    const kernel = gpu.createKernel(`function(value) {\n      return Math.${method}(value);\n    }`, { output: [1] });\n    for (let j = 0; j < 10; j++) {\n      assert.equal(kernel(j / 10)[0].toFixed(3), Math[method](j / 10).toFixed(3), `Math.${method}(${j / 10})`);\n    }\n  }\n  gpu.destroy();\n}\n\ntest('Single argument Math methods auto', () => {\n  singleArgumentMathMethods();\n});\n\ntest('Single argument Math methods gpu', () => {\n  singleArgumentMathMethods('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Single argument Math methods webgl', () => {\n  singleArgumentMathMethods('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Single argument Math methods webgl2', () => {\n  singleArgumentMathMethods('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Single argument Math methods headlessgl', () => {\n  singleArgumentMathMethods('headlessgl');\n});\n\ntest('Single argument Math methods cpu', () => {\n  singleArgumentMathMethods('cpu');\n});\n\nfunction twoArgumentMathMethods(mode) {\n  const methods = [\n    'atan2',\n    'imul',\n    'max',\n    'min',\n    'pow',\n  ];\n\n  const gpu = new GPU({ mode });\n  for (let i = 0; i < methods.length; i++) {\n    const method = methods[i];\n    const kernel = gpu.createKernel(`function(value1, value2) {\n      return Math.${method}(value1, value2);\n    }`, { output: [1] });\n    for (let j = 0; j < 10; j++) {\n      const value1 = j / 10;\n      const value2 = value1;\n      assert.equal(kernel(value1, value2)[0].toFixed(3), Math[method](value1, value2).toFixed(3), `Math.${method}(${value1}, ${value2})`);\n    }\n  }\n  gpu.destroy();\n}\n\ntest('Two argument Math methods auto', () => {\n  twoArgumentMathMethods();\n});\n\ntest('Two argument Math methods gpu', () => {\n  twoArgumentMathMethods('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Two argument Math methods webgl', () => {\n  twoArgumentMathMethods('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Two argument Math methods webgl2', () => {\n  twoArgumentMathMethods('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Two argument Math methods headlessgl', () => {\n  twoArgumentMathMethods('headlessgl');\n});\n\ntest('Two argument Math methods cpu', () => {\n  twoArgumentMathMethods('cpu');\n});\n\nfunction sqrtABTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    return Math.sqrt(a[this.thread.x] * b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n  const a = [3, 4, 5, 6, 7, 8];\n  const b = [3, 4, 5, 6, 7, 8];\n\n  const res = f(a,b);\n  const exp = [3, 4, 5, 6, 7, 8];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('sqrtAB auto', () => {\n  sqrtABTest(null);\n});\n\ntest('sqrtAB gpu', () => {\n  sqrtABTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('sqrtAB webgl', () => {\n  sqrtABTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('sqrtAB webgl2', () => {\n  sqrtABTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('sqrtAB headlessgl', () => {\n  sqrtABTest('headlessgl');\n});\n\ntest('sqrtAB cpu', () => {\n  sqrtABTest('cpu');\n});\n\n\nfunction mathRandom(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return Math.random();\n  }, { output: [1] });\n\n  const result = kernel();\n  assert.ok(result[0] > 0 && result[0] < 1, `value was expected to be between o and 1, but was ${result[0]}`);\n}\n\ntest('random auto', () => {\n  mathRandom();\n});\n\ntest('random gpu', () => {\n  mathRandom('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('random webgl', () => {\n  mathRandom('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('random webgl2', () => {\n  mathRandom('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('random headlessgl', () => {\n  mathRandom('headlessgl');\n});\n\ntest('random cpu', () => {\n  mathRandom('cpu');\n});\n\n\n"
  },
  {
    "path": "test/features/nested-function.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('nested function');\n\nfunction nestedSumABTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(function(a, b) {\n    function custom_adder(a,b) {\n      return a+b;\n    }\n\n    return custom_adder(a[this.thread.x], b[this.thread.x]);\n  }, {\n    output : [6]\n  });\n\n  assert.ok(f !== null, 'function generated test');\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n\n  const res = f(a,b);\n  const exp = [5, 7, 9, 6, 8, 10];\n\n  assert.deepEqual(Array.from(res), exp);\n  gpu.destroy();\n}\n\ntest('nested_sum auto', () => {\n  nestedSumABTest(null);\n});\n\ntest('nested_sum gpu', () => {\n  nestedSumABTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('nested_sum webgl', () => {\n  nestedSumABTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('nested_sum webgl2', () => {\n  nestedSumABTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('nested_sum headlessgl', () => {\n  nestedSumABTest('headlessgl');\n});\n\ntest('nested_sum cpu', () => {\n  nestedSumABTest('cpu');\n});\n\nfunction testNestedInCustomFunction(mode) {\n  function custom1() {\n    function nested1() {\n      return 1;\n    }\n\n    return nested1();\n  }\n  const gpu = new GPU({ mode });\n  gpu.addFunction(custom1);\n  const kernel = gpu.createKernel(function() {\n    return custom1();\n  }, { output: [1] });\n  assert.deepEqual(kernel(), new Float32Array([1]));\n  gpu.destroy();\n}\n\ntest('nested in custom auto', () => {\n  testNestedInCustomFunction();\n});\n\ntest('nested in custom gpu', () => {\n  testNestedInCustomFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('nested in custom webgl', () => {\n  testNestedInCustomFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('nested in custom webgl2', () => {\n  testNestedInCustomFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('nested in custom headlessgl', () => {\n  testNestedInCustomFunction('headlessgl');\n});\n\ntest('nested in custom cpu', () => {\n  testNestedInCustomFunction('cpu');\n});\n"
  },
  {
    "path": "test/features/offscreen-canvas.js",
    "content": "if (typeof importScripts !== 'undefined') {\n  // inside Worker\n  importScripts('../../dist/gpu-browser.js');\n  onmessage = function (e) {\n    const gpu = new GPU({ mode: e.data });\n    const a = [1,2,3];\n    const b = [3,2,1];\n    const kernel = gpu.createKernel(function(a, b) {\n      return a[this.thread.x] - b[this.thread.x];\n    })\n      .setOutput([3]);\n    postMessage({ mode: gpu.mode, result: kernel(a, b) });\n    gpu.destroy();\n  };\n} else if (typeof isBrowser !== 'undefined' && isBrowser) {\n  const { assert, skip, test, module: describe } = require('qunit');\n  describe('offscreen canvas');\n\n  function testOffscreenCanvas(mode, done) {\n    const worker = new Worker('features/offscreen-canvas.js');\n    worker.onmessage = function (e) {\n      const mode = e.data.mode;\n      const result = e.data.result;\n      assert.equal(mode, 'gpu', 'GPU mode used in Worker');\n      assert.deepEqual(result, Float32Array.from([-2, 0, 2]));\n      done();\n    };\n    worker.postMessage(mode);\n  }\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('offscreen canvas auto', t => {\n    testOffscreenCanvas(null, t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('offscreen canvas gpu', t => {\n    testOffscreenCanvas('gpu', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('offscreen canvas webgl', t => {\n    testOffscreenCanvas('webgl', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('offscreen canvas webgl2', t => {\n    testOffscreenCanvas('webgl2', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('offscreen canvas cpu', t => {\n    testOffscreenCanvas('cpu', t.async());\n  });\n}\n"
  },
  {
    "path": "test/features/optimize-float-memory.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, utils } = require('../../src');\n\ndescribe('feature: optimizeFloatMemory');\n\nfunction whenEnabledCallsCorrectRenderFunction(mode) {\n  const gpu = new GPU({ mode });\n  const fn = gpu.createKernel(function() { return 1 }, {\n    output: [1],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const result = fn();\n  assert.equal(fn.TextureConstructor.name, 'GLTextureMemoryOptimized');\n  assert.equal(fn.formatValues, utils.erectMemoryOptimizedFloat);\n  assert.equal(result[0], 1);\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('when enabled calls correct render function gpu (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('when enabled calls correct render function webgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('when enabled calls correct render function webgl2 (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('when enabled calls correct render function headlessgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction('headlessgl');\n});\n\n\nfunction whenEnabledCallsCorrectRenderFunction2D(mode) {\n  const gpu = new GPU({ mode });\n  const fn = gpu.createKernel(function() { return 1 }, {\n    output: [2, 2],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const result = fn();\n  assert.equal(fn.TextureConstructor.name, 'GLTextureMemoryOptimized2D');\n  assert.equal(fn.formatValues, utils.erectMemoryOptimized2DFloat);\n  assert.deepEqual(result.map(row => Array.from(row)), [[1,1],[1,1]]);\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('when enabled calls correct render function 2d gpu (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction2D('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('when enabled calls correct render function 2d webgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction2D('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('when enabled calls correct render function 2d webgl2 (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction2D('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('when enabled calls correct render function 2d headlessgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction2D('headlessgl');\n});\n\nfunction whenEnabledCallsCorrectRenderFunction3D(mode) {\n  const gpu = new GPU({ mode });\n  const fn = gpu.createKernel(function() { return 1 }, {\n    output: [2, 2, 2],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const result = fn();\n  assert.equal(fn.TextureConstructor.name, 'GLTextureMemoryOptimized3D');\n  assert.equal(fn.formatValues, utils.erectMemoryOptimized3DFloat);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), [[[1,1],[1,1]],[[1,1],[1,1]]]);\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('when enabled calls correct render function 3d gpu (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction3D('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('when enabled calls correct render function 3d webgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction3D('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('when enabled calls correct render function 3d webgl2 (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction3D('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('when enabled calls correct render function 3d headlessgl (GPU ONLY)', () => {\n  whenEnabledCallsCorrectRenderFunction3D('headlessgl');\n});\n\nfunction singlePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const array = [1,2,3,4,5];\n  const kernel = gpu.createKernel(function(array) {\n    return array[this.thread.x];\n  }, {\n    output: [5],\n    optimizeFloatMemory: true,\n    precision: 'single',\n  });\n  const result = kernel(array);\n  assert.deepEqual(Array.from(result), array);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('single precision auto', () => {\n  singlePrecision();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('single precision gpu', () => {\n  singlePrecision('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('single precision webgl', () => {\n  singlePrecision('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('single precision webgl2', () => {\n  singlePrecision('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('single precision headlessgl', () => {\n  singlePrecision('headlessgl');\n});\n\ntest('single precision cpu', () => {\n  singlePrecision('cpu');\n});\n\n\nfunction float2DOutput(mode) {\n  const gpu = new GPU({ mode });\n  const matrix = [\n    [1,2,3,4,5],\n    [6,7,8,9,10],\n    [11,12,13,14,15],\n  ];\n  const kernel = gpu.createKernel(function(matrix) {\n    return matrix[this.thread.y][this.thread.x];\n  }, {\n    output: [5, 3],\n    optimizeFloatMemory: true,\n    precision: 'single',\n  });\n  const result = kernel(matrix);\n  assert.deepEqual(result.map(row => Array.from(row)), matrix);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('float 2d output auto', () => {\n  float2DOutput();\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isGPUSupported ? test : skip)('float 2d output gpu', () => {\n  float2DOutput('gpu');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isWebGLSupported ? test : skip)('float 2d output webgl', () => {\n  float2DOutput('webgl');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isWebGL2Supported ? test : skip)('float 2d output webgl2', () => {\n  float2DOutput('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isHeadlessGLSupported ? test : skip)('float 2d output headlessgl', () => {\n  float2DOutput('headlessgl');\n});\n\ntest('float 2d output cpu', () => {\n  float2DOutput('cpu');\n});\n\n\nfunction float3DOutput(mode) {\n  const gpu = new GPU({ mode });\n  const cube = [\n    [\n      [1,2,3,4,5],\n      [6,7,8,9,10],\n      [11,12,13,14,15],\n    ],\n    [\n      [16,17,18,19,20],\n      [21,22,23,24,25],\n      [26,27,28,29,30],\n    ]\n  ];\n  const kernel = gpu.createKernel(function(cube) {\n    return cube[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [5, 3, 2],\n    optimizeFloatMemory: true,\n    precision: 'single',\n  });\n  const result = kernel(cube);\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), cube);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('float 3d output auto', () => {\n  float3DOutput();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('float 3d output gpu', () => {\n  float3DOutput('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('float 3d output webgl', () => {\n  float3DOutput('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('float 3d output webgl2', () => {\n  float3DOutput('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('float 3d output headlessgl', () => {\n  float3DOutput('headlessgl');\n});\n\ntest('float 3d output cpu', () => {\n  float3DOutput('cpu');\n});\n\nfunction floatPipelineOutput(mode) {\n  const gpu = new GPU({ mode });\n  const array = [1,2,3,4,5];\n  const kernel = gpu.createKernel(function(array) {\n    return array[this.thread.x];\n  }, {\n    output: [5],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  });\n  const result = kernel(array).toArray();\n  assert.deepEqual(Array.from(result), array);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('float pipeline output gpu (GPU only)', () => {\n  floatPipelineOutput('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('float pipeline output webgl (GPU only)', () => {\n  floatPipelineOutput('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('float pipeline output webgl2 (GPU only)', () => {\n  floatPipelineOutput('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('float pipeline output headlessgl (GPU only)', () => {\n  floatPipelineOutput('headlessgl');\n});\n\n\nfunction floatPipeline2DOutput(mode) {\n  const gpu = new GPU({ mode });\n  const matrix = [\n    [1,2,3,4,5],\n    [6,7,8,9,10],\n    [11,12,13,14,15],\n  ];\n  const kernel = gpu.createKernel(function(matrix) {\n    return matrix[this.thread.y][this.thread.x];\n  }, {\n    output: [5, 3],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  });\n  const texture = kernel(matrix);\n  const result = texture.toArray();\n  assert.deepEqual(result.map(row => Array.from(row)), matrix);\n  gpu.destroy();\n}\n\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('float pipeline 2d output gpu (GPU Only)', () => {\n  floatPipeline2DOutput('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('float pipeline 2d output webgl (GPU Only)', () => {\n  floatPipeline2DOutput('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('float pipeline 2d output webgl2 (GPU Only)', () => {\n  floatPipeline2DOutput('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('float pipeline 2d output headlessgl (GPU Only)', () => {\n  floatPipeline2DOutput('headlessgl');\n});\n\n\nfunction floatPipeline3DOutput(mode) {\n  const gpu = new GPU({ mode });\n  const cube = [\n    [\n      [1,2,3,4,5],\n      [6,7,8,9,10],\n      [11,12,13,14,15],\n    ],\n    [\n      [16,17,18,19,20],\n      [21,22,23,24,25],\n      [26,27,28,29,30],\n    ]\n  ];\n  const kernel = gpu.createKernel(function(cube) {\n    return cube[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [5, 3, 2],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  });\n  const result = kernel(cube).toArray();\n  assert.deepEqual(result.map(matrix => matrix.map(row => Array.from(row))), cube);\n  gpu.destroy();\n}\n\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('float pipeline 3d output gpu (GPU only)', () => {\n  floatPipeline3DOutput('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('float pipeline 3d output webgl (GPU only)', () => {\n  floatPipeline3DOutput('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('float pipeline 3d output webgl2 (GPU only)', () => {\n  floatPipeline3DOutput('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('float pipeline 3d output headlessgl (GPU only)', () => {\n  floatPipeline3DOutput('headlessgl');\n});\n"
  },
  {
    "path": "test/features/output.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: output');\n\nfunction outputArray(mode) {\n  const gpu = new GPU({ mode });\n  const input = [1,2,3,4,5];\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.x];\n  }, { output: [5] });\n  const result = kernel(input);\n  assert.deepEqual(Array.from(result), input);\n  gpu.destroy();\n}\n\ntest('output array auto', () => {\n  outputArray();\n});\n\ntest('output array gpu', () => {\n  outputArray('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('output array webgl', () => {\n  outputArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('output array webgl2', () => {\n  outputArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('output array headlessgl', () => {\n  outputArray('headlessgl');\n});\n\ntest('output array cpu', () => {\n  outputArray('cpu');\n});\n\nfunction outputMatrix(mode) {\n  const gpu = new GPU({ mode });\n  const input = [\n    [1,2,3,4,5],\n    [1,2,3,4,5],\n    [1,2,3,4,5],\n    [1,2,3,4,5],\n    [1,2,3,4,5],\n  ];\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.y][this.thread.x];\n  }, { output: [5, 5] });\n  const result = kernel(input);\n  assert.deepEqual(result.map(array => Array.from(array)), input);\n  gpu.destroy();\n}\n\ntest('output matrix auto', () => {\n  outputMatrix();\n});\n\ntest('output matrix gpu', () => {\n  outputMatrix('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('output matrix webgl', () => {\n  outputMatrix('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('output matrix webgl2', () => {\n  outputMatrix('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('output matrix headlessgl', () => {\n  outputMatrix('headlessgl');\n});\n\ntest('output matrix cpu', () => {\n  outputMatrix('cpu');\n});\n\nfunction outputCube(mode) {\n  const gpu = new GPU({ mode });\n  const input = [\n    [\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n    ],\n    [\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n    ],\n    [\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n    ],\n    [\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n    ],\n    [\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n      [1,2,3,4,5],\n    ]\n  ];\n  const kernel = gpu.createKernel(function(input) {\n    return input[this.thread.z][this.thread.y][this.thread.x];\n  }, { output: [5, 5, 5] });\n  const result = kernel(input);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), input);\n  gpu.destroy();\n}\n\ntest('output cube auto', () => {\n  outputCube();\n});\n\ntest('output cube gpu', () => {\n  outputCube('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('output cube webgl', () => {\n  outputCube('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('output cube webgl2', () => {\n  outputCube('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('output cube headlessgl', () => {\n  outputCube('headlessgl');\n});\n\ntest('output cube cpu', () => {\n  outputCube('cpu');\n});\n\nfunction outputGraphicalArray(mode) {\n  const gpu = new GPU({ mode });\n  const mockContext = {\n    getExtension: () => {}\n  };\n  const mockCanvas = {\n    getContext: () => mockContext,\n  };\n  assert.throws(() => {\n    const kernel = gpu.createKernel(function(input) {\n      return input[this.thread.x];\n    }, {\n      canvas: mockCanvas,\n      output: [5],\n      graphical: true\n    });\n    kernel([1]);\n  }, new Error('Output must have 2 dimensions on graphical mode'));\n  gpu.destroy();\n}\n\ntest('graphical output array auto', () => {\n  outputGraphicalArray();\n});\n\ntest('graphical output array gpu', () => {\n  outputGraphicalArray('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('graphical output array webgl', () => {\n  outputGraphicalArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('graphical output array webgl2', () => {\n  outputGraphicalArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('graphical output array headlessgl', () => {\n  outputGraphicalArray('headlessgl');\n});\n\ntest('graphical output array cpu', () => {\n  outputGraphicalArray('cpu');\n});\n\nfunction outputGraphicalMatrix(mode, canvas, context) {\n  const gpu = new GPU({ mode });\n  const input = [\n    [0.25,.50],\n    [.75,1],\n  ];\n  const kernel = gpu.createKernel(function(input) {\n    const color = input[this.thread.y][this.thread.x];\n    this.color(color, color, color, color);\n  }, {\n    context,\n    canvas,\n    output: [2, 2],\n    graphical: true\n  });\n  const result = kernel(input);\n  assert.equal(result, undefined);\n  const pixels = Array.from(kernel.getPixels());\n  gpu.destroy();\n  return pixels;\n}\n\n(GPU.isWebGLSupported ? test : skip)('graphical output matrix webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl', { premultipliedAlpha: false });\n  const pixels = outputGraphicalMatrix('webgl', canvas, context);\n  assert.deepEqual(pixels, [\n    191,\n    191,\n    191,\n    191,\n    255,\n    255,\n    255,\n    255,\n    64,\n    64,\n    64,\n    64,\n    128,\n    128,\n    128,\n    128\n  ]);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('graphical output matrix webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2', { premultipliedAlpha: false });\n  const pixels = outputGraphicalMatrix('webgl2', canvas, context);\n  assert.deepEqual(pixels, [\n    191,\n    191,\n    191,\n    191,\n    255,\n    255,\n    255,\n    255,\n    64,\n    64,\n    64,\n    64,\n    128,\n    128,\n    128,\n    128\n  ]);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('graphical output matrix headlessgl', () => {\n  const pixels = outputGraphicalMatrix('headlessgl');\n  assert.deepEqual(pixels, [\n    191,\n    191,\n    191,\n    191,\n    255,\n    255,\n    255,\n    255,\n    64,\n    64,\n    64,\n    64,\n    128,\n    128,\n    128,\n    128\n  ]);\n});\n\n(GPU.isCanvasSupported ? test : skip)('graphical output matrix cpu with real canvas', () => {\n  const pixels = outputGraphicalMatrix('cpu');\n  assert.deepEqual(pixels, [\n    191,\n    191,\n    191,\n    191,\n    255,\n    255,\n    255,\n    255,\n    63,\n    63,\n    63,\n    63,\n    127,\n    127,\n    127,\n    127\n  ]);\n});\n\ntest('graphical output matrix cpu with mocked canvas', () => {\n  // allow tests on node or browser\n  let outputImageData = null;\n  const mockContext = {\n    createImageData: () => {\n      return { data: new Uint8ClampedArray(2 * 2 * 4) };\n    },\n    putImageData: (_outputImageData) => {\n      outputImageData = _outputImageData;\n    },\n    getImageData: () => {\n      return outputImageData;\n    },\n    getExtension: () => {\n      return null;\n    }\n  };\n  const mockCanvas = {\n    getContext: () => mockContext,\n  };\n  const pixels = outputGraphicalMatrix('cpu', mockCanvas, mockContext);\n  assert.deepEqual(pixels, [\n    191,\n    191,\n    191,\n    191,\n    255,\n    255,\n    255,\n    255,\n    63,\n    63,\n    63,\n    63,\n    127,\n    127,\n    127,\n    127\n  ]);\n});\n\nfunction outputGraphicalCube(mode) {\n  const gpu = new GPU({ mode });\n  const mockContext = {\n    getExtension: () => {}\n  };\n  const mockCanvas = {\n    getContext: () => mockContext\n  };\n  assert.throws(() => {\n    const kernel = gpu.createKernel(function(input) {\n      return input[this.thread.x];\n    }, {\n      canvas: mockCanvas,\n      output: [5,5,5],\n      graphical: true\n    });\n    kernel([1]);\n  }, new Error('Output must have 2 dimensions on graphical mode'));\n  gpu.destroy();\n}\n\ntest('graphical output array auto', () => {\n  outputGraphicalCube();\n});\n\ntest('graphical output array gpu', () => {\n  outputGraphicalCube('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('graphical output array webgl', () => {\n  outputGraphicalCube('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('graphical output array webgl2', () => {\n  outputGraphicalCube('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('graphical output array headlessgl', () => {\n  outputGraphicalCube('headlessgl');\n});\n\ntest('graphical output array cpu', () => {\n  outputGraphicalCube('cpu');\n});\n"
  },
  {
    "path": "test/features/promise-api.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: promise api');\n\nfunction promiseApiFunctionReturn(mode, done) {\n  const gpu = new GPU({ mode });\n\n  const kernelFn = function() {\n    return 42.0;\n  };\n\n  const settings = {\n    output : [1]\n  };\n\n  // Setup kernel\n  const kernel = gpu.createKernel(kernelFn, settings);\n  // Get promise object\n  const promiseObj = kernel.exec();\n  assert.ok(promiseObj !== null, 'Promise object generated test');\n  promiseObj\n    .then((res) => {\n      assert.equal(res[0], 42.0 );\n      gpu.destroy();\n      done();\n    })\n    .catch((err) => {\n      throw err;\n    });\n}\n\ntest('functionReturn auto', t => {\n  promiseApiFunctionReturn(null, t.async());\n});\n\ntest('functionReturn gpu', t => {\n  promiseApiFunctionReturn('gpu', t.async());\n});\n\n(GPU.isWebGLSupported ? test : skip)('functionReturn webgl', t => {\n  promiseApiFunctionReturn('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported ? test : skip)('functionReturn webgl2', t => {\n  promiseApiFunctionReturn('webgl2', t.async());\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('functionReturn headlessgl', t => {\n  promiseApiFunctionReturn('headlessgl', t.async());\n});\n\ntest('functionReturn cpu', t => {\n  promiseApiFunctionReturn('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/raw-output.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: raw output');\n\nfunction rawUnsignedPrecisionRenderOutput(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, {\n    output: [1],\n    precision: 'unsigned',\n  });\n  kernel.build([1]);\n  kernel.run([1]);\n  const result = kernel.renderRawOutput();\n  assert.equal(result.constructor, Uint8Array);\n  assert.deepEqual(result, new Uint8Array(new Float32Array([1]).buffer));\n  gpu.destroy();\n}\n\ntest('raw unsigned precision render output auto', () => {\n  rawUnsignedPrecisionRenderOutput();\n});\n\n(GPU.isGPUSupported ? test : skip)('raw unsigned precision render output gpu', () => {\n  rawUnsignedPrecisionRenderOutput('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('raw unsigned precision render output webgl', () => {\n  rawUnsignedPrecisionRenderOutput('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('raw unsigned precision render output webgl2', () => {\n  rawUnsignedPrecisionRenderOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('raw unsigned precision render output headlessgl', () => {\n  rawUnsignedPrecisionRenderOutput('headlessgl');\n});\n\ntest('raw unsigned precision render output cpu', () => {\n  assert.throws(() => {\n    rawUnsignedPrecisionRenderOutput('cpu');\n  });\n});\n\n\nfunction rawSinglePrecisionRenderOutput(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x];\n  }, {\n    output: [1],\n    precision: 'single',\n  });\n  kernel.build([1]);\n  kernel.run([1]);\n  const result = kernel.renderRawOutput();\n  assert.equal(result.constructor, Float32Array);\n  // TODO: there is an odd bug in headless gl that causes this to output:\n  //   \"0\": 1,\n  //   \"1\": 2097761,\n  //   \"2\": 2.120494879387071e-36,\n  //   \"3\": -814303.375\n  //  For the time being, just check the first value, until this is fixed\n  // assert.deepEqual(result, new Float32Array([1, 0, 0, 0]));\n  assert.deepEqual(result.length, 4);\n  assert.deepEqual(result[0], 1);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('raw single precision render output auto', () => {\n  rawSinglePrecisionRenderOutput();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('raw single precision render output gpu', () => {\n  rawSinglePrecisionRenderOutput('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('raw single precision render output webgl', () => {\n  rawSinglePrecisionRenderOutput('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('raw single precision render output webgl2', () => {\n  rawSinglePrecisionRenderOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('raw single precision render output headlessgl', () => {\n  rawSinglePrecisionRenderOutput('headlessgl');\n});\n\ntest('raw single precision render output cpu', () => {\n  assert.throws(() => {\n    rawSinglePrecisionRenderOutput('cpu');\n  });\n});\n"
  },
  {
    "path": "test/features/read-color-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: read color texture');\n\nfunction colorSyntaxTest(mode) {\n  const gpu = new GPU({ mode });\n  const createTexture = gpu.createKernel(\n    function(value) {\n      this.color(\n        value[this.thread.y][this.thread.x],\n        value[this.thread.y][this.thread.x],\n        value[this.thread.y][this.thread.x],\n        value[this.thread.y][this.thread.x]\n      );\n    }\n  )\n    .setOutput([4, 4])\n    .setGraphical(true)\n    .setPipeline(true);\n\n  const readRTexture = gpu.createKernel(\n    function(texture) {\n      const pixel = texture[this.thread.y][this.thread.x];\n      return pixel.r;\n    }\n  )\n    .setOutput([4, 4]);\n\n  const readGTexture = gpu.createKernel(\n    function(texture) {\n      const pixel = texture[this.thread.y][this.thread.x];\n      return pixel.g;\n    }\n  )\n    .setOutput([4, 4]);\n\n  const readBTexture = gpu.createKernel(\n    function(texture) {\n      const pixel = texture[this.thread.y][this.thread.x];\n      return pixel.b;\n    }\n  )\n    .setOutput([4, 4]);\n\n  const readATexture = gpu.createKernel(\n    function(texture) {\n      const pixel = texture[this.thread.y][this.thread.x];\n      return pixel.a;\n    }\n  )\n    .setOutput([4, 4]);\n\n  const texture = createTexture([\n    [.01,.02,.03,.04],\n    [.05,.06,.07,.08],\n    [.09,.10,.11,.12],\n    [.13,.14,.15,.16]\n  ]);\n  const resultR = readRTexture(texture);\n  const resultG = readGTexture(texture);\n  const resultB = readBTexture(texture);\n  const resultA = readATexture(texture);\n\n  assert.equal(texture.constructor.name, 'GLTextureGraphical');\n\n  // R\n  assert.equal(resultR[0][0].toFixed(2), '0.01');\n  assert.equal(resultR[0][1].toFixed(2), '0.02');\n  assert.equal(resultR[0][2].toFixed(2), '0.03');\n  assert.equal(resultR[0][3].toFixed(2), '0.04');\n\n  assert.equal(resultR[1][0].toFixed(2), '0.05');\n  assert.equal(resultR[1][1].toFixed(2), '0.06');\n  assert.equal(resultR[1][2].toFixed(2), '0.07');\n  assert.equal(resultR[1][3].toFixed(2), '0.08');\n\n  assert.equal(resultR[2][0].toFixed(2), '0.09');\n  assert.equal(resultR[2][1].toFixed(2), '0.10');\n  assert.equal(resultR[2][2].toFixed(2), '0.11');\n  assert.equal(resultR[2][3].toFixed(2), '0.12');\n\n  assert.equal(resultR[3][0].toFixed(2), '0.13');\n  assert.equal(resultR[3][1].toFixed(2), '0.14');\n  assert.equal(resultR[3][2].toFixed(2), '0.15');\n  assert.equal(resultR[3][3].toFixed(2), '0.16');\n\n  // G\n  assert.equal(resultG[0][0].toFixed(2), '0.01');\n  assert.equal(resultG[0][1].toFixed(2), '0.02');\n  assert.equal(resultG[0][2].toFixed(2), '0.03');\n  assert.equal(resultG[0][3].toFixed(2), '0.04');\n\n  assert.equal(resultG[1][0].toFixed(2), '0.05');\n  assert.equal(resultG[1][1].toFixed(2), '0.06');\n  assert.equal(resultG[1][2].toFixed(2), '0.07');\n  assert.equal(resultG[1][3].toFixed(2), '0.08');\n\n  assert.equal(resultG[2][0].toFixed(2), '0.09');\n  assert.equal(resultG[2][1].toFixed(2), '0.10');\n  assert.equal(resultG[2][2].toFixed(2), '0.11');\n  assert.equal(resultG[2][3].toFixed(2), '0.12');\n\n  assert.equal(resultG[3][0].toFixed(2), '0.13');\n  assert.equal(resultG[3][1].toFixed(2), '0.14');\n  assert.equal(resultG[3][2].toFixed(2), '0.15');\n  assert.equal(resultG[3][3].toFixed(2), '0.16');\n\n  // B\n  assert.equal(resultB[0][0].toFixed(2), '0.01');\n  assert.equal(resultB[0][1].toFixed(2), '0.02');\n  assert.equal(resultB[0][2].toFixed(2), '0.03');\n  assert.equal(resultB[0][3].toFixed(2), '0.04');\n\n  assert.equal(resultB[1][0].toFixed(2), '0.05');\n  assert.equal(resultB[1][1].toFixed(2), '0.06');\n  assert.equal(resultB[1][2].toFixed(2), '0.07');\n  assert.equal(resultB[1][3].toFixed(2), '0.08');\n\n  assert.equal(resultB[2][0].toFixed(2), '0.09');\n  assert.equal(resultB[2][1].toFixed(2), '0.10');\n  assert.equal(resultB[2][2].toFixed(2), '0.11');\n  assert.equal(resultB[2][3].toFixed(2), '0.12');\n\n  assert.equal(resultB[3][0].toFixed(2), '0.13');\n  assert.equal(resultB[3][1].toFixed(2), '0.14');\n  assert.equal(resultB[3][2].toFixed(2), '0.15');\n  assert.equal(resultB[3][3].toFixed(2), '0.16');\n\n  // A\n  assert.equal(resultA[0][0].toFixed(2), '0.01');\n  assert.equal(resultA[0][1].toFixed(2), '0.02');\n  assert.equal(resultA[0][2].toFixed(2), '0.03');\n  assert.equal(resultA[0][3].toFixed(2), '0.04');\n\n  assert.equal(resultA[1][0].toFixed(2), '0.05');\n  assert.equal(resultA[1][1].toFixed(2), '0.06');\n  assert.equal(resultA[1][2].toFixed(2), '0.07');\n  assert.equal(resultA[1][3].toFixed(2), '0.08');\n\n  assert.equal(resultA[2][0].toFixed(2), '0.09');\n  assert.equal(resultA[2][1].toFixed(2), '0.10');\n  assert.equal(resultA[2][2].toFixed(2), '0.11');\n  assert.equal(resultA[2][3].toFixed(2), '0.12');\n\n  assert.equal(resultA[3][0].toFixed(2), '0.13');\n  assert.equal(resultA[3][1].toFixed(2), '0.14');\n  assert.equal(resultA[3][2].toFixed(2), '0.15');\n  assert.equal(resultA[3][3].toFixed(2), '0.16');\n  gpu.destroy();\n}\n\ntest('colorSyntaxTest auto', () => {\n  colorSyntaxTest(null);\n});\n\ntest('colorSyntaxTest gpu', () => {\n  colorSyntaxTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('colorSyntaxTest webgl', () => {\n  colorSyntaxTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('colorSyntaxTest webgl2', () => {\n  colorSyntaxTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('colorSyntaxTest headlessgl', () => {\n  colorSyntaxTest('headlessgl');\n});\n\ntest('colorSyntaxTest (cpu) throws', () => {\n  assert.throws(() => {\n    colorSyntaxTest('cpu');\n  });\n});\n"
  },
  {
    "path": "test/features/read-from-texture.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, HeadlessGLKernel } = require('../../src');\n\ndescribe('features: read from texture');\n\nfunction readWithoutTextureKernels(mode) {\n  const gpu = new GPU({ mode });\n\n  function add(m, n) {\n    return m + n;\n  }\n\n  const kernels = gpu.createKernelMap({\n    addResult: add\n  }, function (a, b) {\n    return add(a[this.thread.x], b[this.thread.x]);\n  })\n    .setOutput([5]);\n  const result = kernels([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]);\n  const nonTextureResult = result.addResult;\n  assert.deepEqual(Array.from(result.result), [2, 4, 6, 8, 10]);\n  assert.deepEqual(Array.from(nonTextureResult), [2, 4, 6, 8, 10]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('Read without texture auto', () => {\n  readWithoutTextureKernels();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('Read without texture gpu', () => {\n  readWithoutTextureKernels('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Read without texture webgl', () => {\n  readWithoutTextureKernels('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Read without texture webgl2', () => {\n  readWithoutTextureKernels('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && HeadlessGLKernel.features.kernelMap ? test : skip)('Read without texture headlessgl', () => {\n  readWithoutTextureKernels('headlessgl');\n});\n\nfunction readFromTextureKernels(mode) {\n  const gpu = new GPU({ mode });\n  function add(m, n) {\n    return m + n;\n  }\n  const kernels = gpu.createKernelMap({\n    addResult: add\n  }, function (a, b) {\n    return add(a[this.thread.x], b[this.thread.x]);\n  })\n    .setPipeline(true)\n    .setOutput([5]);\n  const result = kernels([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]);\n  const textureResult = result.addResult;\n  assert.deepEqual(Array.from(result.result.toArray()), [2, 4, 6, 8, 10]);\n  assert.deepEqual(Array.from(textureResult.toArray()), [2, 4, 6, 8, 10]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('Read from Texture auto', () => {\n  readFromTextureKernels();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('Read from Texture gpu', () => {\n  readFromTextureKernels('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Read from Texture webgl', () => {\n  readFromTextureKernels('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Read from Texture webgl2', () => {\n  readFromTextureKernels('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && HeadlessGLKernel.features.kernelMap ? test : skip)('Read from Texture headlessgl', () => {\n  readFromTextureKernels('headlessgl');\n});\n"
  },
  {
    "path": "test/features/read-image-bitmap.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: read from image bitmap');\n\nfunction readImageBitmap(mode, done) {\n  const gpu = new GPU({ mode });\n  const image = new Image();\n  image.src = 'jellyfish.jpeg';\n  const kernel = gpu.createKernel(function(image) {\n    const pixel = image[this.thread.y][this.thread.x];\n    return pixel[0] + pixel[1] + pixel[2] + pixel[3];\n  }, {\n    output: [1]\n  });\n  image.onload = async function() {\n    const imageBitmapPromise = createImageBitmap(image, 0, 0, 1, 1);\n    const imageBitmap = await imageBitmapPromise;\n    const result = kernel(imageBitmap);\n    assert.equal(result.length, 1);\n    assert.equal(result[0].toFixed(2), 3.22);\n    await gpu.destroy();\n    done();\n  };\n}\n\n(typeof Image !== 'undefined' ? test : skip)('readImageBitmap auto', (assert) => {\n  readImageBitmap(null, assert.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('readImageBitmap gpu', (assert) => {\n  readImageBitmap('gpu', assert.async());\n});\n\n(GPU.isWebGLSupported && typeof Image !== 'undefined' ? test : skip)('readImageBitmap webgl', (assert) => {\n  readImageBitmap('webgl', assert.async());\n});\n\n(GPU.isWebGL2Supported && typeof Image !== 'undefined' ? test : skip)('readImageBitmap webgl2', (assert) => {\n  readImageBitmap('webgl2', assert.async());\n});\n\n(GPU.isHeadlessGLSupported && typeof Image !== 'undefined' ? test : skip)('readImageBitmap headlessgl', (assert) => {\n  readImageBitmap('headlessgl', assert.async());\n});\n\n(typeof Image !== 'undefined' ? test : skip)('readImageBitmap cpu', (assert) => {\n  readImageBitmap('cpu', assert.async());\n});\n"
  },
  {
    "path": "test/features/read-image-data.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: read from image data');\n\nfunction readImageData(mode) {\n  const gpu = new GPU({ mode });\n  const dataArray = new Uint8ClampedArray([255, 255, 255, 255]);\n  const imageData = new ImageData(dataArray, 1, 1);\n  const kernel = gpu.createKernel(function(imageData) {\n    const pixel = imageData[this.thread.y][this.thread.x];\n    return pixel[0] + pixel[1] + pixel[2] + pixel[3];\n  }, {\n    output: [1]\n  });\n  const result = kernel(imageData);\n  assert.equal(result.length, 1);\n  assert.equal(result[0], 4);\n  gpu.destroy();\n}\n\n(typeof ImageData !== 'undefined' ? test : skip)('readImageData auto', () => {\n  readImageData(null);\n});\n\n(typeof ImageData !== 'undefined' ? test : skip)('readImageData gpu', () => {\n  readImageData('gpu');\n});\n\n(GPU.isWebGLSupported && typeof ImageData !== 'undefined' ? test : skip)('readImageData webgl', () => {\n  readImageData('webgl');\n});\n\n(GPU.isWebGL2Supported && typeof ImageData !== 'undefined' ? test : skip)('readImageData webgl2', () => {\n  readImageData('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && typeof ImageData !== 'undefined' ? test : skip)('readImageData headlessgl', () => {\n  readImageData('headlessgl');\n});\n\n(typeof ImageData !== 'undefined' ? test : skip)('readImageData cpu', () => {\n  readImageData('cpu');\n});\n"
  },
  {
    "path": "test/features/read-offscreen-canvas.js",
    "content": "if (typeof importScripts !== 'undefined') {\n  // inside Worker\n  importScripts('../../dist/gpu-browser.js');\n  onmessage = function (e) {\n    const gpu = new GPU({ mode: e.data });\n    const kernel1 = gpu.createKernel(function() {\n      this.color(1, 1, 1, 1);\n    }, {\n      output: [1, 1],\n      graphical: true,\n    });\n    kernel1();\n    const { canvas } = kernel1;\n    const kernel2 = gpu.createKernel(function(canvas) {\n      const pixel = canvas[0][this.thread.x];\n      return pixel[0] + pixel[1] + pixel[2] + pixel[3];\n    }, {\n      output: [1],\n    });\n    postMessage({ mode: gpu.mode, result: kernel2(canvas) });\n    gpu.destroy();\n  };\n} else if (typeof isBrowser !== 'undefined' && isBrowser) {\n  const { assert, skip, test, module: describe } = require('qunit');\n  describe('read offscreen canvas');\n\n  function testReadOffscreenCanvas(mode, done) {\n    const worker = new Worker('features/read-offscreen-canvas.js');\n    worker.onmessage = function (e) {\n      const { result } = e.data;\n      if (mode) assert.equal(e.data.mode, mode, 'GPU mode used in Worker');\n      assert.deepEqual(result, Float32Array.from([4]));\n      done();\n    };\n    worker.postMessage(mode);\n  }\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('read offscreen canvas auto', t => {\n    testReadOffscreenCanvas(null, t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('read offscreen canvas gpu', t => {\n    testReadOffscreenCanvas('gpu', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('read offscreen canvas webgl', t => {\n    testReadOffscreenCanvas('webgl', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('read offscreen canvas webgl2', t => {\n    testReadOffscreenCanvas('webgl2', t.async());\n  });\n\n  (GPU.isOffscreenCanvasSupported ? test : skip)('read offscreen canvas cpu', t => {\n    testReadOffscreenCanvas('cpu', t.async());\n  });\n}\n"
  },
  {
    "path": "test/features/return-arrays.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: return arrays');\n\nfunction returnArray2FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [1, 2];\n  }, { output: [1], precision: 'single' });\n  const result = kernel();\n  assert.deepEqual(result.map(v => Array.from(v)), [[1, 2]]);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(2) from kernel auto', () => {\n  returnArray2FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(2) from kernel gpu', () => {\n  returnArray2FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(2) from kernel webgl', () => {\n  returnArray2FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(2) from kernel webgl2', () => {\n  returnArray2FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(2) from kernel headlessgl', () => {\n  returnArray2FromKernel('headlessgl');\n});\n\ntest('return Array(2) from kernel cpu', () => {\n  returnArray2FromKernel('cpu');\n});\n\nfunction returnArray2D2FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.y];\n  }, { output : [3, 7], precision: 'single' });\n  const res = kernel();\n  for (let y = 0; y < 7; ++y) {\n    for (let x = 0; x < 3; ++x) {\n        assert.equal(res[y][x][0], x);\n        assert.equal(res[y][x][1], y);\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(2) from kernel auto', () => {\n  returnArray2D2FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(2) from kernel gpu', () => {\n  returnArray2D2FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array2D(2) from kernel webgl', () => {\n  returnArray2D2FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array2D(2) from kernel webgl2', () => {\n  returnArray2D2FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array2D(2) from kernel headlessgl', () => {\n  returnArray2D2FromKernel('headlessgl');\n});\n\ntest('return Array2D(2) from kernel cpu', () => {\n  returnArray2D2FromKernel('cpu');\n});\n\nfunction returnArray3D2FromKernel(mode) {\n  const gpu = new GPU({ mode });\nconst kernel = gpu.createKernel(function() {\n    return [this.thread.y, this.thread.z];\n  }, { output : [3, 5, 7], precision: 'single' });\n  const res = kernel();\n  for (let z = 0; z < 7; ++z) {\n    for (let y = 0; y < 5; ++y) {\n      for (let x = 0; x < 3; ++x) {\n        assert.equal(res[z][y][x][0], y);\n        assert.equal(res[z][y][x][1], z);\n      }\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(2) from kernel auto', () => {\n  returnArray3D2FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(2) from kernel gpu', () => {\n  returnArray3D2FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array3D(2) from kernel webgl', () => {\n  returnArray3D2FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array3D(2) from kernel webgl2', () => {\n  returnArray3D2FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array3D(2) from kernel headlessgl', () => {\n  returnArray3D2FromKernel('headlessgl');\n});\n\ntest('return Array3D(2) from kernel cpu', () => {\n  returnArray3D2FromKernel('cpu');\n});\n\n\nfunction returnArray3FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [1, 2, 3];\n  }, { output: [1], precision: 'single' });\n  const result = kernel();\n  assert.deepEqual(Array.from(result.map(v => Array.from(v))), [[1, 2, 3]]);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(3) from kernel auto', () => {\n  returnArray3FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(3) from kernel gpu', () => {\n  returnArray3FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(3) from kernel webgl', () => {\n  returnArray3FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(3) from kernel webgl2', () => {\n  returnArray3FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(3) from kernel headlessgl', () => {\n  returnArray3FromKernel('headlessgl');\n});\n\ntest('return Array(3) from kernel cpu', () => {\n  returnArray3FromKernel('cpu');\n});\n\nfunction returnArray2D3FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.y, this.thread.x * this.thread.y];\n  }, { output : [3, 7] ,precision: 'single' });\n  const res = kernel();\n  for (let y = 0; y < 7; ++y) {\n    for (let x = 0; x < 3; ++x) {\n        assert.equal(res[y][x][0], x);\n        assert.equal(res[y][x][1], y);\n        assert.equal(res[y][x][2], x * y);\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(3) from kernel auto', () => {\n  returnArray2D3FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(3) from kernel gpu', () => {\n  returnArray2D3FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array2D(3) from kernel webgl', () => {\n  returnArray2D3FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array2D(3) from kernel webgl2', () => {\n  returnArray2D3FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array2D(3) from kernel headlessgl', () => {\n  returnArray2D3FromKernel('headlessgl');\n});\n\ntest('return Array2D(3) from kernel cpu', () => {\n  returnArray2D3FromKernel('cpu');\n});\n\nfunction returnArray3D3FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.y, this.thread.z];\n  }, { output : [3, 5, 7] ,precision: 'single' });\n  const res = kernel();\n  for (let z = 0; z < 7; ++z) {\n    for (let y = 0; y < 5; ++y) {\n      for (let x = 0; x < 3; ++x) {\n        assert.equal(res[z][y][x][0], x);\n        assert.equal(res[z][y][x][1], y);\n        assert.equal(res[z][y][x][2], z);\n      }\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(3) from kernel auto', () => {\n  returnArray3D3FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(3) from kernel gpu', () => {\n  returnArray3D3FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array3D(3) from kernel webgl', () => {\n  returnArray3D3FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array3D(3) from kernel webgl2', () => {\n  returnArray3D3FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array3D(3) from kernel headlessgl', () => {\n  returnArray3D3FromKernel('headlessgl');\n});\n\ntest('return Array3D(3) from kernel cpu', () => {\n  returnArray3D3FromKernel('cpu');\n});\n\nfunction returnArray4FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [1, 2, 3, 4];\n  }, { output: [1], precision: 'single' });\n  const result = kernel();\n  assert.deepEqual(result.map(v => Array.from(v)), [[1, 2, 3, 4]]);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(4) from kernel auto', () => {\n  returnArray4FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(4) from kernel gpu', () => {\n  returnArray4FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(4) from kernel webgl', () => {\n  returnArray4FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(4) from kernel webgl2', () => {\n  returnArray4FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(4) from kernel headlessgl', () => {\n  returnArray4FromKernel('headlessgl');\n});\n\ntest('return Array(4) from kernel cpu', () => {\n  returnArray4FromKernel('cpu');\n});\n\nfunction returnArray2D4FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.y, this.thread.x * this.thread.y, this.thread.x - this.thread.y];\n  }, { output : [3, 7], precision: 'single' });\n  const res = kernel();\n  for (let y = 0; y < 3; ++y) {\n    for (let x = 0; x < 3; ++x) {\n        assert.equal(res[y][x][0], x);\n        assert.equal(res[y][x][1], y);\n        assert.equal(res[y][x][2], x * y);\n        assert.equal(res[y][x][3], x - y);\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(4) from kernel auto', () => {\n  returnArray2D4FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array2D(4) from kernel gpu', () => {\n  returnArray2D4FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array2D(4) from kernel webgl', () => {\n  returnArray2D4FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array2D(4) from kernel webgl2', () => {\n  returnArray2D4FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array2D(4) from kernel headlessgl', () => {\n  returnArray2D4FromKernel('headlessgl');\n});\n\ntest('return Array2D(4) from kernel cpu', () => {\n  returnArray2D4FromKernel('cpu');\n});\n\nfunction returnArray3D4FromKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return [this.thread.x, this.thread.y, this.thread.z, this.thread.x * this.thread.y * this.thread.z];\n  }, { output : [3, 5, 7], precision: 'single' });\n  const res = kernel();\n  for (let z = 0; z < 7; ++z) {\n    for (let y = 0; y < 5; ++y) {\n      for (let x = 0; x < 3; ++x) {\n        assert.equal(res[z][y][x][0], x);\n        assert.equal(res[z][y][x][1], y);\n        assert.equal(res[z][y][x][2], z);\n        assert.equal(res[z][y][x][3], x * y * z);\n      }\n    }\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(4) from kernel auto', () => {\n  returnArray3D4FromKernel();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array3D(4) from kernel gpu', () => {\n  returnArray3D4FromKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array3D(4) from kernel webgl', () => {\n  returnArray3D4FromKernel('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array3D(4) from kernel webgl2', () => {\n  returnArray3D4FromKernel('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array3D(4) from kernel headlessgl', () => {\n  returnArray3D4FromKernel('headlessgl');\n});\n\ntest('return Array3D(4) from kernel cpu', () => {\n  returnArray3D4FromKernel('cpu');\n});\n\nfunction returnArray2FromKernelVariables33Length(mode) {\n  const array = [\n    1,2,3,4,       5,6,7,8,    9,10,11,12,    13,14,15,16,\n    17,18,19,20,   21,22,23,   24,25,26,27,   28,29,30,31,\n    32,33];\n  const sixteen = new Uint16Array(array);\n  const eight = new Uint8Array(array);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2) {\n    return [v1[this.thread.x], v2[this.thread.x]];\n  }, { output: [33] });\n  const result = kernel(sixteen, eight);\n  assert.deepEqual(result.map(v => Array.from(v)), array.map(v => [v,v]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(2) from kernel variables 33 in length auto', () => {\n  returnArray2FromKernelVariables33Length();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('return Array(2) from kernel variables 33 in length gpu', () => {\n  returnArray2FromKernelVariables33Length('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(2) from kernel variables 33 in length webgl', () => {\n  returnArray2FromKernelVariables33Length('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(2) from kernel variables 33 in length webgl2', () => {\n  returnArray2FromKernelVariables33Length('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(2) from kernel variables 33 in length headlessgl', () => {\n  returnArray2FromKernelVariables33Length('headlessgl');\n});\n\ntest('return Array(2) from kernel variables 33 in length cpu', () => {\n  returnArray2FromKernelVariables33Length('cpu');\n});\n\n\nfunction returnArray3FromKernelVariables33Length(mode) {\n  const array = [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];\n  const thirtyTwo = new Float32Array(array);\n  const sixteen = new Uint16Array(array);\n  const eight = new Uint8Array(array);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2, v3) {\n    return [v1[this.thread.x], v2[this.thread.x], v3[this.thread.x]];\n  }, { output: [33] });\n  const result = kernel(thirtyTwo, sixteen, eight);\n  assert.deepEqual(result.map(v => Array.from(v)), array.map(v => [v,v,v]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(3) from kernel variables 33 in length auto', () => {\n  returnArray3FromKernelVariables33Length();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('return Array(3) from kernel variables 33 in length gpu', () => {\n  returnArray3FromKernelVariables33Length('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(3) from kernel variables 33 in length webgl', () => {\n  returnArray3FromKernelVariables33Length('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(3) from kernel variables 33 in length webgl2', () => {\n  returnArray3FromKernelVariables33Length('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(3) from kernel variables 33 in length headlessgl', () => {\n  returnArray3FromKernelVariables33Length('headlessgl');\n});\n\ntest('return Array(3) from kernel variables 33 in length cpu', () => {\n  returnArray3FromKernelVariables33Length('cpu');\n});\n\n\nfunction returnArray4FromKernelVariables33Length(mode) {\n  const array = [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];\n  const thirtyTwo = new Float32Array(array);\n  const sixteen = new Uint16Array(array);\n  const eight = new Uint8Array(array);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v1, v2, v3, v4) {\n    return [v1[this.thread.x], v2[this.thread.x], v3[this.thread.x], v4[this.thread.x]];\n  }, { output: [33] });\n  const result = kernel(array, thirtyTwo, sixteen, eight);\n  assert.deepEqual(result.map(v => Array.from(v)), array.map(v => [v,v,v,v]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('return Array(4) from kernel variables 33 in length auto', () => {\n  returnArray4FromKernelVariables33Length();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isGPUSupported ? test : skip)('return Array(4) from kernel variables 33 in length gpu', () => {\n  returnArray4FromKernelVariables33Length('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('return Array(4) from kernel variables 33 in length webgl', () => {\n  returnArray4FromKernelVariables33Length('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('return Array(4) from kernel variables 33 in length webgl2', () => {\n  returnArray4FromKernelVariables33Length('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('return Array(4) from kernel variables 33 in length headlessgl', () => {\n  returnArray4FromKernelVariables33Length('headlessgl');\n});\n\ntest('return Array(4) from kernel variables 33 in length cpu', () => {\n  returnArray4FromKernelVariables33Length('cpu');\n});\n"
  },
  {
    "path": "test/features/single-precision-textures.js",
    "content": "const { assert, skip, test, only, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: single precision textures');\n\nfunction singlePrecisionTexturesWithArray(mode) {\n  const original = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), original);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array auto', () => {\n  singlePrecisionTexturesWithArray();\n});\n\ntest('with Array cpu', () => {\n  singlePrecisionTexturesWithArray('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array gpu', () => {\n  singlePrecisionTexturesWithArray('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array webgl', () => {\n  singlePrecisionTexturesWithArray('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Array webgl2', () => {\n  singlePrecisionTexturesWithArray('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array headlessgl', () => {\n  singlePrecisionTexturesWithArray('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithFloat32Array(mode) {\n  const original = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array auto', () => {\n  singlePrecisionTexturesWithFloat32Array();\n});\n\ntest('with Float32Array cpu', () => {\n  singlePrecisionTexturesWithFloat32Array('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array gpu', () => {\n  singlePrecisionTexturesWithFloat32Array('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array webgl', () => {\n  singlePrecisionTexturesWithFloat32Array('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Float32Array webgl2', () => {\n  singlePrecisionTexturesWithFloat32Array('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array headlessgl', () => {\n  singlePrecisionTexturesWithFloat32Array('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint16Array(mode) {\n  const original = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'single',\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array auto', () => {\n  singlePrecisionTexturesWithUint16Array();\n});\n\ntest('with Uint16Array cpu', () => {\n  singlePrecisionTexturesWithUint16Array('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array gpu', () => {\n  singlePrecisionTexturesWithUint16Array('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array webgl', () => {\n  singlePrecisionTexturesWithUint16Array('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint16Array webgl2', () => {\n  singlePrecisionTexturesWithUint16Array('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array headlessgl', () => {\n  singlePrecisionTexturesWithUint16Array('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8Array(mode) {\n  const original = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array auto', () => {\n  singlePrecisionTexturesWithUint8Array();\n});\n\ntest('with Uint8Array cpu', () => {\n  singlePrecisionTexturesWithUint8Array('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array gpu', () => {\n  singlePrecisionTexturesWithUint8Array('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array webgl', () => {\n  singlePrecisionTexturesWithUint8Array('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8Array webgl2', () => {\n  singlePrecisionTexturesWithUint8Array('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array headlessgl', () => {\n  singlePrecisionTexturesWithUint8Array('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8ClampedArray(mode) {\n  const original = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray auto', () => {\n  singlePrecisionTexturesWithUint8ClampedArray();\n});\n\ntest('with Uint8ClampedArray cpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray gpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray webgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray webgl2', () => {\n  singlePrecisionTexturesWithUint8ClampedArray('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray headlessgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithArray2D(mode) {\n  const original = [\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [10, 11, 12, 13, 14, 15, 16, 18, 19],\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D auto', () => {\n  singlePrecisionTexturesWithArray2D();\n});\n\ntest('with Array2D cpu', () => {\n  singlePrecisionTexturesWithArray2D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D gpu', () => {\n  singlePrecisionTexturesWithArray2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D webgl', () => {\n  singlePrecisionTexturesWithArray2D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Array2D webgl2', () => {\n  singlePrecisionTexturesWithArray2D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D headlessgl', () => {\n  singlePrecisionTexturesWithArray2D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithFloat32Array2D(mode) {\n  const original = [\n    new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Float32Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D auto', () => {\n  singlePrecisionTexturesWithFloat32Array2D();\n});\n\ntest('with Float32Array2D cpu', () => {\n  singlePrecisionTexturesWithFloat32Array2D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D gpu', () => {\n  singlePrecisionTexturesWithFloat32Array2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D webgl', () => {\n  singlePrecisionTexturesWithFloat32Array2D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Float32Array2D webgl2', () => {\n  singlePrecisionTexturesWithFloat32Array2D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D headlessgl', () => {\n  singlePrecisionTexturesWithFloat32Array2D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint16Array2D(mode) {\n  const original = [\n    new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint16Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D auto', () => {\n  singlePrecisionTexturesWithUint16Array2D();\n});\n\ntest('with Uint16Array2D cpu', () => {\n  singlePrecisionTexturesWithUint16Array2D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D gpu', () => {\n  singlePrecisionTexturesWithUint16Array2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D webgl', () => {\n  singlePrecisionTexturesWithUint16Array2D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint16Array2D webgl2', () => {\n  singlePrecisionTexturesWithUint16Array2D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D headlessgl', () => {\n  singlePrecisionTexturesWithUint16Array2D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8Array2D(mode) {\n  const original = [\n    new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint8Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D auto', () => {\n  singlePrecisionTexturesWithUint8Array2D();\n});\n\ntest('with Uint8Array2D cpu', () => {\n  singlePrecisionTexturesWithUint8Array2D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D gpu', () => {\n  singlePrecisionTexturesWithUint8Array2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D webgl', () => {\n  singlePrecisionTexturesWithUint8Array2D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8Array2D webgl2', () => {\n  singlePrecisionTexturesWithUint8Array2D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D headlessgl', () => {\n  singlePrecisionTexturesWithUint8Array2D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8ClampedArray2D(mode) {\n  const original = [\n    new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint8ClampedArray([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D auto', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D();\n});\n\ntest('with Uint8ClampedArray2D cpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D gpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D webgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray2D webgl2', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D headlessgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray2D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithArray3D(mode) {\n  const original = [\n    [\n      [1, 2, 3, 4, 5, 6, 7, 8, 9],\n      [10, 11, 12, 13, 14, 15, 16, 18, 19],\n    ],\n    [\n      [20, 21, 22, 23, 24, 25, 26, 27, 28],\n      [29, 30, 31, 32, 33, 34, 35, 36, 37],\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D auto', () => {\n  singlePrecisionTexturesWithArray3D();\n});\n\ntest('with Array3D cpu', () => {\n  singlePrecisionTexturesWithArray3D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D gpu', () => {\n  singlePrecisionTexturesWithArray3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D webgl', () => {\n  singlePrecisionTexturesWithArray3D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Array3D webgl2', () => {\n  singlePrecisionTexturesWithArray3D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D headlessgl', () => {\n  singlePrecisionTexturesWithArray3D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithFloat32Array3D(mode) {\n  const original = [\n    [\n      new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Float32Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Float32Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Float32Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D auto', () => {\n  singlePrecisionTexturesWithFloat32Array3D();\n});\n\ntest('with Float32Array3D cpu', () => {\n  singlePrecisionTexturesWithFloat32Array3D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D gpu', () => {\n  singlePrecisionTexturesWithFloat32Array3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D webgl', () => {\n  singlePrecisionTexturesWithFloat32Array3D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Float32Array3D webgl2', () => {\n  singlePrecisionTexturesWithFloat32Array3D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D headlessgl', () => {\n  singlePrecisionTexturesWithFloat32Array3D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint16Array3D(mode) {\n  const original = [\n    [\n      new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint16Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint16Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint16Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D auto', () => {\n  singlePrecisionTexturesWithUint16Array3D();\n});\n\ntest('with Uint16Array3D cpu', () => {\n  singlePrecisionTexturesWithUint16Array3D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D gpu', () => {\n  singlePrecisionTexturesWithUint16Array3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D webgl', () => {\n  singlePrecisionTexturesWithUint16Array3D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint16Array3D webgl2', () => {\n  singlePrecisionTexturesWithUint16Array3D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D headlessgl', () => {\n  singlePrecisionTexturesWithUint16Array3D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8Array3D(mode) {\n  const original = [\n    [\n      new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint8Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint8Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint8Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D auto', () => {\n  singlePrecisionTexturesWithUint8Array3D();\n});\n\ntest('with Uint8Array3D cpu', () => {\n  singlePrecisionTexturesWithUint8Array3D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D gpu', () => {\n  singlePrecisionTexturesWithUint8Array3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D webgl', () => {\n  singlePrecisionTexturesWithUint8Array3D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8Array3D webgl2', () => {\n  singlePrecisionTexturesWithUint8Array3D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D headlessgl', () => {\n  singlePrecisionTexturesWithUint8Array3D('headlessgl');\n});\n\nfunction singlePrecisionTexturesWithUint8ClampedArray3D(mode) {\n  const original = [\n    [\n      new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint8ClampedArray([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint8ClampedArray([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint8ClampedArray([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'single'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D auto', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D();\n});\n\ntest('with Uint8ClampedArray3D cpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D('cpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D gpu', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D webgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray3D webgl2', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D headlessgl', () => {\n  singlePrecisionTexturesWithUint8ClampedArray3D('headlessgl');\n});\n\nfunction testImmutableDoesNotCollideWithKernelTexture(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x] + 1;\n  }, {\n    output: [1],\n    precision: 'single',\n    pipeline: true,\n    immutable: true,\n  });\n  const v = [1];\n  const result1 = kernel(v);\n  assert.deepEqual(result1.toArray(), new Float32Array([2]));\n\n  // kernel is getting ready to recompile, because a new type of input\n  const result2 = kernel(result1);\n  assert.deepEqual(result2.toArray(), new Float32Array([3]));\n  // now the kernel textures match, this would fail, and this is that this test is testing\n  const result3 = kernel(result2);\n  assert.deepEqual(result3.toArray(), new Float32Array([4]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('immutable does not collide with kernel texture auto', () => {\n  testImmutableDoesNotCollideWithKernelTexture();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('immutable does not collide with kernel texture gpu', () => {\n  testImmutableDoesNotCollideWithKernelTexture('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('immutable does not collide with kernel texture webgl', () => {\n  testImmutableDoesNotCollideWithKernelTexture('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('immutable does not collide with kernel texture webgl2', () => {\n  testImmutableDoesNotCollideWithKernelTexture('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('immutable does not collide with kernel texture headlessgl', () => {\n  testImmutableDoesNotCollideWithKernelTexture('headlessgl');\n});\n\n"
  },
  {
    "path": "test/features/single-precision.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: single precision');\nfunction singlePrecisionKernel(mode) {\n  const lst = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(lst) {\n    return lst[this.thread.x];\n  }, {\n    precision: 'single',\n    output: [lst.length]\n  });\n  assert.deepEqual(kernel(lst), lst);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)(\"auto\", () => {\n  singlePrecisionKernel(null);\n});\n\ntest(\"cpu\", () => {\n  singlePrecisionKernel('cpu');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)(\"gpu\", () => {\n  singlePrecisionKernel('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)(\"webgl\", () => {\n  singlePrecisionKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"webgl2\", () => {\n  singlePrecisionKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"headlessgl\", () => {\n  singlePrecisionKernel('headlessgl');\n});\n"
  },
  {
    "path": "test/features/switches.js",
    "content": "const { assert, skip, test, only, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: switches');\n\nfunction testBasic(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    switch (value) {\n      case 1: return 1;\n      case 2: return 2;\n      case 3: return 3;\n    }\n    return 0;\n  }, {\n    argumentTypes: ['Integer'],\n    output: [1],\n  });\n  assert.equal(kernel(1)[0], 1);\n  assert.equal(kernel(2)[0], 2);\n  assert.equal(kernel(3)[0], 3);\n  assert.equal(kernel(4)[0], 0);\n  gpu.destroy();\n}\n\ntest('basic auto', () => {\n  testBasic();\n});\n\ntest('basic gpu', () => {\n  testBasic('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('basic webgl', () => {\n  testBasic('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('basic webgl2', () => {\n  testBasic('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('basic headlessgl', () => {\n  testBasic('headlessgl');\n});\n\ntest('basic cpu', () => {\n  testBasic('cpu');\n});\n\n\nfunction testOnlyDefault(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    switch (value) {\n      default: return 3;\n    }\n  }, {\n    argumentTypes: ['Integer'],\n    output: [1]\n  });\n  assert.equal(kernel(1)[0], 3);\n  assert.equal(kernel(2)[0], 3);\n  assert.equal(kernel(3)[0], 3);\n  assert.equal(kernel(4)[0], 3);\n  gpu.destroy();\n}\n\ntest('only default auto', () => {\n  testOnlyDefault();\n});\n\ntest('only default gpu', () => {\n  testOnlyDefault('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('only default webgl', () => {\n  testOnlyDefault('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('only default webgl2', () => {\n  testOnlyDefault('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('only default headlessgl', () => {\n  testOnlyDefault('headlessgl');\n});\n\ntest('only default cpu', () => {\n  testOnlyDefault('cpu');\n});\n\nfunction testDefault(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    switch (value) {\n      case 1: return 1;\n      case 2: return 2;\n      default: return 3;\n    }\n  }, {\n    argumentTypes: ['Integer'],\n    output: [1]\n  });\n  assert.equal(kernel(1)[0], 1);\n  assert.equal(kernel(2)[0], 2);\n  assert.equal(kernel(3)[0], 3);\n  assert.equal(kernel(4)[0], 3);\n  gpu.destroy();\n}\n\ntest('default auto', () => {\n  testDefault();\n});\n\ntest('default gpu', () => {\n  testDefault('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('default webgl', () => {\n  testDefault('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('default webgl2', () => {\n  testDefault('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('default headlessgl', () => {\n  testDefault('headlessgl');\n});\n\ntest('default cpu', () => {\n  testDefault('cpu');\n});\n\n\nfunction testEarlyDefault(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    switch (value) {\n      default: return 3;\n      case 1: return 1;\n      case 2: return 2;\n    }\n  }, {\n    argumentTypes: ['Integer'],\n    output: [1],\n  });\n  assert.equal(kernel(1)[0], 1);\n  assert.equal(kernel(2)[0], 2);\n  assert.equal(kernel(3)[0], 3);\n  assert.equal(kernel(4)[0], 3);\n  gpu.destroy();\n}\n\ntest('early default auto', () => {\n  testEarlyDefault();\n});\n\ntest('early default gpu', () => {\n  testEarlyDefault('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('early default webgl', () => {\n  testEarlyDefault('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('early default webgl2', () => {\n  testEarlyDefault('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('early default headlessgl', () => {\n  testEarlyDefault('headlessgl');\n});\n\ntest('early default cpu', () => {\n  testEarlyDefault('cpu');\n});\n\n\nfunction testFallThrough(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    switch (value) {\n      case 1:\n      case 2:\n        return 1;\n      default: return 3;\n    }\n  }, {\n    argumentTypes: ['Integer'],\n    output: [1]\n  });\n  assert.equal(kernel(1)[0], 1);\n  assert.equal(kernel(2)[0], 1);\n  assert.equal(kernel(3)[0], 3);\n  assert.equal(kernel(4)[0], 3);\n  gpu.destroy();\n}\n\ntest('fall through auto', () => {\n  testFallThrough();\n});\n\ntest('fall through gpu', () => {\n  testFallThrough('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('fall through webgl', () => {\n  testFallThrough('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('fall through webgl2', () => {\n  testFallThrough('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('fall through headlessgl', () => {\n  testFallThrough('headlessgl');\n});\n\ntest('fall through cpu', () => {\n  testFallThrough('cpu');\n});\n"
  },
  {
    "path": "test/features/tactic.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: tactic');\n\nfunction speedTest(mode) {\n  const gpu = new GPU({ mode });\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  })\n    .setOutput([1])\n    .setTactic('speed');\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n  gpu.destroy();\n}\n\ntest('speed auto', () => {\n  speedTest();\n});\n\ntest('speed gpu', () => {\n  speedTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('speed webgl', () => {\n  speedTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('speed webgl2', () => {\n  speedTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('speed headlessgl', () => {\n  speedTest('headlessgl');\n});\n\ntest('speed cpu', () => {\n  speedTest('cpu');\n});\n\nfunction balancedTest(mode) {\n  const gpu = new GPU({ mode });\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  })\n    .setOutput([1])\n    .setTactic('balanced');\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n  gpu.destroy();\n}\n\ntest('balanced auto', () => {\n  balancedTest();\n});\n\ntest('balanced gpu', () => {\n  balancedTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('balanced webgl', () => {\n  balancedTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('balanced webgl2', () => {\n  balancedTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('balanced headlessgl', () => {\n  balancedTest('headlessgl');\n});\n\ntest('balanced cpu', () => {\n  balancedTest('cpu');\n});\n\nfunction precisionTest(mode) {\n  const gpu = new GPU({ mode });\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  })\n    .setOutput([1])\n    .setTactic('precision');\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n  gpu.destroy();\n}\n\ntest('precision auto', () => {\n  precisionTest();\n});\n\ntest('precision gpu', () => {\n  precisionTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('precision webgl', () => {\n  precisionTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('precision webgl2', () => {\n  precisionTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('precision headlessgl', () => {\n  precisionTest('headlessgl');\n});\n\ntest('precision cpu', () => {\n  precisionTest('cpu');\n});\n"
  },
  {
    "path": "test/features/ternary.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('feature: Ternary');\n\nfunction ternaryTest(mode) {\n  const gpu = new GPU({ mode });\n  function ternaryFunction(value) {\n    return (value > 1 ? 1 : 0);\n  }\n  const kernel = gpu.createKernel(ternaryFunction, { output: [1] });\n  const truthyResult = kernel(100);\n  const falseyResult = kernel(-100);\n  assert.equal(truthyResult[0], 1);\n  assert.equal(falseyResult[0], 0);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  ternaryTest();\n});\n\ntest('gpu', () => {\n  ternaryTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  ternaryTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  ternaryTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  ternaryTest('headlessgl');\n});\n\ntest('cpu', () => {\n  ternaryTest('cpu');\n});\n\nfunction ternaryWithVariableUsage(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value1) {\n    const value2 = value1 + 1;\n    return value2 > 10 ? 1 : 0;\n  }, { output: [1] });\n\n  assert.equal(kernel(9)[0], 0);\n  assert.equal(kernel(10)[0], 1);\n\n  gpu.destroy();\n}\n\ntest('with variable usage auto', () => {\n  ternaryWithVariableUsage();\n});\n\ntest('with variable usage gpu', () => {\n  ternaryWithVariableUsage('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('with variable usage webgl', () => {\n  ternaryWithVariableUsage('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with variable usage webgl2', () => {\n  ternaryWithVariableUsage('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('with variable usage headlessgl', () => {\n  ternaryWithVariableUsage('headlessgl');\n});\n\ntest('with variable usage cpu', () => {\n  ternaryWithVariableUsage('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/as-file.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../src');\n\ndescribe('features: to-string as file');\n\nfunction toStringAsFileTest(mode) {\n  const path = __dirname + `/to-string-as-file-${mode}.js`;\n  const fs = require('fs');\n  if (fs.existsSync(path)) {\n    fs.unlinkSync(path);\n  }\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.y][this.thread.x] + 1;\n  }, { output: [1, 1] });\n  const a = [[1]];\n  const expected = kernel(a);\n  assert.deepEqual(expected, [new Float32Array([2])]);\n  const kernelAsString = kernel.toString(a);\n  fs.writeFileSync(path, `module.exports = ${kernelAsString};`);\n  const toStringAsFile = require(path);\n  const restoredKernel = toStringAsFile({ context: kernel.context });\n  const result = restoredKernel(a);\n  assert.deepEqual(result, expected);\n  fs.unlinkSync(path);\n  gpu.destroy();\n}\n\n(GPU.isHeadlessGLSupported ? test : skip)('can save and restore function headlessgl', () => {\n  toStringAsFileTest('headlessgl');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('can save and restore function cpu', () => {\n  toStringAsFileTest('cpu');\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4];\n  const expected = new Float32Array([1,2,3,4]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [4,3,2,1];\n  const expected2 = new Float32Array([4,3,2,1]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array2.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array(2)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    argumentTypes: { a: 'Array(2)' }\n  });\n\n  const a = new Float32Array([1, 2]);\n  const expected = [new Float32Array([1,2])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([2, 1]);\n  const expected2 = [new Float32Array([2,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array2D');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let y = 0; y < 4; y++) {\n      sum += a[y][this.thread.x];\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single',\n  });\n\n  const a = [\n    [1, 2, 3, 4],\n    [5, 6, 7, 8],\n    [9, 10, 11, 12],\n    [13, 14, 15, 16],\n  ];\n  const b = [\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n  ];\n  const expected = new Float32Array([28,32,36,40]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const expected2 = new Float32Array([4,4,4,4]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array2d2.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array2D(2)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    const array2 = a[this.thread.y][this.thread.x];\n    return [array2[0] + 1, array2[1] + 1];\n  }, {\n    canvas,\n    context,\n    output: [2,2],\n    precision: 'single',\n    argumentTypes: {\n      a: 'Array2D(2)'\n    }\n  });\n\n  const a = [\n    [\n      [1, 2],\n      [3, 4],\n    ],\n    [\n      [5, 6],\n      [7, 8],\n    ]\n  ];\n  const expected = [\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([4, 5]),\n    ],\n    [\n      new Float32Array([6, 7]),\n      new Float32Array([8, 9]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context })\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [\n      [1, 1],\n      [1, 1],\n    ],\n    [\n      [1, 1],\n      [1, 1],\n    ]\n  ];\n  const expected2 = [\n    [\n      new Float32Array([2, 2]),\n      new Float32Array([2, 2]),\n    ],\n    [\n      new Float32Array([2, 2]),\n      new Float32Array([2, 2]),\n    ]\n  ];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array2d3.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array2D(3)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    const array3 = a[this.thread.y][this.thread.x];\n    return [array3[0] + 1, array3[1] + 1, array3[2] + 1];\n  }, {\n    canvas,\n    context,\n    output: [2,2],\n    precision: 'single',\n    dynamicOutput: true,\n    argumentTypes: {\n      a: 'Array2D(3)'\n    }\n  });\n\n  const a = [\n    [\n      [1, 2, 3],\n      [4, 5, 6],\n    ],\n    [\n      [7, 8, 9],\n      [10, 11, 12],\n    ]\n  ];\n  const expected = [\n    [\n      new Float32Array([2, 3, 4]),\n      new Float32Array([5, 6, 7]),\n    ],\n    [\n      new Float32Array([8, 9, 10]),\n      new Float32Array([11, 12, 13]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context })\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [\n      [1, 1, 1],\n      [1, 1, 1],\n    ],\n    [\n      [1, 1, 1],\n      [1, 1, 1],\n    ]\n  ];\n  const expected2 = [\n    [\n      new Float32Array([2, 2, 2]),\n      new Float32Array([2, 2, 2]),\n    ],\n    [\n      new Float32Array([2, 2, 2]),\n      new Float32Array([2, 2, 2]),\n    ]\n  ];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array3.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array(3)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    argumentTypes: { a: 'Array(3)' }\n  });\n\n  const a = new Float32Array([1, 2, 3]);\n  const expected = [new Float32Array([1,2,3])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([1, 1, 1]);\n  const expected2 = [new Float32Array([1,1,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array3D');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let z = 0; z < 2; z++) {\n      for (let y = 0; y < 2; y++) {\n        sum += a[z][y][this.thread.x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [2],\n    precision: 'single',\n  });\n\n  const a = [\n    [\n      [1, 2],\n      [3, 4],\n    ],\n    [\n      [5, 6],\n      [7, 8],\n    ]\n  ];\n  const expected = new Float32Array([16, 20]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [\n      [1, 1],\n      [1, 1],\n    ],\n    [\n      [1, 1],\n      [1, 1],\n    ]\n  ];\n  const expected2 = new Float32Array([4, 4]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/array4.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Array(4)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    argumentTypes: { a: 'Array(4)' }\n  });\n\n  const a = new Float32Array([1, 2, 3, 4]);\n  const expected = [new Float32Array([1,2,3,4])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([1, 1, 1, 1]);\n  const expected2 = [new Float32Array([1,1,1,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Boolean');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n  });\n  assert.deepEqual(originalKernel(true)[0], 42);\n  assert.deepEqual(originalKernel(false)[0], -42);\n  const kernelString = originalKernel.toString(true);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(true)[0], 42);\n  assert.deepEqual(newKernel(false)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/float.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Float');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return Math.floor(a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    argumentTypes: { a: 'Float' },\n  });\n  assert.equal(originalKernel.argumentTypes[0], 'Float');\n  assert.deepEqual(originalKernel(100)[0], 42);\n  assert.deepEqual(originalKernel(10)[0], -42);\n  const kernelString = originalKernel.toString(100);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(100)[0], 42);\n  assert.deepEqual(newKernel(10)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/html-canvas.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\nconst { greenCanvas } = require('../../../../../browser-test-utils');\n\ndescribe('feature: to-string single precision arguments HTMLCanvas');\n\nfunction testArgument(mode, done) {\n  const canvasInput1 = greenCanvas(mode, 1, 1);\n  const canvasInput2 = greenCanvas(mode, 1, 1);\n  const gpu = new GPU({mode});\n  const originalKernel = gpu.createKernel(function (canvas1, canvas2) {\n    const pixel1 = canvas1[this.thread.y][this.thread.x];\n    const pixel2 = canvas2[this.thread.y][this.thread.x];\n    return pixel1[1] + pixel2[1];\n  }, {\n    output: [1],\n    precision: 'single',\n    argumentTypes: ['HTMLCanvas', 'HTMLCanvas'],\n  });\n  const canvas = originalKernel.canvas;\n  const context = originalKernel.context;\n  assert.deepEqual(originalKernel(canvasInput1, canvasInput2)[0], 2);\n  const kernelString = originalKernel.toString(canvasInput1, canvasInput2);\n  const newKernel = new Function('return ' + kernelString)()({context, canvas});\n  const canvasInput3 = greenCanvas(mode, 1, 1);\n  const canvasInput4 = greenCanvas(mode, 1, 1);\n  assert.deepEqual(newKernel(canvasInput3, canvasInput4)[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testArgument('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testArgument('webgl2');\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, CPUKernel } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments HTMLImageArray');\n\nfunction testArgument(mode, done) {\n  loadImages([\n    'jellyfish-1.jpeg',\n    'jellyfish-2.jpeg',\n    'jellyfish-3.jpeg',\n    'jellyfish-4.jpeg',\n  ])\n    .then(([image1, image2, image3, image4]) => {\n      const imagesArray1 = [image1, image2];\n      const imagesArray2 = [image3, image4];\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a, selection) {\n        const image0 = a[0][0][0];\n        const image1 = a[1][0][0];\n        switch (selection) {\n          case 0: return image0.r * 255;\n          case 1: return image1.r * 255;\n          case 2: return image0.g * 255;\n          case 3: return image1.g * 255;\n        }\n      }, {\n        output: [1],\n        precision: 'single',\n        argumentTypes: ['HTMLImageArray', 'Integer'],\n      });\n      assert.deepEqual(originalKernel(imagesArray1, 0)[0], 172);\n      assert.deepEqual(originalKernel(imagesArray1, 1)[0], 255);\n      assert.deepEqual(originalKernel(imagesArray2, 2)[0], 87);\n      assert.deepEqual(originalKernel(imagesArray2, 3)[0], 110);\n      const kernelString = originalKernel.toString(imagesArray1, 0);\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(imagesArray1, 0)[0], 172);\n      assert.deepEqual(newKernel(imagesArray1, 1)[0], 255);\n      assert.deepEqual(newKernel(imagesArray2, 2)[0], 87);\n      assert.deepEqual(newKernel(imagesArray2, 3)[0], 110);\n      gpu.destroy();\n      done(originalKernel, newKernel);\n    });\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  const done = t.async();\n  testArgument('webgl', (kernel) => {\n    // They aren't supported, so test that kernel falls back\n    assert.equal(kernel.kernel.constructor, CPUKernel);\n    done();\n  });\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments HTMLImage');\n\nfunction testArgument(mode, done) {\n  loadImages(['jellyfish-1.jpeg', 'jellyfish-2.jpeg'])\n    .then(([image1, image2]) => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a) {\n        const pixel = a[0][0];\n        return pixel.b * 255;\n      }, {\n        output: [1],\n        precision: 'single',\n        argumentTypes: ['HTMLImage'],\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel(image1)[0], 253);\n      const kernelString = originalKernel.toString(image1);\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(image1)[0], 253);\n      assert.deepEqual(newKernel(image2)[0], 255);\n      gpu.destroy();\n      done();\n    });\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/html-video.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments HTMLVideo');\n\nfunction testArgument(mode, done) {\n  const video = document.createElement('video');\n  video.currentTime = 2;\n  video.src = 'jellyfish.webm';\n  video.oncanplay = (e) => {\n    video.oncanplay = null;\n    setTimeout(() => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a) {\n        const pixel = a[0][0];\n        return pixel.g * 255;\n      }, {\n        output: [1],\n        precision: 'single',\n        argumentTypes: ['HTMLVideo'],\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel(video)[0], 125);\n      const kernelString = originalKernel.toString(video);\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(video)[0], 125);\n      gpu.destroy();\n      done();\n    }, 1000);\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Input');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let y = 0; y < 2; y++) {\n      for (let x = 0; x < 2; x++) {\n        sum += a[y][x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n  });\n  const arg1 = input([1,2,3,4],[2,2]);\n  const arg2 = input([5,6,7,8],[2,2]);\n  assert.deepEqual(originalKernel(arg1)[0], 10);\n  assert.deepEqual(originalKernel(arg2)[0], 26);\n  const kernelString = originalKernel.toString(arg1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(arg1)[0], 10);\n  assert.deepEqual(newKernel(arg2)[0], 26);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/integer.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments Integer');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return Math.floor(a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    argumentTypes: { a: 'Integer' },\n  });\n  assert.equal(originalKernel.argumentTypes[0], 'Integer');\n  assert.deepEqual(originalKernel(100)[0], 42);\n  assert.deepEqual(originalKernel(10)[0], -42);\n  const kernelString = originalKernel.toString(100);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(100)[0], 42);\n  assert.deepEqual(newKernel(10)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments MemoryOptimizedNumberTexture');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture1 = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return 4 - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single'\n  });\n  assert.deepEqual(originalKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(originalKernel(texture2), new Float32Array([4,3,2,1]));\n\n  const kernelString = originalKernel.toString(texture1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(texture2), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/arguments/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision arguments NumberTexture');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture1 = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return 4 - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single'\n  });\n  assert.deepEqual(originalKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(originalKernel(texture2), new Float32Array([4,3,2,1]));\n\n  const kernelString = originalKernel.toString(texture1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(texture2), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Array');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single',\n    constants: {\n      a: [1, 2, 3, 4]\n    }\n  });\n  const expected = new Float32Array([1,2,3,4]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const newResult = new Function('return ' + kernelString)()({ context, constants: { a: [1, 2, 3, 4] } })();\n  assert.deepEqual(newResult, expected);\n\n  const expected2 = new Float32Array([4,3,2,1]);\n  const newResult2 = new Function('return ' + kernelString)()({ context, constants: { a: [4, 3, 2, 1] } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array2.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Array(2)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: new Float32Array([1, 2])\n    },\n    constantTypes: {\n      a: 'Array(2)'\n    }\n  });\n  const expected = [new Float32Array([1, 2])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a: new Float32Array([1, 2]) } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(2) is \"sticky\" as a constant, and cannot reset\n  const newResult2 = Kernel({ context, constants: { a: new Float32Array([2, 1]) } })();\n  assert.deepEqual(newResult2, expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants 2d Array');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = [\n    [1, 2, 3, 4],\n    [5, 6, 7, 8],\n    [9, 10, 11, 12],\n    [13, 14, 15, 16],\n  ];\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let y = 0; y < 4; y++) {\n      sum += this.constants.a[y][this.thread.x];\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single',\n    constants: {\n      a\n    }\n  });\n  const expected = new Float32Array([28,32,36,40]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n  ];\n  const expected2 = new Float32Array([4,4,4,4]);\n  const newResult2 = Kernel({ context, constants: { a: b } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array3.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Array(3)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: new Float32Array([1, 2, 3])\n    },\n    constantTypes: {\n      a: 'Array(3)'\n    }\n  });\n  const expected = [new Float32Array([1, 2, 3])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a: new Float32Array([1, 2, 3]) } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(3) is \"sticky\" as a constant, and cannot reset\n  const newResult2 = Kernel({ context, constants: { a: new Float32Array([3, 2, 1]) } })();\n  assert.deepEqual(newResult2, expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants 3d Array');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = [\n    [\n      [1, 2],\n      [3, 4],\n    ],\n    [\n      [5, 6],\n      [7, 8],\n    ]\n  ];\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let z = 0; z < 2; z++) {\n      for (let y = 0; y < 2; y++) {\n        sum += this.constants.a[z][y][this.thread.x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [2],\n    precision: 'single',\n    constants: {\n      a\n    }\n  });\n\n  const expected = new Float32Array([16, 20]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n\n  const b = [\n    [\n      [1, 1],\n      [1, 1],\n    ],\n    [\n      [1, 1],\n      [1, 1],\n    ]\n  ];\n  const newResult2 = Kernel({ context, constants: { a: b } })();\n  const expected2 = new Float32Array([4, 4]);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/array4.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Array(4)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = new Float32Array([1, 2, 3, 4]);\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a\n    },\n    constantTypes: {\n      a: 'Array(4)'\n    }\n  });\n  const expected = [new Float32Array([1, 2, 3, 4])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const newResult = new Function('return ' + kernelString)()({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(3) is \"sticky\" as a constant, and cannot reset\n  const b = new Float32Array([4, 3, 2, 1]);\n  const expected2 = [new Float32Array([1, 2, 3, 4])];\n  const newResult2 = new Function('return ' + kernelString)()({ context, constants: { a: b } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Boolean');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel1 = gpu.createKernel(function() {\n    return this.constants.a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: true\n    }\n  });\n  const originalKernel2 = gpu.createKernel(function() {\n    return this.constants.a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: false\n    }\n  });\n  assert.deepEqual(originalKernel1()[0], 42);\n  assert.deepEqual(originalKernel2()[0], -42);\n  const kernelString1 = originalKernel1.toString();\n  const kernelString2 = originalKernel2.toString();\n  const Kernel1 = new Function('return ' + kernelString1)();\n  const Kernel2 = new Function('return ' + kernelString2)();\n  const newKernel1 = Kernel1({ context, constants: { a: true } });\n  const newKernel2 = Kernel1({ context, constants: { a: false } });\n  const newKernel3 = Kernel2({ context, constants: { a: false } });\n  const newKernel4 = Kernel2({ context, constants: { a: true } });\n\n  // Boolean is \"sticky\" as a constant, and cannot reset\n  assert.deepEqual(newKernel1()[0], 42);\n  assert.deepEqual(newKernel2()[0], 42);\n  assert.deepEqual(newKernel3()[0], -42);\n  assert.deepEqual(newKernel4()[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/float.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Float');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return Math.floor(this.constants.a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: 100\n    },\n    constantTypes: { a: 'Float' }\n  });\n  assert.equal(originalKernel.constantTypes.a, 'Float');\n  assert.deepEqual(originalKernel()[0], 42);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n\n  // Float is \"sticky\" as a constant, and cannot reset\n  const newKernel = Kernel({ context, constants: { a: 100 } });\n  assert.deepEqual(newKernel()[0], 42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/html-canvas.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\nconst { greenCanvas } = require('../../../../../browser-test-utils');\n\ndescribe('feature: to-string single precision constants HTMLCanvas');\n\nfunction testArgument(mode, done) {\n  const canvasInput1 = greenCanvas(mode, 1, 1);\n  const canvasInput2 = greenCanvas(mode, 1, 1);\n  const gpu = new GPU({mode});\n  const originalKernel = gpu.createKernel(function () {\n    const pixel1 = this.constants.canvas1[this.thread.y][this.thread.x];\n    const pixel2 = this.constants.canvas2[this.thread.y][this.thread.x];\n    return pixel1[1] + pixel2[1];\n  }, {\n    output: [1],\n    precision: 'single',\n    constants: { canvas1: canvasInput1, canvas2: canvasInput2 }\n  });\n  const canvas = originalKernel.canvas;\n  const context = originalKernel.context;\n  assert.deepEqual(originalKernel()[0], 2);\n  const kernelString = originalKernel.toString();\n  const canvasInput3 = greenCanvas(mode, 1, 1);\n  const canvasInput4 = greenCanvas(mode, 1, 1);\n  const newKernel = new Function('return ' + kernelString)()({\n    context,\n    canvas,\n    constants: {\n      canvas1: canvasInput3,\n      canvas2: canvasInput4\n    }\n  });\n  assert.deepEqual(newKernel()[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testArgument('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testArgument('webgl2');\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, CPUKernel } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants HTMLImageArray');\n\nfunction testArgument(mode, done) {\n  loadImages([\n    'jellyfish-1.jpeg',\n    'jellyfish-2.jpeg',\n    'jellyfish-3.jpeg',\n    'jellyfish-4.jpeg',\n  ])\n    .then(([image1, image2, image3, image4]) => {\n      const images1 = [image1, image2];\n      const images2 = [image3, image4];\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (selection) {\n        const image0 = this.constants.a[0][0][0];\n        const image1 = this.constants.a[1][0][0];\n        switch (selection) {\n          case 0: return image0.r * 255;\n          case 1: return image1.r * 255;\n          case 2: return image0.b * 255;\n          case 3: return image1.b * 255;\n        }\n      }, {\n        output: [1],\n        precision: 'single',\n        argumentTypes: ['Integer'],\n        constants: {\n          a: images1,\n        }\n      });\n      assert.deepEqual(originalKernel(0)[0], 172);\n      assert.deepEqual(originalKernel(1)[0], 255);\n      assert.deepEqual(originalKernel(2)[0], 253);\n      assert.deepEqual(originalKernel(3)[0], 255);\n      const kernelString = originalKernel.toString(0);\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      const Kernel = new Function('return ' + kernelString)();\n      const newKernel1 = Kernel({context, canvas, constants: { a: images1 }});\n      assert.deepEqual(newKernel1(0)[0], 172);\n      assert.deepEqual(newKernel1(1)[0], 255);\n      assert.deepEqual(newKernel1(2)[0], 253);\n      assert.deepEqual(newKernel1(3)[0], 255);\n\n      const newKernel2 = Kernel({context, canvas, constants: { a: images2 }});\n      assert.deepEqual(newKernel2(0)[0], 0);\n      assert.deepEqual(newKernel2(1)[0], 73);\n      assert.deepEqual(newKernel2(2)[0], 255);\n      assert.deepEqual(newKernel2(3)[0], 253);\n      gpu.destroy();\n      done(originalKernel, newKernel1);\n    });\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  const done = t.async();\n  testArgument('webgl', (kernel) => {\n    // They aren't supported, so test that kernel falls back\n    assert.equal(kernel.kernel.constructor, CPUKernel);\n    done();\n  });\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n(GPU.isSinglePrecisionSupported && (GPU.isWebGLSupported || GPU.isWebGL2Supported) ? test : skip)('cpu', t => {\n  testArgument('cpu', t.async());\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants HTMLImage');\n\nfunction testArgument(mode, done) {\n  loadImages(['jellyfish-1.jpeg', 'jellyfish-2.jpeg'])\n    .then(([image1, image2]) => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function () {\n        const pixel = this.constants.a[0][0];\n        return pixel.b * 255;\n      }, {\n        output: [1],\n        precision: 'single',\n        constants: { a: image1 }\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel()[0], 253);\n      const kernelString = originalKernel.toString();\n      const newKernel = new Function('return ' + kernelString)()({context, canvas, constants: { a: image2 } });\n      assert.deepEqual(newKernel(image2)[0], 255);\n      gpu.destroy();\n      done();\n    });\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n(GPU.isSinglePrecisionSupported && (GPU.isWebGLSupported || GPU.isWebGL2Supported) ? test : skip)('cpu', t => {\n  testArgument('cpu', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Input');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = input([1,2,3,4],[2,2]);\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let y = 0; y < 2; y++) {\n      for (let x = 0; x < 2; x++) {\n        sum += this.constants.a[y][x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a\n    }\n  });\n  assert.deepEqual(originalKernel()[0], 10);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a } });\n  assert.deepEqual(newKernel()[0], 10);\n\n  const b = input([1,1,1,1],[2,2]);\n  const newKernel2 = Kernel({ context, constants: { a: b } });\n  assert.deepEqual(newKernel2()[0], 4);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/integer.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants Integer');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return Math.floor(this.constants.a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'single',\n    constants: {\n      a: 100\n    },\n    constantTypes: { a: 'Integer' }\n  });\n  assert.equal(originalKernel.constantTypes.a, 'Integer');\n  assert.deepEqual(originalKernel()[0], 42);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: 100 } });\n  assert.deepEqual(newKernel()[0], 42);\n\n  // Integer is \"sticky\" as a constant, and cannot reset\n  const newKernel2 = Kernel({ context, constants: { a: 200 } });\n  assert.deepEqual(newKernel2()[0], 42);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants MemoryOptimizedNumberTexture');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return this.output.x - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    output: [4],\n    precision: 'single',\n    constants: {\n      a: texture\n    }\n  });\n  assert.deepEqual(originalKernel(), new Float32Array([0,1,2,3]));\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: texture } });\n  const newKernel2 = Kernel({ context, constants: { a: texture2 } });\n  assert.deepEqual(texture2.toArray ? texture2.toArray() : texture2, new Float32Array([4,3,2,1]));\n  assert.deepEqual(texture.toArray ? texture.toArray() : texture, new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel2(), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/constants/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision constants NumberTexture');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return this.output.x - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'single',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'single',\n    constants: {\n      a: texture\n    }\n  });\n  assert.deepEqual(originalKernel(), new Float32Array([0,1,2,3]));\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: texture } });\n  const newKernel2 = Kernel({ context, constants: { a: texture2 } });\n  assert.deepEqual(texture2.toArray ? texture2.toArray() : texture2, new Float32Array([4,3,2,1]));\n  assert.deepEqual(texture.toArray ? texture.toArray() : texture, new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel2(), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/graphical.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../src');\n\ndescribe('feature: to-string single precision graphical');\n\nfunction testGraphical(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    this.color(1,1,1,1);\n  }, {\n    canvas,\n    context,\n    output: [2,2],\n    precision: 'single',\n    graphical: true,\n  });\n\n  const expected = new Uint8ClampedArray([\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n  ]);\n  originalKernel();\n  assert.deepEqual(originalKernel.getPixels(), expected);\n  const kernelString = originalKernel.toString();\n  const newKernel = new Function('return ' + kernelString)()({ canvas, context });\n  newKernel();\n  assert.deepEqual(newKernel.getPixels(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testGraphical('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testGraphical('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testGraphical('headlessgl', require('gl')(1, 1), null);\n});\n\n(GPU.isCanvasSupported ? test : skip)('cpu', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('2d');\n  testGraphical('cpu', context, canvas);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/array/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision array style kernel map returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/array/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision array style kernel map returns 2D Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = [\n    new Float32Array([2, 3]),\n    new Float32Array([2, 3]),\n  ];\n  const expectedZero = [\n    new Float32Array([3, 4]),\n    new Float32Array([3, 4]),\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/array/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision array style kernel map returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2];\n  const expected = [\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ],\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ]\n  ];\n  const expectedZero = [\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ],\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/array/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision array style kernel map returns MemoryOptimizedNumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n    pipeline: true,\n    optimizeFloatMemory: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult[0].toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult[0].toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/array/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision array style kernel map returns NumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult[0].toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult[0].toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/object/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision object style returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/object/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision object style returns Array2D');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = [\n    new Float32Array([2, 3]),\n    new Float32Array([2, 3]),\n  ];\n  const expectedZero = [\n    new Float32Array([3, 4]),\n    new Float32Array([3, 4]),\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/object/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision object style returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2];\n  const expected = [\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ],\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ]\n  ]\n  const expectedZero = [\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ],\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/object/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision object style kernel map returns MemoryOptimizedNumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n    pipeline: true,\n    optimizeFloatMemory: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult.addOneResult.toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult.addOneResult.toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/kernel-map/object/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string single precision object style kernel map returns NumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult.addOneResult.toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult.addOneResult.toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/returns/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x] + 1;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/returns/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision returns Array2D');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.y];\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2];\n  const b = [2, 3];\n  const expected = [\n    new Float32Array([3, 4]),\n    new Float32Array([4, 5]),\n  ];\n  const originalResult = originalKernel(a, b);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a, b);\n  const newResult = new Function('return ' + kernelString)()({ context })(a, b);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/returns/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a, b, c) {\n    return a[this.thread.x] + b[this.thread.y] + c[this.thread.z];\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'single',\n  });\n\n  const a = [1, 2];\n  const b = [3, 4];\n  const c = [5, 6];\n  const expected = [\n    [\n      new Float32Array([9,10]),\n      new Float32Array([10,11]),\n    ],[\n      new Float32Array([10,11]),\n      new Float32Array([11,12]),\n    ]\n  ];\n  const originalResult = originalKernel(a, b, c);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a, b, c);\n  const newResult = new Function('return ' + kernelString)()({ context })(a, b, c);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/single/returns/texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string single precision returns Texture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x] + 1;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'single',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const originalResult = originalKernel(a);\n  assert.equal(originalResult.constructor.name, 'GLTextureFloat');\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.toArray(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4];\n  const expected = new Float32Array([1,2,3,4]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [4,3,2,1];\n  const expected2 = new Float32Array([4,3,2,1]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array2.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array(2)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: { a: 'Array(2)' }\n  });\n\n  const a = new Float32Array([1, 2]);\n  const expected = [new Float32Array([1,2])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([2, 1]);\n  const expected2 = [new Float32Array([2,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array2D');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let y = 0; y < 4; y++) {\n      sum += a[y][this.thread.x];\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned',\n  });\n\n  const a = [\n    [1, 2, 3, 4],\n    [5, 6, 7, 8],\n    [9, 10, 11, 12],\n    [13, 14, 15, 16],\n  ];\n  const b = [\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n  ];\n  const expected = new Float32Array([28,32,36,40]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const expected2 = new Float32Array([4,4,4,4]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array3.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array(3)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: { a: 'Array(3)' }\n  });\n\n  const a = new Float32Array([1, 2, 3]);\n  const expected = [new Float32Array([1,2,3])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([1, 1, 1]);\n  const expected2 = [new Float32Array([1,1,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array3D');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let z = 0; z < 2; z++) {\n      for (let y = 0; y < 2; y++) {\n        sum += a[z][y][this.thread.x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [2],\n    precision: 'unsigned',\n  });\n\n  const a = [\n    [\n      [1, 2],\n      [3, 4],\n    ],\n    [\n      [5, 6],\n      [7, 8],\n    ]\n  ];\n  const expected = new Float32Array([16, 20]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [\n      [1, 1],\n      [1, 1],\n    ],\n    [\n      [1, 1],\n      [1, 1],\n    ]\n  ];\n  const expected2 = new Float32Array([4, 4]);\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/array4.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Array(4)');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: { a: 'Array(4)' }\n  });\n\n  const a = new Float32Array([1, 2, 3, 4]);\n  const expected = [new Float32Array([1,2,3,4])];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  const newResult = newKernel(a);\n  assert.deepEqual(newResult, expected);\n\n  const b = new Float32Array([1, 1, 1, 1]);\n  const expected2 = [new Float32Array([1,1,1,1])];\n  const newResult2 = newKernel(b);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Boolean');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n  });\n  assert.deepEqual(originalKernel(true)[0], 42);\n  assert.deepEqual(originalKernel(false)[0], -42);\n  const kernelString = originalKernel.toString(true);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(true)[0], 42);\n  assert.deepEqual(newKernel(false)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/float.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Float');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return Math.floor(a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: { a: 'Float' },\n  });\n  assert.equal(originalKernel.argumentTypes[0], 'Float');\n  assert.deepEqual(originalKernel(100)[0], 42);\n  assert.deepEqual(originalKernel(10)[0], -42);\n  const kernelString = originalKernel.toString(100);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(100)[0], 42);\n  assert.deepEqual(newKernel(10)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/html-canvas.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\nconst { greenCanvas } = require('../../../../../browser-test-utils');\n\ndescribe('feature: to-string unsigned precision arguments HTMLCanvas');\n\nfunction testArgument(mode, done) {\n  const canvasInput1 = greenCanvas(mode, 1, 1);\n  const canvasInput2 = greenCanvas(mode, 1, 1);\n  const gpu = new GPU({mode});\n  const originalKernel = gpu.createKernel(function (canvas1, canvas2) {\n    const pixel1 = canvas1[this.thread.y][this.thread.x];\n    const pixel2 = canvas2[this.thread.y][this.thread.x];\n    return pixel1[1] + pixel2[1];\n  }, {\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: ['HTMLCanvas', 'HTMLCanvas'],\n  });\n  const canvas = originalKernel.canvas;\n  const context = originalKernel.context;\n  assert.deepEqual(originalKernel(canvasInput1, canvasInput2)[0], 2);\n  const kernelString = originalKernel.toString(canvasInput1, canvasInput2);\n  const newKernel = new Function('return ' + kernelString)()({context, canvas});\n  const canvasInput3 = greenCanvas(mode, 1, 1);\n  const canvasInput4 = greenCanvas(mode, 1, 1);\n  assert.deepEqual(newKernel(canvasInput3, canvasInput4)[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testArgument('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testArgument('webgl2');\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, CPUKernel } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments HTMLImageArray');\n\nfunction testArgument(mode, done) {\n  loadImages([\n    'jellyfish-1.jpeg',\n    'jellyfish-2.jpeg',\n    'jellyfish-3.jpeg',\n    'jellyfish-4.jpeg',\n  ])\n    .then(([image1, image2, image3, image4]) => {\n      const imagesArray1 = [image1, image2];\n      const imagesArray2 = [image3, image4];\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a, selection) {\n        const image0 = a[0][0][0];\n        const image1 = a[1][0][0];\n        switch (selection) {\n          case 0: return image0.r * 255;\n          case 1: return image1.r * 255;\n          case 2: return image0.g * 255;\n          case 3: return image1.g * 255;\n        }\n      }, {\n        output: [1],\n        precision: 'unsigned',\n        argumentTypes: ['HTMLImageArray', 'Integer'],\n      });\n      assert.deepEqual(originalKernel(imagesArray1, 0)[0], 172);\n      assert.deepEqual(originalKernel(imagesArray1, 1)[0], 255);\n      assert.deepEqual(originalKernel(imagesArray2, 2)[0], 87);\n      assert.deepEqual(originalKernel(imagesArray2, 3)[0], 110);\n      const kernelString = originalKernel.toString(imagesArray1, 0);\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(imagesArray1, 0)[0], 172);\n      assert.deepEqual(newKernel(imagesArray1, 1)[0], 255);\n      assert.deepEqual(newKernel(imagesArray2, 2)[0], 87);\n      assert.deepEqual(newKernel(imagesArray2, 3)[0], 110);\n      gpu.destroy();\n      done(originalKernel, newKernel);\n    });\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  const done = t.async();\n  testArgument('webgl', (kernel) => {\n    // They aren't supported, so test that kernel falls back\n    assert.equal(kernel.kernel.constructor, CPUKernel);\n    done();\n  });\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments HTMLImage');\n\nfunction testArgument(mode, done) {\n  loadImages(['jellyfish-1.jpeg', 'jellyfish-2.jpeg'])\n    .then(([image1, image2]) => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a) {\n        const pixel = a[0][0];\n        return pixel.b * 255;\n      }, {\n        output: [1],\n        precision: 'unsigned',\n        argumentTypes: ['HTMLImage'],\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel(image1)[0], 253);\n      const kernelString = originalKernel.toString(image1);\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(image1)[0], 253);\n      assert.deepEqual(newKernel(image2)[0], 255);\n      gpu.destroy();\n      done();\n    });\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/html-video.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments HTMLVideo');\n\nfunction testArgument(mode, done) {\n  const video = document.createElement('video');\n  video.currentTime = 2;\n  video.src = 'jellyfish.webm';\n  video.oncanplay = (e) => {\n    video.oncanplay = null;\n    setTimeout(() => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (a) {\n        const pixel = a[0][0];\n        return pixel.g * 255;\n      }, {\n        output: [1],\n        precision: 'unsigned',\n        argumentTypes: ['HTMLVideo'],\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel(video)[0], 125);\n      const kernelString = originalKernel.toString(video);\n      const newKernel = new Function('return ' + kernelString)()({context, canvas});\n      assert.deepEqual(newKernel(video)[0], 125);\n      gpu.destroy();\n      done();\n    }, 1000);\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Input');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    let sum = 0;\n    for (let y = 0; y < 2; y++) {\n      for (let x = 0; x < 2; x++) {\n        sum += a[y][x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n  });\n  const arg1 = input([1,2,3,4],[2,2]);\n  const arg2 = input([5,6,7,8],[2,2]);\n  assert.deepEqual(originalKernel(arg1)[0], 10);\n  assert.deepEqual(originalKernel(arg2)[0], 26);\n  const kernelString = originalKernel.toString(arg1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(arg1)[0], 10);\n  assert.deepEqual(newKernel(arg2)[0], 26);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/integer.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments Integer');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return Math.floor(a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    argumentTypes: { a: 'Integer' },\n  });\n  assert.equal(originalKernel.argumentTypes[0], 'Integer');\n  assert.deepEqual(originalKernel(100)[0], 42);\n  assert.deepEqual(originalKernel(10)[0], -42);\n  const kernelString = originalKernel.toString(100);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(100)[0], 42);\n  assert.deepEqual(newKernel(10)[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments MemoryOptimizedNumberTexture');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture1 = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return 4 - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned'\n  });\n  assert.deepEqual(originalKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(originalKernel(texture2), new Float32Array([4,3,2,1]));\n\n  const kernelString = originalKernel.toString(texture1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(texture2), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/arguments/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision arguments NumberTexture');\n\nfunction testArgument(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture1 = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return 4 - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned'\n  });\n  assert.deepEqual(originalKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(originalKernel(texture2), new Float32Array([4,3,2,1]));\n\n  const kernelString = originalKernel.toString(texture1);\n  const newKernel = new Function('return ' + kernelString)()({ context });\n  assert.deepEqual(newKernel(texture1), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(texture2), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testArgument('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testArgument('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testArgument('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testArgument('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned',\n    constants: {\n      a: [1, 2, 3, 4]\n    }\n  });\n  const expected = new Float32Array([1,2,3,4]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const newResult = new Function('return ' + kernelString)()({ context, constants: { a: [1, 2, 3, 4] } })();\n  assert.deepEqual(newResult, expected);\n\n  const expected2 = new Float32Array([4,3,2,1]);\n  const newResult2 = new Function('return ' + kernelString)()({ context, constants: { a: [4, 3, 2, 1] } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array2.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array(2)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: new Float32Array([1, 2])\n    },\n    constantTypes: {\n      a: 'Array(2)'\n    }\n  });\n  const expected = [new Float32Array([1, 2])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a: new Float32Array([1, 2]) } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(2) is \"sticky\" as a constant, and cannot reset\n  const newResult2 = Kernel({ context, constants: { a: new Float32Array([2, 1]) } })();\n  assert.deepEqual(newResult2, expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array2D');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = [\n    [1, 2, 3, 4],\n    [5, 6, 7, 8],\n    [9, 10, 11, 12],\n    [13, 14, 15, 16],\n  ];\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let y = 0; y < 4; y++) {\n      sum += this.constants.a[y][this.thread.x];\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned',\n    constants: {\n      a\n    }\n  });\n  const expected = new Float32Array([28,32,36,40]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n  const b = [\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n    [1, 1, 1, 1],\n  ];\n  const expected2 = new Float32Array([4,4,4,4]);\n  const newResult2 = Kernel({ context, constants: { a: b } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array3.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array(3)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: new Float32Array([1, 2, 3])\n    },\n    constantTypes: {\n      a: 'Array(3)'\n    }\n  });\n  const expected = [new Float32Array([1, 2, 3])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a: new Float32Array([1, 2, 3]) } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(3) is \"sticky\" as a constant, and cannot reset\n  const newResult2 = Kernel({ context, constants: { a: new Float32Array([3, 2, 1]) } })();\n  assert.deepEqual(newResult2, expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array3D');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = [\n    [\n      [1, 2],\n      [3, 4],\n    ],\n    [\n      [5, 6],\n      [7, 8],\n    ]\n  ];\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let z = 0; z < 2; z++) {\n      for (let y = 0; y < 2; y++) {\n        sum += this.constants.a[z][y][this.thread.x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [2],\n    precision: 'unsigned',\n    constants: {\n      a\n    }\n  });\n\n  const expected = new Float32Array([16, 20]);\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newResult = Kernel({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n\n  const b = [\n    [\n      [1, 1],\n      [1, 1],\n    ],\n    [\n      [1, 1],\n      [1, 1],\n    ]\n  ];\n  const newResult2 = Kernel({ context, constants: { a: b } })();\n  const expected2 = new Float32Array([4, 4]);\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/array4.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Array(4)');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = new Float32Array([1, 2, 3, 4]);\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a\n    },\n    constantTypes: {\n      a: 'Array(4)'\n    }\n  });\n  const expected = [new Float32Array([1, 2, 3, 4])];\n  const originalResult = originalKernel();\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString();\n  const newResult = new Function('return ' + kernelString)()({ context, constants: { a } })();\n  assert.deepEqual(newResult, expected);\n\n  // Array(3) is \"sticky\" as a constant, and cannot reset\n  const b = new Float32Array([4, 3, 2, 1]);\n  const expected2 = [new Float32Array([1, 2, 3, 4])];\n  const newResult2 = new Function('return ' + kernelString)()({ context, constants: { a: b } })();\n  assert.deepEqual(newResult2, expected2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Boolean');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel1 = gpu.createKernel(function() {\n    return this.constants.a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: true\n    }\n  });\n  const originalKernel2 = gpu.createKernel(function() {\n    return this.constants.a ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: false\n    }\n  });\n  assert.deepEqual(originalKernel1()[0], 42);\n  assert.deepEqual(originalKernel2()[0], -42);\n  const kernelString1 = originalKernel1.toString();\n  const kernelString2 = originalKernel2.toString();\n  const Kernel1 = new Function('return ' + kernelString1)();\n  const Kernel2 = new Function('return ' + kernelString2)();\n  const newKernel1 = Kernel1({ context, constants: { a: true } });\n  const newKernel2 = Kernel1({ context, constants: { a: false } });\n  const newKernel3 = Kernel2({ context, constants: { a: false } });\n  const newKernel4 = Kernel2({ context, constants: { a: true } });\n\n  // Boolean is \"sticky\" as a constant, and cannot reset\n  assert.deepEqual(newKernel1()[0], 42);\n  assert.deepEqual(newKernel2()[0], 42);\n  assert.deepEqual(newKernel3()[0], -42);\n  assert.deepEqual(newKernel4()[0], -42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/float.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Float');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return Math.floor(this.constants.a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: 100\n    },\n    constantTypes: { a: 'Float' }\n  });\n  assert.equal(originalKernel.constantTypes.a, 'Float');\n  assert.deepEqual(originalKernel()[0], 42);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n\n  // Float is \"sticky\" as a constant, and cannot reset\n  const newKernel = Kernel({ context, constants: { a: 100 } });\n  assert.deepEqual(newKernel()[0], 42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/html-canvas.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\nconst { greenCanvas } = require('../../../../../browser-test-utils');\n\ndescribe('feature: to-string unsigned precision constants HTMLCanvas');\n\nfunction testArgument(mode, done) {\n  const canvasInput1 = greenCanvas(mode, 1, 1);\n  const canvasInput2 = greenCanvas(mode, 1, 1);\n  const gpu = new GPU({mode});\n  const originalKernel = gpu.createKernel(function () {\n    const pixel1 = this.constants.canvas1[this.thread.y][this.thread.x];\n    const pixel2 = this.constants.canvas2[this.thread.y][this.thread.x];\n    return pixel1[1] + pixel2[1];\n  }, {\n    output: [1],\n    precision: 'unsigned',\n    constants: { canvas1: canvasInput1, canvas2: canvasInput2 }\n  });\n  const canvas = originalKernel.canvas;\n  const context = originalKernel.context;\n  assert.deepEqual(originalKernel()[0], 2);\n  const kernelString = originalKernel.toString();\n  const canvasInput3 = greenCanvas(mode, 1, 1);\n  const canvasInput4 = greenCanvas(mode, 1, 1);\n  const newKernel = new Function('return ' + kernelString)()({\n    context,\n    canvas,\n    constants: {\n      canvas1: canvasInput3,\n      canvas2: canvasInput4\n    }\n  });\n  assert.deepEqual(newKernel()[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testArgument('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testArgument('webgl2');\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, CPUKernel } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants HTMLImageArray');\n\nfunction testArgument(mode, done) {\n  loadImages([\n    'jellyfish-1.jpeg',\n    'jellyfish-2.jpeg',\n    'jellyfish-3.jpeg',\n    'jellyfish-4.jpeg',\n  ])\n    .then(([image1, image2, image3, image4]) => {\n      const images1 = [image1, image2];\n      const images2 = [image3, image4];\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function (selection) {\n        const image0 = this.constants.a[0][0][0];\n        const image1 = this.constants.a[1][0][0];\n        switch (selection) {\n          case 0: return image0.r * 255;\n          case 1: return image1.r * 255;\n          case 2: return image0.b * 255;\n          case 3: return image1.b * 255;\n        }\n      }, {\n        output: [1],\n        precision: 'unsigned',\n        argumentTypes: ['Integer'],\n        constants: {\n          a: images1,\n        }\n      });\n      assert.deepEqual(originalKernel(0)[0], 172);\n      assert.deepEqual(originalKernel(1)[0], 255);\n      assert.deepEqual(originalKernel(2)[0], 253);\n      assert.deepEqual(originalKernel(3)[0], 255);\n      const kernelString = originalKernel.toString(0);\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      const Kernel = new Function('return ' + kernelString)();\n      const newKernel1 = Kernel({context, canvas, constants: { a: images1 }});\n      assert.deepEqual(newKernel1(0)[0], 172);\n      assert.deepEqual(newKernel1(1)[0], 255);\n      assert.deepEqual(newKernel1(2)[0], 253);\n      assert.deepEqual(newKernel1(3)[0], 255);\n\n      const newKernel2 = Kernel({context, canvas, constants: { a: images2 }});\n      assert.deepEqual(newKernel2(0)[0], 0);\n      assert.deepEqual(newKernel2(1)[0], 73);\n      assert.deepEqual(newKernel2(2)[0], 255);\n      assert.deepEqual(newKernel2(3)[0], 253);\n      gpu.destroy();\n      done(originalKernel, newKernel1);\n    });\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  const done = t.async();\n  testArgument('webgl', (kernel) => {\n    // They aren't supported, so test that kernel falls back\n    assert.equal(kernel.kernel.constructor, CPUKernel);\n    done();\n  });\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, utils } = require('../../../../../../src');\nconst { loadImage, imageToArray, check2DImage } = require('../../../../../browser-test-utils');\n\ndescribe('feature: to-string unsigned precision constants HTMLImage');\n\nfunction testArgument(mode, done) {\n  loadImages(['jellyfish-1.jpeg', 'jellyfish-2.jpeg'])\n    .then(([image1, image2]) => {\n      const gpu = new GPU({mode});\n      const originalKernel = gpu.createKernel(function () {\n        const pixel = this.constants.a[0][0];\n        return pixel.b * 255;\n      }, {\n        output: [1],\n        precision: 'unsigned',\n        constants: { a: image1 }\n      });\n      const canvas = originalKernel.canvas;\n      const context = originalKernel.context;\n      assert.deepEqual(originalKernel()[0], 253);\n      const kernelString = originalKernel.toString();\n      const newKernel = new Function('return ' + kernelString)()({\n        context,\n        canvas,\n        constants: {\n          a: image2\n        }\n      });\n      assert.deepEqual(newKernel()[0], 255);\n      gpu.destroy();\n      done();\n    });\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', t => {\n  testArgument('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', t => {\n  testArgument('webgl2', t.async());\n});\n\n\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, input } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Input');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const a = input([1,2,3,4],[2,2]);\n  const originalKernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let y = 0; y < 2; y++) {\n      for (let x = 0; x < 2; x++) {\n        sum += this.constants.a[y][x];\n      }\n    }\n    return sum;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a\n    }\n  });\n  assert.deepEqual(originalKernel()[0], 10);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a } });\n  assert.deepEqual(newKernel()[0], 10);\n\n  const b = input([1,1,1,1],[2,2]);\n  const newKernel2 = Kernel({ context, constants: { a: b } });\n  assert.deepEqual(newKernel2()[0], 4);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/integer.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants Integer');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    return Math.floor(this.constants.a) === 100 ? 42 : -42;\n  }, {\n    canvas,\n    context,\n    output: [1],\n    precision: 'unsigned',\n    constants: {\n      a: 100\n    },\n    constantTypes: { a: 'Integer' }\n  });\n  assert.equal(originalKernel.constantTypes.a, 'Integer');\n  assert.deepEqual(originalKernel()[0], 42);\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: 100 } });\n  assert.deepEqual(newKernel()[0], 42);\n\n  // Integer is \"sticky\" as a constant, and cannot reset\n  const newKernel2 = Kernel({ context, constants: { a: 200 } });\n  assert.deepEqual(newKernel2()[0], 42);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants MemoryOptimizedNumberTexture');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return this.output.x - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    output: [4],\n    precision: 'unsigned',\n    constants: {\n      a: texture\n    }\n  });\n  assert.deepEqual(originalKernel(), new Float32Array([0,1,2,3]));\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: texture } });\n  const newKernel2 = Kernel({ context, constants: { a: texture2 } });\n  assert.deepEqual(texture2.toArray ? texture2.toArray() : texture2, new Float32Array([4,3,2,1]));\n  assert.deepEqual(texture.toArray ? texture.toArray() : texture, new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel2(), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/constants/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision constants NumberTexture');\n\nfunction testConstant(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const texture = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const texture2 = gpu.createKernel(function() {\n    return this.output.x - this.thread.x;\n  }, {\n    output: [4],\n    optimizeFloatMemory: false,\n    precision: 'unsigned',\n    pipeline: true,\n  })();\n  const originalKernel = gpu.createKernel(function() {\n    return this.constants.a[this.thread.x];\n  }, {\n    canvas,\n    context,\n    output: [4],\n    precision: 'unsigned',\n    constants: {\n      a: texture\n    }\n  });\n  assert.deepEqual(originalKernel(), new Float32Array([0,1,2,3]));\n  const kernelString = originalKernel.toString();\n  const Kernel = new Function('return ' + kernelString)();\n  const newKernel = Kernel({ context, constants: { a: texture } });\n  const newKernel2 = Kernel({ context, constants: { a: texture2 } });\n  assert.deepEqual(texture2.toArray ? texture2.toArray() : texture2, new Float32Array([4,3,2,1]));\n  assert.deepEqual(texture.toArray ? texture.toArray() : texture, new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel(), new Float32Array([0,1,2,3]));\n  assert.deepEqual(newKernel2(), new Float32Array([4,3,2,1]));\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testConstant('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testConstant('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testConstant('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testConstant('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/graphical.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../src');\n\ndescribe('feature: to-string unsigned precision graphical');\n\nfunction testGraphical(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function() {\n    this.color(1,1,1,1);\n  }, {\n    canvas,\n    context,\n    output: [2,2],\n    precision: 'unsigned',\n    graphical: true,\n  });\n\n  const expected = new Uint8ClampedArray([\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n    255, 255, 255, 255,\n  ]);\n  originalKernel();\n  assert.deepEqual(originalKernel.getPixels(), expected);\n  const kernelString = originalKernel.toString();\n  const newKernel = new Function('return ' + kernelString)()({ canvas, context });\n  newKernel();\n  assert.deepEqual(newKernel.getPixels(), expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testGraphical('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testGraphical('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testGraphical('headlessgl', require('gl')(1, 1), null);\n});\n\n(GPU.isCanvasSupported ? test : skip)('cpu', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('2d');\n  testGraphical('cpu', context, canvas);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/array/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision array style kernel map returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/array/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision array style kernel map returns Array2D');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = [\n    new Float32Array([2, 3]),\n    new Float32Array([2, 3]),\n  ];\n  const expectedZero = [\n    new Float32Array([3, 4]),\n    new Float32Array([3, 4]),\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/array/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision array style kernel map returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2];\n  const expected = [\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ],\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ]\n  ];\n  const expectedZero = [\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ],\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult[0], expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult[0], expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/array/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision array style kernel map returns MemoryOptimizedNumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n    pipeline: true,\n    optimizeFloatMemory: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult[0].toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult[0].toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/array/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision array style kernel map returns NumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap([addOne], function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult[0].toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult[0].toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/object/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision object style returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/object/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision object style returns Array2D');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = [\n    new Float32Array([2, 3]),\n    new Float32Array([2, 3]),\n  ];\n  const expectedZero = [\n    new Float32Array([3, 4]),\n    new Float32Array([3, 4]),\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/object/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision object style returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2];\n  const expected = [\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ],\n    [\n      new Float32Array([2, 3]),\n      new Float32Array([2, 3]),\n    ]\n  ];\n  const expectedZero = [\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ],\n    [\n      new Float32Array([3, 4]),\n      new Float32Array([3, 4]),\n    ]\n  ];\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result, expected);\n  assert.deepEqual(originalResult.addOneResult, expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result, expected);\n  assert.deepEqual(newResult.addOneResult, expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/object/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision object style kernel map returns MemoryOptimizedNumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n    pipeline: true,\n    optimizeFloatMemory: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult.addOneResult.toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult.addOneResult.toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/kernel-map/object/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../../src');\n\ndescribe('feature: to-string unsigned precision object style kernel map returns NumberTexture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  function addOne(value) {\n    return value + 1;\n  }\n  const originalKernel = gpu.createKernelMap({ addOneResult: addOne }, function(a) {\n    const result = a[this.thread.x] + 1;\n    addOne(result);\n    return result;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const expectedZero = new Float32Array([3, 4, 5, 6, 7, 8]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult.result.toArray(), expected);\n  assert.deepEqual(originalResult.addOneResult.toArray(), expectedZero);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.result.toArray(), expected);\n  assert.deepEqual(newResult.addOneResult.toArray(), expectedZero);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/returns/array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision returns Array');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x] + 1;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const originalResult = originalKernel(a);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/returns/array2d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision returns Array2D');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.y];\n  }, {\n    canvas,\n    context,\n    output: [2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2];\n  const b = [2, 3];\n  const expected = [\n    new Float32Array([3, 4]),\n    new Float32Array([4, 5]),\n  ];\n  const originalResult = originalKernel(a, b);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a, b);\n  const newResult = new Function('return ' + kernelString)()({ context })(a, b);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/returns/array3d.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision returns Array3d');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a, b, c) {\n    return a[this.thread.x] + b[this.thread.y] + c[this.thread.z];\n  }, {\n    canvas,\n    context,\n    output: [2, 2, 2],\n    precision: 'unsigned',\n  });\n\n  const a = [1, 2];\n  const b = [3, 4];\n  const c = [5, 6];\n  const expected = [\n    [\n      new Float32Array([9,10]),\n      new Float32Array([10,11]),\n    ],[\n      new Float32Array([10,11]),\n      new Float32Array([11,12]),\n    ]\n  ];\n  const originalResult = originalKernel(a, b, c);\n  assert.deepEqual(originalResult, expected);\n  const kernelString = originalKernel.toString(a, b, c);\n  const newResult = new Function('return ' + kernelString)()({ context })(a, b, c);\n  assert.deepEqual(newResult, expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('cpu', () => {\n  testReturn('cpu');\n});\n"
  },
  {
    "path": "test/features/to-string/precision/unsigned/returns/texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../../../../../src');\n\ndescribe('feature: to-string unsigned precision returns Texture');\n\nfunction testReturn(mode, context, canvas) {\n  const gpu = new GPU({ mode });\n  const originalKernel = gpu.createKernel(function(a) {\n    return a[this.thread.x] + 1;\n  }, {\n    canvas,\n    context,\n    output: [6],\n    precision: 'unsigned',\n    pipeline: true,\n  });\n\n  const a = [1, 2, 3, 4, 5, 6];\n  const expected = new Float32Array([2, 3, 4, 5, 6, 7]);\n  const originalResult = originalKernel(a);\n  assert.equal(originalResult.constructor.name, 'GLTextureUnsigned');\n  const kernelString = originalKernel.toString(a);\n  const newResult = new Function('return ' + kernelString)()({ context })(a);\n  assert.deepEqual(newResult.toArray(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testReturn('webgl', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testReturn('webgl2', context, canvas);\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testReturn('headlessgl', require('gl')(1, 1), null);\n});\n"
  },
  {
    "path": "test/features/type-management.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU, WebGLFunctionNode, WebGL2FunctionNode, CPUFunctionNode } = require('../../src');\n\ndescribe('features: type management');\n\ntest('arrays directly - Array(2) webgl', () => {\n  const node = new WebGLFunctionNode((function direct() {\n    return [0, 0];\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 direct() {\\n\\\nreturn vec2(0.0, 0.0);\\n\\\n}');\n});\ntest('arrays directly - Array(2) webgl2', () => {\n  const node = new WebGL2FunctionNode((function direct() {\n    return [0, 0];\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 direct() {\\n\\\nreturn vec2(0.0, 0.0);\\n\\\n}');\n});\n\ntest('arrays directly - Array(2) cpu', () => {\n  const node = new CPUFunctionNode((function direct() {\n    return [0, 0];\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'function direct() {\\n\\\nreturn new Float32Array([0, 0]);\\n\\\n}');\n});\n\ntest('arrays directly - Array(3) webgl', () => {\n  const node = new WebGLFunctionNode((function direct() {\n    return [0, 0, 0];\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 direct() {\\n\\\nreturn vec3(0.0, 0.0, 0.0);\\n\\\n}');\n});\ntest('arrays directly - Array(3) webgl2', () => {\n  const node = new WebGL2FunctionNode((function direct() {\n    return [0, 0, 0];\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 direct() {\\n\\\nreturn vec3(0.0, 0.0, 0.0);\\n\\\n}');\n});\ntest('arrays directly - Array(3) cpu', () => {\n  const node = new CPUFunctionNode((function direct() {\n    return [0, 0, 0];\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'function direct() {\\n\\\nreturn new Float32Array([0, 0, 0]);\\n\\\n}');\n});\n\n\ntest('arrays directly - Array(4) webgl', () => {\n  const node = new WebGLFunctionNode((function direct() {\n    return [0, 0, 0, 0];\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 direct() {\\n\\\nreturn vec4(0.0, 0.0, 0.0, 0.0);\\n\\\n}');\n});\ntest('arrays directly - Array(4) webgl2', () => {\n  const node = new WebGL2FunctionNode((function direct() {\n    return [0, 0, 0, 0];\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 direct() {\\n\\\nreturn vec4(0.0, 0.0, 0.0, 0.0);\\n\\\n}');\n});\ntest(\"arrays directly - Array(4) cpu\", function(assert) {\n  const node = new CPUFunctionNode((function direct() {\n    return [0, 0, 0, 0];\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'function direct() {\\n\\\nreturn new Float32Array([0, 0, 0, 0]);\\n\\\n}');\n});\n\n\ntest('arrays referenced directly - Array(2) webgl', () => {\n  const node = new WebGLFunctionNode((function refDirect() {\n    const array = [0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 refDirect() {\\n\\\nvec2 user_array=vec2(0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(2) webgl2', () => {\n  const node = new WebGL2FunctionNode((function refDirect() {\n    const array = [0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 refDirect() {\\n\\\nvec2 user_array=vec2(0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(2) cpu', () => {\n  const node = new CPUFunctionNode((function refDirect() {\n    const array = [0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'function refDirect() {\\n\\\nconst user_array=new Float32Array([0, 0]);\\n\\\nreturn user_array;\\n\\\n}');\n});\n\n\ntest('arrays referenced directly - Array(3) webgl', () => {\n  const node = new WebGLFunctionNode((function refDirect() {\n    const array = [0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 refDirect() {\\n\\\nvec3 user_array=vec3(0.0, 0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(3) webgl2', () => {\n  const node = new WebGL2FunctionNode((function refDirect() {\n    const array = [0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 refDirect() {\\n\\\nvec3 user_array=vec3(0.0, 0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(3) cpu', () => {\n  const node = new CPUFunctionNode((function refDirect() {\n    const array = [0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'function refDirect() {\\n\\\nconst user_array=new Float32Array([0, 0, 0]);\\n\\\nreturn user_array;\\n\\\n}');\n});\n\n\ntest('arrays referenced directly - Array(4) webgl', () => {\n  const node = new WebGLFunctionNode((function refDirect() {\n    const array = [0, 0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 refDirect() {\\n\\\nvec4 user_array=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(4) webgl2', () => {\n  const node = new WebGL2FunctionNode((function refDirect() {\n    const array = [0, 0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 refDirect() {\\n\\\nvec4 user_array=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nreturn user_array;\\n\\\n}');\n});\ntest('arrays referenced directly - Array(4) cpu', () => {\n  const node = new CPUFunctionNode((function refDirect() {\n    const array = [0, 0, 0, 0];\n    return array;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'function refDirect() {\\n\\\nconst user_array=new Float32Array([0, 0, 0, 0]);\\n\\\nreturn user_array;\\n\\\n}');\n});\n\n\ntest('arrays referenced indirectly - Array(2) webgl', () => {\n  const node = new WebGLFunctionNode((function indirect() {\n    const array = [0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 indirect() {\\n\\\nvec2 user_array=vec2(0.0, 0.0);\\n\\\nvec2 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(2) webgl2', () => {\n  const node = new WebGL2FunctionNode((function indirect() {\n    const array = [0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 indirect() {\\n\\\nvec2 user_array=vec2(0.0, 0.0);\\n\\\nvec2 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(2) cpu', () => {\n  const node = new CPUFunctionNode((function indirect() {\n    const array = [0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'function indirect() {\\n\\\nconst user_array=new Float32Array([0, 0]);\\n\\\nconst user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\n\n\ntest('arrays referenced indirectly - Array(3) webgl', () => {\n  const node = new WebGLFunctionNode((function indirect() {\n    const array = [0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 indirect() {\\n\\\nvec3 user_array=vec3(0.0, 0.0, 0.0);\\n\\\nvec3 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(3) webgl2', () => {\n  const node = new WebGL2FunctionNode((function indirect() {\n    const array = [0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 indirect() {\\n\\\nvec3 user_array=vec3(0.0, 0.0, 0.0);\\n\\\nvec3 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(3) cpu', () => {\n  const node = new CPUFunctionNode((function indirect() {\n    const array = [0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'function indirect() {\\n\\\nconst user_array=new Float32Array([0, 0, 0]);\\n\\\nconst user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\n\n\ntest('arrays referenced indirectly - Array(4) webgl', () => {\n  const node = new WebGLFunctionNode((function indirect() {\n    const array = [0, 0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 indirect() {\\n\\\nvec4 user_array=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nvec4 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(4) webgl2', () => {\n  const node = new WebGL2FunctionNode((function indirect() {\n    const array = [0, 0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 indirect() {\\n\\\nvec4 user_array=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nvec4 user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\ntest('arrays referenced indirectly - Array(4) cpu', () => {\n  const node = new CPUFunctionNode((function indirect() {\n    const array = [0, 0, 0, 0];\n    const array2 = array;\n    return array2;\n  }).toString(), { returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'function indirect() {\\n\\\nconst user_array=new Float32Array([0, 0, 0, 0]);\\n\\\nconst user_array2=user_array;\\n\\\nreturn user_array2;\\n\\\n}');\n});\n\n\ntest('arrays arguments - Array(2) webgl', () => {\n  const node = new WebGLFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], output: [1] });\n  assert.equal(node.toString(), 'vec2 arrayArguments(vec2 user_array, vec2 user_array2) {\\n\\\nvec2 user_array3=vec2(0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(2) webgl2', () => {\n  const node = new WebGL2FunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], output: [1] });\n  assert.equal(node.toString(), 'vec2 arrayArguments(vec2 user_array, vec2 user_array2) {\\n\\\nvec2 user_array3=vec2(0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(2) cpu', () => {\n  const node = new CPUFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], output: [1] });\n  assert.equal(node.toString(), 'function arrayArguments(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('arrays arguments - Array(3) webgl', () => {\n  const node = new WebGLFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], output: [1] });\n  assert.equal(node.toString(), 'vec3 arrayArguments(vec3 user_array, vec3 user_array2) {\\n\\\nvec3 user_array3=vec3(0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(3) webgl2', () => {\n  const node = new WebGL2FunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], output: [1] });\n  assert.equal(node.toString(), 'vec3 arrayArguments(vec3 user_array, vec3 user_array2) {\\n\\\nvec3 user_array3=vec3(0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(3) cpu', () => {\n  const node = new CPUFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], output: [1] });\n  assert.equal(node.toString(), 'function arrayArguments(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\n\ntest('arrays arguments - Array(4) webgl', () => {\n  const node = new WebGLFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], output: [1] });\n  assert.equal(node.toString(), 'vec4 arrayArguments(vec4 user_array, vec4 user_array2) {\\n\\\nvec4 user_array3=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(4) webgl2', () => {\n  const node = new WebGL2FunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], output: [1] });\n  assert.equal(node.toString(), 'vec4 arrayArguments(vec4 user_array, vec4 user_array2) {\\n\\\nvec4 user_array3=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays arguments - Array(4) cpu', () => {\n  const node = new CPUFunctionNode((function arrayArguments(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], output: [1] });\n  assert.equal(node.toString(), 'function arrayArguments(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0, 0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('arrays inherited - Array(2) webgl', () => {\n  const node = new WebGLFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 inherited(vec2 user_array, vec2 user_array2) {\\n\\\nvec2 user_array3=vec2(0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays inherited - Array(2) webgl2', () => {\n  const node = new WebGL2FunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'vec2 inherited(vec2 user_array, vec2 user_array2) {\\n\\\nvec2 user_array3=vec2(0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays inherited - Array(2) cpu', () => {\n  const node = new CPUFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(2)', 'Array(2)'], returnType: 'Array(2)', output: [1] });\n  assert.equal(node.toString(), 'function inherited(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('arrays inherited - Array(3) webgl', () => {\n  const node = new WebGLFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 inherited(vec3 user_array, vec3 user_array2) {\\n\\\nvec3 user_array3=vec3(0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays inherited - Array(3) webgl2', () => {\n  const node = new WebGL2FunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'vec3 inherited(vec3 user_array, vec3 user_array2) {\\n\\\nvec3 user_array3=vec3(0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('arrays inherited - Array(3) cpu', () => {\n  const node = new CPUFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(3)', 'Array(3)'], returnType: 'Array(3)', output: [1] });\n  assert.equal(node.toString(), 'function inherited(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('arrays inherited - Array(4) webgl', () => {\n  const node = new WebGLFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 inherited(vec4 user_array, vec4 user_array2) {\\n\\\nvec4 user_array3=vec4(0.0, 0.0, 0.0, 0.0);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\ntest('arrays inherited - Array(4) webgl2', () => {\n  const node = new WebGL2FunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'vec4 inherited(vec4 user_array, vec4 user_array2) {'\n    + '\\nvec4 user_array3=vec4(0.0, 0.0, 0.0, 0.0);'\n    + '\\nuser_array3[0]=user_array[0];'\n    + '\\nuser_array3[1]=(user_array[1]*user_array2[1]);'\n    + '\\nreturn user_array3;'\n    + '\\n}');\n});\ntest('arrays inherited - Array(4) cpu', () => {\n  const node = new CPUFunctionNode((function inherited(array, array2) {\n    const array3 = [0, 0, 0, 0];\n    array3[0] = array[0];\n    array3[1] = array[1] * array2[1];\n    return array3;\n  }).toString(), { argumentTypes: ['Array(4)', 'Array(4)'], returnType: 'Array(4)', output: [1] });\n  assert.equal(node.toString(), 'function inherited(user_array, user_array2) {\\n\\\nconst user_array3=new Float32Array([0, 0, 0, 0]);\\n\\\nuser_array3[0]=user_array[0];\\n\\\nuser_array3[1]=(user_array[1]*user_array2[1]);\\n\\\nreturn user_array3;\\n\\\n}');\n});\n\ntest('auto detect float, array, array2d, array3d - webgl', () => {\n  const node = new WebGLFunctionNode(`function advancedUsed(int, array, array2d, array3d) {\n    let allValues = this.constants.float;\n    allValues += this.constants.int;\n    allValues += this.constants.array[this.thread.x];\n    allValues += this.constants.array2d[this.thread.x][this.thread.y];\n    allValues += this.constants.array3d[this.thread.x][this.thread.y][this.thread.z];\n    allValues += int;\n    allValues += array[this.thread.x];\n    allValues += array2d[this.thread.x][this.thread.y];\n    allValues += array3d[this.thread.x][this.thread.y][this.thread.z];\n\n    return allValues * Math.random();\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer', 'Array', 'Array2D', 'Array3D'],\n    constants: { float: 1, int: 1, array: [1], array2d: [[1]], array3d: [[[1]]] },\n    constantTypes: { float: 'Float', int: 'Integer', array: 'Array', array2d: 'Array2D', array3d: 'Array3D' },\n    constantBitRatios: { float: 0, int: 0, array: 4, array2d: 4, array3d: 4 },\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(node.toString(), 'float advancedUsed(int user_int, sampler2D user_array,ivec2 user_arraySize,ivec3 user_arrayDim, sampler2D user_array2d,ivec2 user_array2dSize,ivec3 user_array2dDim, sampler2D user_array3d,ivec2 user_array3dSize,ivec3 user_array3dDim) {'\n    + '\\nfloat user_allValues=constants_float;'\n    + '\\nuser_allValues+=float(constants_int);'\n    + '\\nuser_allValues+=get32(constants_array, constants_arraySize, constants_arrayDim, 0, 0, threadId.x);'\n    + '\\nuser_allValues+=get32(constants_array2d, constants_array2dSize, constants_array2dDim, 0, threadId.x, threadId.y);'\n    + '\\nuser_allValues+=get32(constants_array3d, constants_array3dSize, constants_array3dDim, threadId.x, threadId.y, threadId.z);'\n    + '\\nuser_allValues+=float(user_int);'\n    + '\\nuser_allValues+=get32(user_array, user_arraySize, user_arrayDim, 0, 0, threadId.x);'\n    + '\\nuser_allValues+=get32(user_array2d, user_array2dSize, user_array2dDim, 0, threadId.x, threadId.y);'\n    + '\\nuser_allValues+=get32(user_array3d, user_array3dSize, user_array3dDim, threadId.x, threadId.y, threadId.z);'\n    + '\\nreturn (user_allValues*random());'\n    + '\\n}');\n});\n\n\nfunction notDefined(mode) {\n  const gpu = new GPU({ mode });\n  const kernel1 = gpu.createKernel(function() {\n    return result;\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel1();\n  }, new Error('Identifier is not defined on line 1, position 0:\\n result'));\n  const kernel2 = gpu.createKernel(function() {\n    return result[0];\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel2();\n  }, new Error('Identifier is not defined on line 1, position 0:\\n result'));\n  const kernel3 = gpu.createKernel(function() {\n    return result[0][0];\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel3();\n  }, new Error('Identifier is not defined on line 1, position 0:\\n result'));\n  const kernel4 = gpu.createKernel(function() {\n    return result[0][0][0];\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel4();\n  }, new Error('Identifier is not defined on line 1, position 0:\\n result'));\n  const kernel5 = gpu.createKernel(function() {\n    return result[0][0][0][0];\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel5();\n  }, new Error('Identifier is not defined on line 1, position 1:\\n result'));\n  gpu.destroy();\n}\n\ntest('not defined auto', () => {\n  notDefined();\n});\n\ntest('not defined gpu', () => {\n  notDefined('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('not defined webgl', () => {\n  notDefined('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('not defined webgl2', () => {\n  notDefined('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('not defined headlessgl', () => {\n  notDefined('headlessgl');\n});\n\ntest('not defined cpu', () => {\n  notDefined('cpu');\n});\n"
  },
  {
    "path": "test/features/unsigned-precision-textures.js",
    "content": "const { assert, skip, test, only, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('features: unsigned precision textures');\n\nfunction unsignedPrecisionTexturesWithArray(mode) {\n  const original = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), original);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Array auto', () => {\n  unsignedPrecisionTexturesWithArray();\n});\n\ntest('with Array cpu', () => {\n  unsignedPrecisionTexturesWithArray('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Array gpu', () => {\n  unsignedPrecisionTexturesWithArray('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array webgl', () => {\n  unsignedPrecisionTexturesWithArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Array webgl2', () => {\n  unsignedPrecisionTexturesWithArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array headlessgl', () => {\n  unsignedPrecisionTexturesWithArray('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithFloat32Array(mode) {\n  const original = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array auto', () => {\n  unsignedPrecisionTexturesWithFloat32Array();\n});\n\ntest('with Float32Array cpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array gpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array webgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Float32Array webgl2', () => {\n  unsignedPrecisionTexturesWithFloat32Array('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array headlessgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint16Array(mode) {\n  const original = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'unsigned',\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array auto', () => {\n  unsignedPrecisionTexturesWithUint16Array();\n});\n\ntest('with Uint16Array cpu', () => {\n  unsignedPrecisionTexturesWithUint16Array('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array gpu', () => {\n  unsignedPrecisionTexturesWithUint16Array('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array webgl', () => {\n  unsignedPrecisionTexturesWithUint16Array('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint16Array webgl2', () => {\n  unsignedPrecisionTexturesWithUint16Array('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array headlessgl', () => {\n  unsignedPrecisionTexturesWithUint16Array('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8Array(mode) {\n  const original = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array auto', () => {\n  unsignedPrecisionTexturesWithUint8Array();\n});\n\ntest('with Uint8Array cpu', () => {\n  unsignedPrecisionTexturesWithUint8Array('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array gpu', () => {\n  unsignedPrecisionTexturesWithUint8Array('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array webgl', () => {\n  unsignedPrecisionTexturesWithUint8Array('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8Array webgl2', () => {\n  unsignedPrecisionTexturesWithUint8Array('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8Array('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8ClampedArray(mode) {\n  const original = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.x];\n  }, {\n    output: [9],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(Array.from(result), Array.from(original));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray auto', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray();\n});\n\ntest('with Uint8ClampedArray cpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray gpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray webgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray webgl2', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithArray2D(mode) {\n  const original = [\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    [10, 11, 12, 13, 14, 15, 16, 18, 19],\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Array2D auto', () => {\n  unsignedPrecisionTexturesWithArray2D();\n});\n\ntest('with Array2D cpu', () => {\n  unsignedPrecisionTexturesWithArray2D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Array2D gpu', () => {\n  unsignedPrecisionTexturesWithArray2D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D webgl', () => {\n  unsignedPrecisionTexturesWithArray2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Array2D webgl2', () => {\n  unsignedPrecisionTexturesWithArray2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array2D headlessgl', () => {\n  unsignedPrecisionTexturesWithArray2D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithFloat32Array2D(mode) {\n  const original = [\n    new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Float32Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array2D auto', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D();\n});\n\ntest('with Float32Array2D cpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array2D gpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D webgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Float32Array2D webgl2', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array2D headlessgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array2D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint16Array2D(mode) {\n  const original = [\n    new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint16Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D auto', () => {\n  unsignedPrecisionTexturesWithUint16Array2D();\n});\n\ntest('with Uint16Array2D cpu', () => {\n  unsignedPrecisionTexturesWithUint16Array2D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D gpu', () => {\n  unsignedPrecisionTexturesWithUint16Array2D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D webgl', () => {\n  unsignedPrecisionTexturesWithUint16Array2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint16Array2D webgl2', () => {\n  unsignedPrecisionTexturesWithUint16Array2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array2D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint16Array2D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8Array2D(mode) {\n  const original = [\n    new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint8Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D auto', () => {\n  unsignedPrecisionTexturesWithUint8Array2D();\n});\n\ntest('with Uint8Array2D cpu', () => {\n  unsignedPrecisionTexturesWithUint8Array2D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D gpu', () => {\n  unsignedPrecisionTexturesWithUint8Array2D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D webgl', () => {\n  unsignedPrecisionTexturesWithUint8Array2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8Array2D webgl2', () => {\n  unsignedPrecisionTexturesWithUint8Array2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array2D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8Array2D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8ClampedArray2D(mode) {\n  const original = [\n    new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n    new Uint8ClampedArray([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(array => Array.from(array)), original.map(array => Array.from(array)));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D auto', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D();\n});\n\ntest('with Uint8ClampedArray2D cpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D gpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D webgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray2D webgl2', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray2D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray2D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithArray3D(mode) {\n  const original = [\n    [\n      [1, 2, 3, 4, 5, 6, 7, 8, 9],\n      [10, 11, 12, 13, 14, 15, 16, 18, 19],\n    ],\n    [\n      [20, 21, 22, 23, 24, 25, 26, 27, 28],\n      [29, 30, 31, 32, 33, 34, 35, 36, 37],\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Array3D auto', () => {\n  unsignedPrecisionTexturesWithArray3D();\n});\n\ntest('with Array3D cpu', () => {\n  unsignedPrecisionTexturesWithArray3D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Array3D gpu', () => {\n  unsignedPrecisionTexturesWithArray3D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D webgl', () => {\n  unsignedPrecisionTexturesWithArray3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Array3D webgl2', () => {\n  unsignedPrecisionTexturesWithArray3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Array3D headlessgl', () => {\n  unsignedPrecisionTexturesWithArray3D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithFloat32Array3D(mode) {\n  const original = [\n    [\n      new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Float32Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Float32Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Float32Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array3D auto', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D();\n});\n\ntest('with Float32Array3D cpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Float32Array3D gpu', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D webgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Float32Array3D webgl2', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Float32Array3D headlessgl', () => {\n  unsignedPrecisionTexturesWithFloat32Array3D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint16Array3D(mode) {\n  const original = [\n    [\n      new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint16Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint16Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint16Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D auto', () => {\n  unsignedPrecisionTexturesWithUint16Array3D();\n});\n\ntest('with Uint16Array3D cpu', () => {\n  unsignedPrecisionTexturesWithUint16Array3D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D gpu', () => {\n  unsignedPrecisionTexturesWithUint16Array3D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D webgl', () => {\n  unsignedPrecisionTexturesWithUint16Array3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint16Array3D webgl2', () => {\n  unsignedPrecisionTexturesWithUint16Array3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint16Array3D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint16Array3D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8Array3D(mode) {\n  const original = [\n    [\n      new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint8Array([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint8Array([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint8Array([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D auto', () => {\n  unsignedPrecisionTexturesWithUint8Array3D();\n});\n\ntest('with Uint8Array3D cpu', () => {\n  unsignedPrecisionTexturesWithUint8Array3D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D gpu', () => {\n  unsignedPrecisionTexturesWithUint8Array3D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D webgl', () => {\n  unsignedPrecisionTexturesWithUint8Array3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8Array3D webgl2', () => {\n  unsignedPrecisionTexturesWithUint8Array3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8Array3D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8Array3D('headlessgl');\n});\n\nfunction unsignedPrecisionTexturesWithUint8ClampedArray3D(mode) {\n  const original = [\n    [\n      new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8, 9]),\n      new Uint8ClampedArray([10, 11, 12, 13, 14, 15, 16, 18, 19]),\n    ],\n    [\n      new Uint8ClampedArray([20, 21, 22, 23, 24, 25, 26, 27, 28]),\n      new Uint8ClampedArray([29, 30, 31, 32, 33, 34, 35, 36, 37]),\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(packed) {\n    return packed[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [9, 2, 2],\n    precision: 'unsigned'\n  });\n\n  const result = kernel(original);\n  assert.deepEqual(result.map(matrix => matrix.map(array => Array.from(array))), original.map(matrix => matrix.map(array => Array.from(array))));\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D auto', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D();\n});\n\ntest('with Uint8ClampedArray3D cpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D('cpu');\n});\n\n(GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D gpu', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D webgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('with Uint8ClampedArray3D webgl2', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('with Uint8ClampedArray3D headlessgl', () => {\n  unsignedPrecisionTexturesWithUint8ClampedArray3D('headlessgl');\n});\n\nfunction testImmutableDoesNotCollideWithKernelTexture(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[this.thread.x] + 1;\n  }, {\n    output: [1],\n    precision: 'unsigned',\n    pipeline: true,\n    immutable: true,\n  });\n  const v = [1];\n  const result1 = kernel(v);\n  assert.deepEqual(result1.toArray(), new Float32Array([2]));\n  // kernel is getting ready to recompile, because a new type of input\n  const result2 = kernel(result1);\n  assert.deepEqual(result2.toArray(), new Float32Array([3]));\n  // now the kernel textures match, this would fail, and this is that this test is testing\n  const result3 = kernel(result2);\n  assert.deepEqual(result3.toArray(), new Float32Array([4]));\n  gpu.destroy();\n}\n\ntest('immutable does not collide with kernel texture auto', () => {\n  testImmutableDoesNotCollideWithKernelTexture();\n});\n\ntest('immutable does not collide with kernel texture gpu', () => {\n  testImmutableDoesNotCollideWithKernelTexture('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable does not collide with kernel texture webgl', () => {\n  testImmutableDoesNotCollideWithKernelTexture('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable does not collide with kernel texture webgl2', () => {\n  testImmutableDoesNotCollideWithKernelTexture('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable does not collide with kernel texture headlessgl', () => {\n  testImmutableDoesNotCollideWithKernelTexture('headlessgl');\n});\n"
  },
  {
    "path": "test/features/video.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('video');\nfunction videoArgumentTest(mode, done) {\n  const video = document.createElement('video');\n  video.src = 'jellyfish.webm';\n  setTimeout(() => {\n    const gpu = new GPU({mode});\n    const videoKernel = gpu.createKernel(function (a) {\n      const pixel = a[this.thread.y][this.thread.x];\n      return pixel.g * 255;\n    }, {\n      output: [200],\n      precision: 'unsigned',\n      argumentTypes: ['HTMLVideo'],\n    });\n    const pixelResult = videoKernel(video)[0];\n    // CPU captures a bit different of a color\n    assert.ok(pixelResult <= 127 && pixelResult >= 121);\n    assert.equal(true, true, 'does not throw');\n    gpu.destroy();\n    done();\n  }, 1000);\n}\n\n(typeof HTMLVideoElement !== 'undefined' ? test : skip)('video argument auto', t => {\n  videoArgumentTest(null, t.async());\n});\n\n(typeof HTMLVideoElement !== 'undefined' ? test : skip)('video argument gpu', t => {\n  videoArgumentTest('gpu', t.async());\n});\n\n(GPU.isWebGLSupported && typeof HTMLVideoElement !== 'undefined' ? test : skip)('video argument webgl', t => {\n  videoArgumentTest('webgl', t.async());\n});\n\n(GPU.isWebGL2Supported && typeof HTMLVideoElement !== 'undefined' ? test : skip)('video argument webgl2', t => {\n  videoArgumentTest('webgl2', t.async());\n});\n\n(typeof HTMLVideoElement !== 'undefined' ? test : skip)('video argument cpu', t => {\n  videoArgumentTest('cpu', t.async());\n});\n"
  },
  {
    "path": "test/index.js",
    "content": "const { expect } = require('chai');\n\nconst GPU = require('../src/index.js');\n\ndescribe('Test Node GPU', () => {\n  describe('gpu mode', () => {\n    it('should find and use gpu runner', () => {\n      const gpu = new GPU({ mode: 'gpu' });\n\n      const kernel = gpu.createKernel(function() {\n        return 1;\n      }).setOutput([1]);\n\n      const result = kernel();\n\n      expect(gpu.runner.constructor).to.equal(GPU.HeadlessGLRunner);\n      expect(result[0]).to.equal(1);\n    });\n\n    it('supports 2x2 size', () => {\n      const gpu = new GPU({ mode: 'gpu' });\n\n      const kernel = gpu.createKernel(function() {\n        return this.thread.x * this.thread.y;\n      }).setOutput([2, 2]);\n\n      const result = kernel();\n\n      expect(gpu.runner.constructor).to.equal(GPU.HeadlessGLRunner);\n      expect(result).to.deep.equal(\n        [\n          Float32Array.from([0,0]),\n          Float32Array.from([0,1])\n        ]\n      );\n    });\n  });\n\n  describe('cpu mode', () => {\n    it('should find and use gpu runner', () => {\n      const gpu = new GPU({ mode: 'cpu' });\n\n      const kernel = gpu.createKernel(function() {\n        return 1;\n      }).setOutput([1]);\n\n      const result = kernel();\n\n      expect(gpu.runner.constructor).to.equal(GPU.CPURunner);\n      expect(result[0]).to.equal(1);\n    });\n\n    it('supports 2x2 size', () => {\n      const gpu = new GPU({ mode: 'cpu' });\n\n      const kernel = gpu.createKernel(function() {\n        return this.thread.x * this.thread.y;\n      }).setOutput([2, 2]);\n\n      const result = kernel();\n\n      expect(gpu.runner.constructor).to.equal(GPU.CPURunner);\n      expect(result).to.deep.equal(\n        [\n          Float32Array.from([0,0]),\n          Float32Array.from([0,1])\n        ]\n      );\n    });\n  });\n});\n\n"
  },
  {
    "path": "test/internal/argument-texture-switching.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: argument texture switching');\n\nfunction testArrayWithoutTypeDefined(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return this.thread.x; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Float32Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array without type defined (GPU only) auto', () => {\n  testArrayWithoutTypeDefined();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array without type defined (GPU only) gpu', () => {\n  testArrayWithoutTypeDefined('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array without type defined (GPU only) webgl', () => {\n  testArrayWithoutTypeDefined('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array without type defined (GPU only) webgl2', () => {\n  testArrayWithoutTypeDefined('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array without type defined (GPU only) headlessgl', () => {\n  testArrayWithoutTypeDefined('headlessgl');\n});\n\nfunction testArrayWithTypeDefined(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return this.thread.x; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Float32Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array with type defined (GPU only) auto', () => {\n  testArrayWithTypeDefined();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array with type defined (GPU only) gpu', () => {\n  testArrayWithTypeDefined('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array with type defined (GPU only) webgl', () => {\n  testArrayWithTypeDefined('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array with type defined (GPU only) webgl2', () => {\n  testArrayWithTypeDefined('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array with type defined (GPU only) headlessgl', () => {\n  testArrayWithTypeDefined('headlessgl');\n});\n\nfunction testArray1D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(2) (GPU only) auto', () => {\n  testArray1D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(2) (GPU only) gpu', () => {\n  testArray1D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(2) (GPU only) webgl', () => {\n  testArray1D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(2) (GPU only) webgl2', () => {\n  testArray1D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(2) (GPU only) headlessgl', () => {\n  testArray1D2('headlessgl');\n});\n\nfunction testArray1D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1, this.thread.x + 2]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array1D(3)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) auto', () => {\n  testArray1D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) gpu', () => {\n  testArray1D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(3) (GPU only) webgl', () => {\n  testArray1D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(3) (GPU only) webgl2', () => {\n  testArray1D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(3) (GPU only) headlessgl', () => {\n  testArray1D3('headlessgl');\n});\n\nfunction testArray1D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1, this.thread.x + 2, this.thread.x + 3]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array1D(4)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) auto', () => {\n  testArray1D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) gpu', () => {\n  testArray1D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(4) (GPU only) webgl', () => {\n  testArray1D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(4) (GPU only) webgl2', () => {\n  testArray1D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(4) (GPU only) headlessgl', () => {\n  testArray1D4('headlessgl');\n});\n\nfunction testArray2D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y]; })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array2D(2)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(2) (GPU only) auto', () => {\n  testArray2D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(2) (GPU only) gpu', () => {\n  testArray2D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(2) (GPU only) webgl', () => {\n  testArray2D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(2) (GPU only) webgl2', () => {\n  testArray2D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(2) (GPU only) headlessgl', () => {\n  testArray2D2('headlessgl');\n});\n\nfunction testArray2D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y, this.thread.x * this.thread.y]; })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array2D(3)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(3) (GPU only) auto', () => {\n  testArray2D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) gpu', () => {\n  testArray2D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(3) (GPU only) webgl', () => {\n  testArray2D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(3) (GPU only) webgl2', () => {\n  testArray2D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(3) (GPU only) headlessgl', () => {\n  testArray2D3('headlessgl');\n});\n\nfunction testArray2D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() {\n      return [\n        this.thread.x,\n        this.thread.y,\n        this.thread.x * this.thread.y,\n        this.thread.x / this.thread.y\n      ];\n    })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array2D(4)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(4) (GPU only) auto', () => {\n  testArray2D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) gpu', () => {\n  testArray2D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(4) (GPU only) webgl', () => {\n  testArray2D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(4) (GPU only) webgl2', () => {\n  testArray2D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(4) (GPU only) headlessgl', () => {\n  testArray2D4('headlessgl');\n});\n\nfunction testArray3D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x * this.thread.y * this.thread.z]; })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array3D(2)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(2) (GPU only) auto', () => {\n  testArray3D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(2) (GPU only) gpu', () => {\n  testArray3D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(2) (GPU only) webgl', () => {\n  testArray3D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(2) (GPU only) webgl2', () => {\n  testArray3D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(2) (GPU only) headlessgl', () => {\n  testArray3D2('headlessgl');\n});\n\nfunction testArray3D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y, this.thread.z]; })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array3D(3)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(3) (GPU only) auto', () => {\n  testArray3D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(3) (GPU only) gpu', () => {\n  testArray3D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(3) (GPU only) webgl', () => {\n  testArray3D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(3) (GPU only) webgl2', () => {\n  testArray3D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(3) (GPU only) headlessgl', () => {\n  testArray3D3('headlessgl');\n});\n\nfunction testArray3D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() {\n      return [\n        this.thread.x,\n        this.thread.y,\n        this.thread.z,\n        this.thread.x * this.thread.y * this.thread.z\n      ];\n    })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setArgumentTypes({\n      value: 'Array3D(4)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(texture), expected);\n  assert.deepEqual(kernel(expected), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(4) (GPU only) auto', () => {\n  testArray3D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(4) (GPU only) gpu', () => {\n  testArray3D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(4) (GPU only) webgl', () => {\n  testArray3D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(4) (GPU only) webgl2', () => {\n  testArray3D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(4) (GPU only) headlessgl', () => {\n  testArray3D4('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/backend/cpu-kernel.js",
    "content": "const sinon = require('sinon');\nconst { assert, skip, test, module: describe, only } = require('qunit');\nconst { CPUKernel } = require('../../../src');\n\ndescribe('internal: CPUKernel');\n\ntest('.build() checks if already built, and returns early if true', () => {\n  const mockContext = {\n    built: true,\n    setupConstants: sinon.spy(),\n  };\n  CPUKernel.prototype.build.apply(mockContext);\n  assert.equal(mockContext.setupConstants.callCount, 0);\n});"
  },
  {
    "path": "test/internal/backend/function-node/isSafe.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { FunctionNode } = require(process.cwd() + '/src');\n\ndescribe('FunctionNode.isSafe()');\n\ntest('calls this.getDependencies(ast) and then this.isSafeDependencies()', () => {\n  const mockAst = {};\n  const dependenciesMock = {\n    dependencies: []\n  };\n  let calls = 0;\n  FunctionNode.prototype.isSafe.call({\n    getDependencies: (ast) => {\n      assert.equal(ast, mockAst);\n      assert.equal(calls++, 0);\n      return dependenciesMock;\n    },\n    isSafeDependencies: (dependencies) => {\n      assert.equal(calls++, 1);\n      assert.equal(dependencies, dependenciesMock);\n    }\n  }, mockAst);\n\n  assert.equal(calls, 2);\n});\n"
  },
  {
    "path": "test/internal/backend/function-node/isSafeDependencies.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { FunctionNode } = require(process.cwd() + '/src');\n\ndescribe('FunctionNode.isSafeDependencies()');\n\ntest('calls if dependencies are falsey, returns true', () => {\n  assert.equal(FunctionNode.prototype.isSafeDependencies(null), true);\n});\n\ntest('calls if dependencies have all isSafe that are true, returns true', () => {\n  assert.equal(FunctionNode.prototype.isSafeDependencies([\n    {\n      isSafe: true\n    },\n    {\n      isSafe: true\n    },\n    {\n      isSafe: true\n    }\n  ]), true);\n});\n\ntest('calls if dependencies have any isSafe that are false, returns false', () => {\n  assert.equal(FunctionNode.prototype.isSafeDependencies([\n    {\n      isSafe: true\n    },\n    {\n      isSafe: false\n    },\n    {\n      isSafe: true\n    }\n  ]), false);\n});\n"
  },
  {
    "path": "test/internal/backend/gl-kernel.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst sinon = require('sinon');\nconst { GLKernel, GPU } = require(process.cwd() + '/src');\n\ndescribe('GLKernel');\n\ntest('nativeFunctionArguments() parse simple function', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(vec2 longName) {\n    return vec2(1, 1);\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['longName'],\n    argumentTypes: ['Array(2)']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function with argument that has number', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(vec2 longName123) {\n    return vec2(1, 1);\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['longName123'],\n    argumentTypes: ['Array(2)']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function, multiple arguments', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(vec3 a,vec3 b,float c) {\n    return vec2(1, 1);\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'b', 'c'],\n    argumentTypes: ['Array(3)', 'Array(3)', 'Number']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function, multiple arguments with comments', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(vec3 a /* vec4 b */,vec2 c, /* vec4 d */ float e) {\n    return vec2(1, 1);\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'c', 'e'],\n    argumentTypes: ['Array(3)', 'Array(2)', 'Number']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function, multiple arguments on multi line with spaces', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(\n    vec4  a,\n    vec3  b,\n    float  c\n  ) {\n    vec3 delta = a - b;\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'b', 'c'],\n    argumentTypes: ['Array(4)', 'Array(3)', 'Number']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function, multiple arguments on multi line with spaces and multi-line-comments', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(\n    vec2  a,\n    /* test 1 */\n    vec3  b,\n    /* test 2 */\n    float  c\n    /* test 3 */\n  ) {\n    vec3 delta = a - b;\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'b', 'c'],\n    argumentTypes: ['Array(2)', 'Array(3)', 'Number']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function, multiple arguments on multi line with spaces and in-line-comments', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(\n    vec2  a, // test 1\n    vec4  b, // test 2\n    int  c // test 3\n  ) {\n    vec3 delta = a - b;\n  }`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'b', 'c'],\n    argumentTypes: ['Array(2)', 'Array(4)', 'Integer']\n  });\n});\n\ntest('nativeFunctionArguments() parse simple function that is cut short', () => {\n  const result = GLKernel.nativeFunctionArguments(`vec2 myFunction(\n    vec2  a,\n    vec3  b,\n    float  c\n  )`);\n\n  assert.deepEqual(result, {\n    argumentNames: ['a', 'b', 'c'],\n    argumentTypes: ['Array(2)', 'Array(3)', 'Number']\n  });\n});\n\ntest('getVariablePrecisionString() when tactic is set to \"speed\" returns \"lowp\"', () => {\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call({ tactic: 'speed' }), 'lowp');\n});\n\ntest('getVariablePrecisionString() when tactic is set to \"balanced\" returns \"mediump\"', () => {\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call({ tactic: 'balanced' }), 'mediump');\n});\n\ntest('getVariablePrecisionString() when tactic is set to \"precision\" returns \"highp\"', () => {\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call({ tactic: 'precision' }), 'highp');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within lowFloatPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowFloatPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumFloatPrecision: { rangeMax: Math.log2(4 * 4) },\n        highFloatPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [2, 2];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize), 'lowp');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within mediumFloatPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowFloatPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumFloatPrecision: { rangeMax: Math.log2(4 * 4) },\n        highFloatPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [4, 4];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize), 'mediump');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within highFloatPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowFloatPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumFloatPrecision: { rangeMax: Math.log2(4 * 4) },\n        highFloatPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [5, 5];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize), 'highp');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is outside highFloatPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowFloatPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumFloatPrecision: { rangeMax: Math.log2(4 * 4) },\n        highFloatPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [6, 6];\n  assert.throws(() => GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize));\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within lowIntPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowIntPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumIntPrecision: { rangeMax: Math.log2(4 * 4) },\n        highIntPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [2, 2];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize, null, true), 'lowp');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within mediumIntPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowIntPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumIntPrecision: { rangeMax: Math.log2(4 * 4) },\n        highIntPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [4, 4];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize, null, true), 'mediump');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is within highIntPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowIntPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumIntPrecision: { rangeMax: Math.log2(4 * 4) },\n        highIntPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [5, 5];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize, null, true), 'highp');\n});\n\ntest('getVariablePrecisionString() when tactic is not set and texSize is outside highIntPrecision', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        lowIntPrecision: { rangeMax: Math.log2(3 * 3) },\n        mediumIntPrecision: { rangeMax: Math.log2(4 * 4) },\n        highIntPrecision: { rangeMax: Math.log2(5 * 5) },\n        isSpeedTacticSupported: true,\n      }\n    }\n  };\n  const textureSize = [6, 6];\n  assert.throws(() => GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize, null, true));\n});\n\ntest('getVariablePrecisionString() when features.isSpeedTacticSupported is false returns \"highp\"', () => {\n  const mockInstance = {\n    tactic: null,\n    constructor: {\n      features: {\n        isSpeedTacticSupported: false,\n      }\n    }\n  };\n  const textureSize = [1, 1];\n  assert.equal(GLKernel.prototype.getVariablePrecisionString.call(mockInstance, textureSize, null, true), 'highp');\n});\n\nfunction testGetFeatures(canvas, context) {\n  const gpu = new GPU({ canvas, context });\n  const { Kernel } = gpu;\n  Kernel.setupFeatureChecks();\n  const features = Kernel.getFeatures();\n  assert.ok(typeof features.isFloatRead === 'boolean');\n  assert.ok(typeof features.isIntegerDivisionAccurate === 'boolean');\n  assert.ok(typeof features.isSpeedTacticSupported === 'boolean');\n  assert.ok(typeof features.isTextureFloat === 'boolean');\n  assert.ok(typeof features.isDrawBuffers === 'boolean');\n  assert.ok(typeof features.kernelMap === 'boolean');\n  assert.ok(typeof features.channelCount === 'number');\n  assert.ok(typeof features.maxTextureSize === 'number');\n\n  assert.ok(typeof features.lowIntPrecision.rangeMax === 'number');\n  assert.ok(typeof features.mediumIntPrecision.rangeMax === 'number');\n  assert.ok(typeof features.highIntPrecision.rangeMax === 'number');\n\n  assert.ok(typeof features.lowFloatPrecision.rangeMax === 'number');\n  assert.ok(typeof features.mediumFloatPrecision.rangeMax === 'number');\n  assert.ok(typeof features.highFloatPrecision.rangeMax === 'number');\n\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('getFeatures() webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  testGetFeatures(canvas, context);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('getFeatures() webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  testGetFeatures(canvas, context);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('getFeatures() headlessgl', () => {\n  const canvas = null;\n  const context = require('gl')(1, 1);\n  testGetFeatures(canvas, context);\n});\n\ntest('setOutput() throws when not dynamicOutput and already compiled', () => {\n  assert.throws(() => {\n    GLKernel.prototype.setOutput.call({\n      program: {},\n      toKernelOutput: () => {},\n      dynamicOutput: false\n    });\n  }, new Error('Resizing a kernel with dynamicOutput: false is not possible'));\n});\n\ntest('setOutput() when not dynamicOutput and not already compiled', () => {\n  const mockInstance = {\n    toKernelOutput: () => [100, 100],\n    dynamicOutput: false,\n    output: null,\n  };\n  GLKernel.prototype.setOutput.call(mockInstance, [100, 100]);\n  assert.deepEqual(mockInstance.output, [100, 100]);\n});\n\ntest('setOutput() when does not need to trigger recompile', () => {\n  const mockContext = {\n    bindFramebuffer: sinon.spy(),\n    FRAMEBUFFER: 'FRAMEBUFFER',\n    viewport: sinon.spy()\n  };\n  const mockTexture = {\n    delete: sinon.spy(),\n  };\n  const mockMappedTexture = {\n    delete: sinon.spy()\n  };\n  const mock_setupOutputTexture = sinon.spy();\n  const mock_setupSubOutputTextures = sinon.spy();\n  const mockInstance = {\n    context: mockContext,\n    program: {},\n    texSize: [1, 1],\n    framebuffer: {\n      width: 0,\n      height: 0,\n    },\n    toKernelOutput: GLKernel.prototype.toKernelOutput,\n    dynamicOutput: true,\n    getVariablePrecisionString: () => {\n      return 'lowp';\n    },\n    switchKernels: sinon.spy(),\n    updateMaxTexSize: sinon.spy(),\n    maxTexSize: [123, 321],\n    canvas: {\n      width: 0,\n      height: 0,\n    },\n    texture: mockTexture,\n    mappedTextures: [\n      mockMappedTexture\n    ],\n    _setupOutputTexture: mock_setupOutputTexture,\n    _setupSubOutputTextures: mock_setupSubOutputTextures,\n  };\n  GLKernel.prototype.setOutput.call(mockInstance, [100, 100]);\n  assert.equal(mockContext.bindFramebuffer.callCount, 1);\n  assert.equal(mockContext.bindFramebuffer.args[0][0], 'FRAMEBUFFER');\n  assert.equal(mockContext.bindFramebuffer.args[0][1], mockInstance.framebuffer);\n  assert.equal(mockInstance.updateMaxTexSize.callCount, 1);\n  assert.equal(mockInstance.framebuffer.width, 100);\n  assert.equal(mockInstance.framebuffer.height, 100);\n  assert.equal(mockContext.viewport.callCount, 1);\n  assert.equal(mockContext.viewport.args[0][0], 0);\n  assert.equal(mockContext.viewport.args[0][1], 0);\n  assert.equal(mockContext.viewport.args[0][2], 123);\n  assert.equal(mockContext.viewport.args[0][3], 321);\n  assert.equal(mockInstance.canvas.width, 123);\n  assert.equal(mockInstance.canvas.height, 321);\n  assert.equal(mockInstance.texture, null);\n  assert.equal(mockInstance.mappedTextures, null);\n  assert.ok(mockTexture.delete.called);\n  assert.ok(mockMappedTexture.delete.called);\n  assert.ok(mock_setupOutputTexture.called);\n  assert.ok(mock_setupSubOutputTextures.called);\n});\n\ntest('setOutput() when needs to trigger recompile', () => {\n  const mockInstance = {\n    program: {},\n    texSize: [1, 1],\n    toKernelOutput: GLKernel.prototype.toKernelOutput,\n    dynamicOutput: true,\n    getVariablePrecisionString: (textureSize) => {\n      if (textureSize[0] === 1) return 'lowp';\n      return 'highp';\n    },\n    switchKernels: sinon.spy()\n  };\n  GLKernel.prototype.setOutput.call(mockInstance, [100, 100]);\n  assert.ok(mockInstance.switchKernels.callCount, 1);\n});"
  },
  {
    "path": "test/internal/backend/headless-gl/kernel/index.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\n\ndescribe('internal: HeadlessGLKernel');\n\n(typeof global !== 'undefined' ? test : skip)('.setupFeatureChecks() should not blow up, even if global WebGLRenderingContext is available', () => {\n  global.WebGLRenderingContext = {};\n  global.document = {\n    createElement: () => {\n      return {};\n    }\n  };\n  // this is done late on purpose!  Do not change this, as it causes HeadlessGL to initialize with certain values\nconst { HeadlessGLKernel } = require('../../../../../src');\n  HeadlessGLKernel.setupFeatureChecks();\n  assert.ok(true);\n  delete global.document;\n  delete global.WebGLRenderingContext;\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/astBinaryExpression.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.astBinaryExpression()');\n\n\ntest('divide float & float', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number', 'Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left, float user_right) {'\n    + '\\nreturn (user_left/user_right);'\n    + '\\n}');\n});\n\ntest('divide float & int', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number', 'Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left, int user_right) {'\n    + '\\nreturn (user_left/float(user_right));'\n    + '\\n}');\n});\n\ntest('divide float & literal float', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return left / 1.1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left) {'\n    + '\\nreturn (user_left/1.1);'\n    + '\\n}');\n});\n\ntest('divide float & literal integer', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return left / 1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left) {'\n    + '\\nreturn (user_left/1.0);'\n    + '\\n}');\n});\n\ntest('divide float & Input', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right[this.thread.x];\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number', 'Input'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left, sampler2D user_right,ivec2 user_rightSize,ivec3 user_rightDim) {'\n    + '\\nreturn (user_left/get32(user_right, user_rightSize, user_rightDim, 0, 0, threadId.x));'\n    + '\\n}');\n});\n\ntest('divide int & float', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer', 'Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left, float user_right) {'\n    + '\\nreturn float((user_left/int(user_right)));'\n    + '\\n}');\n});\n\ntest('divide int & int', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer', 'Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left, int user_right) {'\n    + '\\nreturn float((user_left/user_right));'\n    + '\\n}');\n});\n\ntest('divide int & literal float', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return left / 1.1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left) {'\n    + '\\nreturn float((user_left/1));'\n    + '\\n}');\n});\n\ntest('divide int & literal integer', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return left / 1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left) {'\n    + '\\nreturn float((user_left/1));'\n    + '\\n}');\n});\n\ntest('divide int & Input', () => {\n  const node = new WebGLFunctionNode(`function kernel(left, right) {\n    return left / right[this.thread.x];\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer', 'Input'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left, sampler2D user_right,ivec2 user_rightSize,ivec3 user_rightDim) {'\n    + '\\nreturn float((user_left/int(get32(user_right, user_rightSize, user_rightDim, 0, 0, threadId.x))));'\n    + '\\n}');\n});\n\ntest('divide literal integer & float', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return 1 / left;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_left) {'\n    + '\\nreturn (1.0/user_left);'\n    + '\\n}');\n});\n\ntest('divide literal integer & int', () => {\n  const node = new WebGLFunctionNode(`function kernel(left) {\n    return 1 / left;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_left) {'\n    + '\\nreturn float((1/user_left));'\n    + '\\n}');\n});\n\ntest('divide literal integer & literal float', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return 1 / 1.1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: []\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn (1.0/1.1);'\n    + '\\n}');\n});\n\ntest('divide literal integer & literal integer', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return 1 / 1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: []\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn (1.0/1.0);'\n    + '\\n}');\n});\n\ntest('divide literal integer & Input', () => {\n  const node = new WebGLFunctionNode(`function kernel(v) {\n    return 1 / v[this.thread.x];\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Input'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v,ivec2 user_vSize,ivec3 user_vDim) {'\n    + '\\nreturn (1.0/get32(user_v, user_vSize, user_vDim, 0, 0, threadId.x));'\n    + '\\n}');\n});\n\ntest('divide literal float & float', () => {\n  const node = new WebGLFunctionNode(`function kernel(right) {\n    return 1.1 / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_right) {'\n    + '\\nreturn (1.1/user_right);'\n    + '\\n}');\n});\n\ntest('divide literal float & int', () => {\n  const node = new WebGLFunctionNode(`function kernel(right) {\n    return 1.1 / right;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Integer']\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_right) {'\n    + '\\nreturn (1.1/float(user_right));'\n    + '\\n}');\n});\n\ntest('divide literal float & literal float', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return 1.1 / 1.1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: []\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn (1.1/1.1);'\n    + '\\n}');\n});\n\ntest('divide literal float & literal integer', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return 1.1 / 1;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: []\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn (1.1/1.0);'\n    + '\\n}');\n});\n\ntest('divide literal float & Input', () => {\n  const node = new WebGLFunctionNode(`function kernel(v) {\n    return 1.1 / v[this.thread.x];\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: ['Input'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v,ivec2 user_vSize,ivec3 user_vDim) {'\n    + '\\nreturn (1.1/get32(user_v, user_vSize, user_vDim, 0, 0, threadId.x));'\n    + '\\n}');\n});\n\ntest('divide this.thread.x by this.output.x and multiple, integer, integer, and float with this.fixIntegerDivisionAccuracy = false', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return (this.thread.x / this.output.x) * 4;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: [],\n    lookupFunctionArgumentBitRatio: () => 4,\n    fixIntegerDivisionAccuracy: false,\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn float(((threadId.x/1)*4));'\n    + '\\n}');\n});\n\ntest('divide this.thread.x by this.output.x and multiple, integer, integer, and float with this.fixIntegerDivisionAccuracy = true', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    return (this.thread.x / this.output.x) * 4;\n  }`, {\n    returnType: 'Number',\n    output: [1],\n    argumentTypes: [],\n    lookupFunctionArgumentBitRatio: () => 4,\n    fixIntegerDivisionAccuracy: true,\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nreturn (divWithIntCheck(float(threadId.x), 1.0)*4.0);'\n    + '\\n}');\n});\n\ntest('multiply Input and Input', () => {\n  const node = new WebGLFunctionNode('function kernel(v1, v2) {'\n    + '\\n return v1[this.thread.x] * v2[this.thread.x];'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['Input', 'Input'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v1,ivec2 user_v1Size,ivec3 user_v1Dim, sampler2D user_v2,ivec2 user_v2Size,ivec3 user_v2Dim) {'\n    + '\\nreturn (get32(user_v1, user_v1Size, user_v1Dim, 0, 0, threadId.x)*get32(user_v2, user_v2Size, user_v2Dim, 0, 0, threadId.x));'\n    + '\\n}');\n});\n\ntest('multiply Input and int', () => {\n  const node = new WebGLFunctionNode('function kernel(v1, v2) {'\n    + '\\n return v1[this.thread.x] * v2;'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['Input', 'Integer'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v1,ivec2 user_v1Size,ivec3 user_v1Dim, int user_v2) {'\n    + '\\nreturn (get32(user_v1, user_v1Size, user_v1Dim, 0, 0, threadId.x)*float(user_v2));'\n    + '\\n}');\n});\n\ntest('multiply Input and float', () => {\n  const node = new WebGLFunctionNode('function kernel(v1, v2) {'\n    + '\\n return v1[this.thread.x] * v2;'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['Input', 'Float'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v1,ivec2 user_v1Size,ivec3 user_v1Dim, float user_v2) {'\n    + '\\nreturn (get32(user_v1, user_v1Size, user_v1Dim, 0, 0, threadId.x)*user_v2);'\n    + '\\n}');\n});\n\ntest('multiply Input and Number', () => {\n  const node = new WebGLFunctionNode('function kernel(v1, v2) {'\n    + '\\n return v1[this.thread.x] * v2;'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['Input', 'Number'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v1,ivec2 user_v1Size,ivec3 user_v1Dim, float user_v2) {'\n    + '\\nreturn (get32(user_v1, user_v1Size, user_v1Dim, 0, 0, threadId.x)*user_v2);'\n    + '\\n}');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/astCallExpression.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.astCallExpression()');\n\ntest('handles Math.abs with floats', () => {\n  const node = new WebGLFunctionNode(`function kernel(v) {\n    return Math.abs(v);\n  }`, { output: [1], argumentTypes: ['Number'] });\n  assert.equal(node.toString(), 'float kernel(float user_v) {'\n    + '\\nreturn abs(user_v);'\n    + '\\n}');\n});\ntest('handles Math.abs with ints', () => {\n  const node = new WebGLFunctionNode(`function kernel(v) {\n    return Math.abs(v);\n  }`, { output: [1], argumentTypes: ['Integer'] });\n  assert.equal(node.toString(), 'float kernel(int user_v) {'\n    + '\\nreturn abs(float(user_v));'\n    + '\\n}');\n});\ntest('handles Math.pow with floats', () => {\n  const node = new WebGLFunctionNode(`function kernel(v, v2) {\n    return Math.pow(v, v2);\n  }`, { output: [1], argumentTypes: ['Number', 'Number'] });\n  assert.equal(node.toString(), 'float kernel(float user_v, float user_v2) {'\n    + '\\nreturn _pow(user_v, user_v2);'\n    + '\\n}');\n});\ntest('handles Math.pow with mixed 1', () => {\n  const node = new WebGLFunctionNode(`function kernel(v, v2) {\n    return Math.pow(v, v2);\n  }`, { output: [1], argumentTypes: ['Number', 'Integer'] });\n  assert.equal(node.toString(), 'float kernel(float user_v, int user_v2) {'\n    + '\\nreturn _pow(user_v, float(user_v2));'\n    + '\\n}');\n});\ntest('handles Math.pow with mixed 2', () => {\n  const node = new WebGLFunctionNode(`function kernel(v, v2) {\n    return Math.pow(v, v2);\n  }`, { output: [1], argumentTypes: ['Integer', 'Number'] });\n  assert.equal(node.toString(), 'float kernel(int user_v, float user_v2) {'\n    + '\\nreturn _pow(float(user_v), user_v2);'\n    + '\\n}');\n});\ntest('handles Math.pow with ints', () => {\n  const node = new WebGLFunctionNode(`function kernel(v, v2) {\n    return Math.pow(v, v2);\n  }`, { output: [1], argumentTypes: ['Integer', 'Integer'] });\n  assert.equal(node.toString(), 'float kernel(int user_v, int user_v2) {'\n    + '\\nreturn _pow(float(user_v), float(user_v2));'\n    + '\\n}');\n});\ntest('handles argument of type Input', () => {\n  let lookupReturnTypeCalls = 0;\n  let lookupFunctionArgumentTypes = 0;\n  const node = new WebGLFunctionNode('function kernel(v) {'\n    + '\\n return childFunction(v);'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['Input'],\n    needsArgumentType: () => false,\n    lookupReturnType: (functionName) => {\n      lookupReturnTypeCalls++;\n      if (functionName === 'childFunction') {\n        return 'Number';\n      }\n      throw new Error(`unhanded lookupReturnType for ${functionName}`);\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      lookupFunctionArgumentTypes++;\n      if (functionName === 'childFunction') {\n        return ['Input'];\n      }\n      throw new Error(`unhanded lookupFunctionArgumentTypes for ${functionName}`);\n    },\n    triggerImplyArgumentBitRatio: () => {},\n    assignArgumentType: () => {}\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2D user_v,ivec2 user_vSize,ivec3 user_vDim) {'\n    + '\\nreturn childFunction(user_v,user_vSize,user_vDim);'\n    + '\\n}');\n  assert.equal(lookupReturnTypeCalls, 2);\n  assert.equal(lookupFunctionArgumentTypes, 1);\n});\ntest('handles argument of type HTMLImageArray', () => {\n  let lookupReturnTypeCalls = 0;\n  let lookupFunctionArgumentTypes = 0;\n  const node = new WebGLFunctionNode('function kernel(v) {'\n    + '\\n return childFunction(v);'\n    + '\\n}', {\n    output: [1],\n    argumentTypes: ['HTMLImageArray'],\n    needsArgumentType: () => false,\n    lookupReturnType: (functionName) => {\n      lookupReturnTypeCalls++;\n      if (functionName === 'childFunction') {\n        return 'Number';\n      }\n      throw new Error(`unhanded lookupReturnType for ${functionName}`);\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      lookupFunctionArgumentTypes++;\n      if (functionName === 'childFunction') {\n        return ['HTMLImageArray'];\n      }\n      throw new Error(`unhanded lookupFunctionArgumentTypes for ${functionName}`);\n    },\n    triggerImplyArgumentBitRatio: () => {},\n    assignArgumentType: () => {}\n  });\n  assert.equal(node.toString(), 'float kernel(sampler2DArray user_v,ivec2 user_vSize,ivec3 user_vDim) {'\n    + '\\nreturn childFunction(user_v,user_vSize,user_vDim);'\n    + '\\n}');\n  assert.equal(lookupReturnTypeCalls, 2);\n  assert.equal(lookupFunctionArgumentTypes, 1);\n});\ntest('handles argument types of CallExpression that return arrays', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const p = [this.thread.x, this.thread.y];\n    const z = array2(array2(p, 0.01), 0.02);\n    return 1.0;\n  }`, {\n    output: [1, 1],\n    lookupReturnType: () => 'Number',\n    needsArgumentType: () => false,\n    lookupFunctionArgumentTypes: () => [],\n    triggerImplyArgumentType: () => {}\n  });\n  assert.equal(node.toString(), `float kernel() {\nvec2 user_p=vec2(threadId.x, threadId.y);\nfloat user_z=array2(array2(user_p, 0.01), 0.02);\nreturn 1.0;\n}`);\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/astForStatement.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.astForStatement()');\n\ntest('with safe loop with init', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0;i < 100; i++) {\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nint user_sum=0;'\n    + '\\nfor (int user_i=0;(user_i<100);user_i++){'\n    + '\\nuser_sum++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with safe loop with init and if', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0;i < 100; i++) {\n      if (i > 50) {\n        sum++;\n      }\n    }\n    return sum;\n  }`, {\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nint user_sum=0;'\n    + '\\nfor (int user_i=0;(user_i<100);user_i++){'\n    + '\\nif ((user_i>50)){'\n    + '\\nuser_sum++;}'\n    + '\\n}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with safe loop with no init', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    const i = 0;\n    for (;i < 100; i++) {\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nint user_sum=0;'\n    + '\\nint user_i=0;'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif (!(user_i<100)) break;'\n    + '\\nuser_sum++;'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with safe loop with no test', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0;; i++) {\n      if (i > 100) break;\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nint user_sum=0;'\n    + '\\nint user_i=0;'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif ((user_i>100)) {'\n    + '\\nbreak;'\n    + '\\n}'\n    + '\\nuser_sum++;'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with unsafe loop with init', () => {\n  const node = new WebGLFunctionNode(`function kernel(arg1) {\n    let sum = 0;\n    for (let i = 0 + arg1;i < 100; i++) {\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_arg1) {'\n    + '\\nint user_sum=0;'\n    + '\\nfloat user_i=(0.0+user_arg1);'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif (!(user_i<100.0)) break;'\n    + '\\nuser_sum++;'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with unsafe loop with no init', () => {\n  const node = new WebGLFunctionNode(`function kernel(arg1) {\n    let sum = 0;\n    let i = 0 + arg1;\n    for (;i < 100; i++) {\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_arg1) {'\n    + '\\nint user_sum=0;'\n    + '\\nfloat user_i=(0.0+user_arg1);'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif (!(user_i<100.0)) break;'\n    + '\\nuser_sum++;'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('with unsafe loop with no init reversed', () => {\n  const node = new WebGLFunctionNode(`function kernel(arg1) {\n    let sum = 0;\n    let i = 0 + arg1;\n    for (;100 > i; i++) {\n      sum++;\n    }\n    return sum;\n  }`, {\n    output: [1],\n    argumentTypes: ['Number']\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_arg1) {'\n    + '\\nint user_sum=0;'\n    + '\\nfloat user_i=(0.0+user_arg1);'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif (!(100.0>user_i)) break;'\n    + '\\nuser_sum++;'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\n\ntest('nested safe loop', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0; i < 100; i++) {\n      for (let j = 0; j < 100; j++) {\n        sum++;\n      }\n    }\n    return sum;\n  }`, {\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nint user_sum=0;'\n    + '\\nfor (int user_i=0;(user_i<100);user_i++){'\n    + '\\nfor (int user_j=0;(user_j<100);user_j++){'\n    + '\\nuser_sum++;}'\n    + '\\n}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('nested unsafe loop', () => {\n  const node = new WebGLFunctionNode(`function kernel(arg1, arg2) {\n    let sum = 0;\n    for (let i = arg1; i < 100; i++) {\n      for (let j = arg2; j < 100; j++) {\n        sum++;\n      }\n    }\n    return sum;\n  }`, {\n    argumentTypes: ['Number', 'Number'],\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_arg1, float user_arg2) {'\n    + '\\nint user_sum=0;'\n    + '\\nfloat user_i=user_arg1;'\n    + '\\nfor (int safeI2=0;safeI2<LOOP_MAX;safeI2++){'\n    + '\\nif (!(user_i<100.0)) break;'\n    + '\\nfloat user_j=user_arg2;'\n    + '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){'\n    + '\\nif (!(user_j<100.0)) break;'\n    + '\\nuser_sum++;'\n    + '\\nuser_j++;}'\n    + '\\n'\n    + '\\nuser_i++;}'\n    + '\\n'\n    + '\\nreturn float(user_sum);'\n    + '\\n}');\n});\n\ntest('this.output.x usage inside loop', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0; i < this.output.x; i++) {\n      sum += 1;\n    }\n    return sum;\n  }`, {\n    argumentTypes: [],\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nfloat user_sum=0.0;'\n    + '\\nfor (int user_i=0;(user_i<1);user_i++){'\n    + '\\nuser_sum+=1.0;}'\n    + '\\n'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n});\n\ntest('this.thread.x usage inside loop', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    for (let i = 0; i < this.thread.x; i++) {\n      sum += 1;\n    }\n    return sum;\n  }`, {\n    argumentTypes: [],\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nfloat user_sum=0.0;'\n    + '\\nfor (int user_i=0;(user_i<threadId.x);user_i++){'\n    + '\\nuser_sum+=1.0;}'\n    + '\\n'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n});\n\ntest('this.thread.x usage outside loop', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    let sum = 0;\n    let x = this.thread.x;\n    for (let i = 0; i < x; i++) {\n      sum += 1;\n    }\n    return sum;\n  }`, {\n    argumentTypes: [],\n    output: [1]\n  });\n\n  assert.equal(node.toString(), 'float kernel() {'\n    + '\\nfloat user_sum=0.0;'\n    + '\\nfloat user_x=float(threadId.x);'\n    + '\\nfor (int user_i=0;(user_i<int(user_x));user_i++){'\n    + '\\nuser_sum+=1.0;}'\n    + '\\n'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/astVariableDeclaration.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.getVariableSignature()');\n\nfunction run(value, settings) {\n  const node = new WebGLFunctionNode(`function fn() {\n    ${ value };\n  }`, Object.assign({ output: [1, 2, 3] }, settings));\n\n  const ast = node.getJsAST();\n  node.traceFunctionAST(ast);\n  assert.equal(ast.type, 'FunctionExpression');\n  assert.equal(ast.body.type, 'BlockStatement');\n  assert.equal(ast.body.body[0].type, 'VariableDeclaration');\n  return node.astVariableDeclaration(ast.body.body[0], []).join('');\n}\n\ntest('value float', () => {\n  assert.equal(run('const value = it', {\n    argumentNames: ['it'],\n    argumentTypes: ['Number']\n  }), 'float user_value=user_it;');\n});\n\ntest('value int', () => {\n  assert.equal(run('const value = it', {\n    argumentNames: ['it'],\n    argumentTypes: ['Integer']\n  }), 'float user_value=float(user_it);');\n});\n\ntest('value[] float', () => {\n  assert.equal(run('const value = it[1]', {\n    argumentNames: ['it'],\n    argumentTypes: ['Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value=get32(user_it, user_itSize, user_itDim, 0, 0, 1);');\n});\n\ntest('value[][] float', () => {\n  assert.equal(run('const value = it[1][2]', {\n    argumentNames: ['it'],\n    argumentTypes: ['Array2D'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value=get32(user_it, user_itSize, user_itDim, 0, 1, 2);');\n});\n\ntest('value[][][] float', () => {\n  assert.equal(run('const value = it[1][2][3]', {\n    argumentNames: ['it'],\n    argumentTypes: ['Array3D'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value=get32(user_it, user_itSize, user_itDim, 1, 2, 3);');\n});\n\ntest('Array2 value[] from value[]', () => {\n  assert.equal(run('const value = [arg1[0], arg2[0]];', {\n    argumentNames: ['arg1', 'arg2'],\n    argumentTypes: ['Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'vec2 user_value=vec2('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0)'\n    + ');');\n});\n\ntest('Array3 value[] from value[]', () => {\n  assert.equal(run('const value = [arg1[0], arg2[0], arg3[0]];', {\n    argumentNames: ['arg1', 'arg2', 'arg3'],\n    argumentTypes: ['Array', 'Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'vec3 user_value=vec3('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0)'\n    + ');');\n});\n\ntest('Array4 value[] from value[]', () => {\n  assert.equal(run('const value = [arg1[0], arg2[0], arg3[0], arg4[0]];', {\n    argumentNames: ['arg1', 'arg2', 'arg3', 'arg4'],\n    argumentTypes: ['Array', 'Array', 'Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'vec4 user_value=vec4('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0), '\n    + 'get32(user_arg4, user_arg4Size, user_arg4Dim, 0, 0, 0)'\n    + ');');\n});\n\ntest('float, Array2, Array3 chain values', () => {\n  assert.equal(run('const value1 = 1, '\n    + 'value2 = [arg1[0], arg2[0]], '\n    + 'value3 = [arg1[0], arg2[0], arg3[0]], '\n    + 'value4 = [arg1[0], arg2[0], arg3[0], arg4[0]];', {\n    argumentNames: ['arg1', 'arg2', 'arg3', 'arg4'],\n    argumentTypes: ['Array', 'Array', 'Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value1=1.0;'\n    + 'vec2 user_value2=vec2('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0)'\n    + ');'\n    + 'vec3 user_value3=vec3('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0)'\n    + ');'\n    + 'vec4 user_value4=vec4('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0), '\n    + 'get32(user_arg4, user_arg4Size, user_arg4Dim, 0, 0, 0)'\n    + ');');\n});\n\ntest('float, Array2, Array3, Array4 multiple values', () => {\n  assert.equal(run('const value1 = 1, '\n    + 'value2 = [arg1[0], arg2[0]], '\n    + 'value3 = [arg1[0], arg2[0], arg3[0]], '\n    + 'value4 = [arg1[0], arg2[0], arg3[0], arg4[0]];', {\n    argumentNames: ['arg1', 'arg2', 'arg3', 'arg4'],\n    argumentTypes: ['Array', 'Array', 'Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value1=1.0;'\n    + 'vec2 user_value2=vec2('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0)'\n    + ');'\n    + 'vec3 user_value3=vec3('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0)'\n    + ');'\n    + 'vec4 user_value4=vec4('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0), '\n    + 'get32(user_arg4, user_arg4Size, user_arg4Dim, 0, 0, 0)'\n    + ');');\n});\n\ntest('float, float, Array4, Array4, Array4 chain values', () => {\n  assert.equal(run('const value1 = 1, value2 = 1.5, '\n    + 'value3 = [arg1[0], arg2[0], arg3[0], arg4[0]], '\n    + 'value4 = [arg4[4], arg3[4], arg2[4], arg1[4]], '\n    + 'value5 = [arg2[1], arg2[2], arg2[3], arg2[4]];', {\n    argumentNames: ['arg1', 'arg2', 'arg3', 'arg4'],\n    argumentTypes: ['Array', 'Array', 'Array', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  }), 'float user_value1=1.0,user_value2=1.5;'\n    + 'vec4 user_value3=vec4('\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 0), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 0), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 0), '\n    + 'get32(user_arg4, user_arg4Size, user_arg4Dim, 0, 0, 0)'\n    + '),'\n    + 'user_value4=vec4('\n    + 'get32(user_arg4, user_arg4Size, user_arg4Dim, 0, 0, 4), '\n    + 'get32(user_arg3, user_arg3Size, user_arg3Dim, 0, 0, 4), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 4), '\n    + 'get32(user_arg1, user_arg1Size, user_arg1Dim, 0, 0, 4)'\n    + '),'\n    + 'user_value5=vec4('\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 1), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 2), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 3), '\n    + 'get32(user_arg2, user_arg2Size, user_arg2Dim, 0, 0, 4)'\n    + ');');\n});\n\ntest('float literal, float literal, multiple values', () => {\n  assert.equal(run('const value1 = 0, '\n    + 'value2 = 0;', {\n    argumentNames: [],\n    argumentTypes: []\n  }), 'float user_value1=0.0,user_value2=0.0;');\n});\n\ntest('float literal, float literal, multiple values', () => {\n  assert.equal(run('const value1 = 0, '\n    + 'value2 = 0;', {\n    argumentNames: [],\n    argumentTypes: []\n  }), 'float user_value1=0.0,user_value2=0.0;');\n});\n\ntest('this.constant.value throws', () => {\n  assert.throws(() => {\n    run('const value=this.constant.it');\n  });\n});\n\ntest('this.constants.value without constantTypes declared', () => {\n  assert.throws(() => {\n    run('const value=this.constants.it')\n  });\n});\n\ntest('this.constants.value float', () => {\n  assert.equal(run('const value = this.constants.it', {\n    constantTypes: { it: 'Number' }\n  }), 'float user_value=constants_it;');\n});\n\ntest('this.constants.value int', () => {\n  assert.equal(run('const value = this.constants.it', {\n    constantTypes: {\n      it: 'Integer'\n    }\n  }), 'float user_value=float(constants_it);');\n});\n\ntest('this.constants.value[] float', () => {\n  assert.equal(run('const value = this.constants.it[1]', {\n    constantTypes: {\n      it: 'Array'\n    },\n    constantBitRatios: { it: 4 },\n  }), 'float user_value=get32(constants_it, constants_itSize, constants_itDim, 0, 0, 1);');\n});\n\ntest('this.constants.value[][] float', () => {\n  assert.equal(run('const value = this.constants.it[1][2]', {\n    constantTypes: {\n      it: 'Array2D'\n    },\n    constantBitRatios: { it: 4 },\n  }), 'float user_value=get32(constants_it, constants_itSize, constants_itDim, 0, 1, 2);');\n});\n\ntest('this.constants.value[][][] float', () => {\n  assert.equal(run('const value = this.constants.it[1][2][3]', {\n    constantTypes: {\n      it: 'Array3D'\n    },\n    constantBitRatios: { it: 4 },\n  }), 'float user_value=get32(constants_it, constants_itSize, constants_itDim, 1, 2, 3);');\n});\n\ntest('this.thread.x float', () => {\n  assert.equal(run('const value = this.thread.x'), 'float user_value=float(threadId.x);');\n});\n\ntest('this.thread.y float', () => {\n  assert.equal(run('const value = this.thread.y'), 'float user_value=float(threadId.y);');\n});\n\ntest('this.thread.z float', () => {\n  assert.equal(run('const value = this.thread.z'), 'float user_value=float(threadId.z);');\n});\n\ntest('this.output.x float', () => {\n  assert.equal(run('const value = this.output.x'), 'float user_value=1.0;');\n});\n\ntest('this.output.y float', () => {\n  assert.equal(run('const value = this.output.y'), 'float user_value=2.0;');\n});\n\ntest('this.output.z float', () => {\n  assert.equal(run('const value = this.output.z'), 'float user_value=3.0;');\n});\n\ntest('this.outputs.z throws', () => {\n  assert.throws(() => {\n    run('const value = this.outputs.z');\n  });\n});\n\ntest('Math.E Number float', () => {\n  assert.equal(run('const value = Math.E'), `float user_value=${Math.E.toString()};`);\n});\n\ntest('Math.E Number float', () => {\n  assert.equal(run('const value = Math.E'), `float user_value=${Math.E.toString()};`);\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/contexts.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.contexts');\n\ntest('safe from literal 1', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const const1 = 1;\n    const const2 = const1 + 2;\n    const const3 = const2 + 3;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1] });\n\n  node.toString();\n  const { const1, const2, const3 } = node.contexts[1];\n  assert.equal(const1.isSafe, true);\n  assert.deepEqual(const1.dependencies, [\n    {\n      origin: 'literal',\n      value: 1,\n      isSafe: true,\n    }\n  ]);\n  assert.equal(const2.isSafe, true);\n  assert.deepEqual(const2.dependencies, [\n    {\n      name: 'const1',\n      origin: 'declaration',\n      isSafe: true\n    },{\n      origin: 'literal',\n      value: 2,\n      isSafe: true,\n    }\n  ]);\n  assert.equal(const3.isSafe, true);\n  assert.deepEqual(const3.dependencies, [\n    {\n      name: 'const2',\n      origin: 'declaration',\n      isSafe: true\n    },{\n      origin: 'literal',\n      value: 3,\n      isSafe: true,\n    }\n  ]);\n});\n\n\ntest('safe from argument', () => {\n  const node = new WebGLFunctionNode(`function kernel(arg1) {\n    const const1 = arg1 + 3;\n    const const2 = const1 + 2;\n    const const3 = const2 + 1;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1], argumentTypes: ['Number'] });\n\n  node.toString();\n  const { const1, const2, const3 } = node.contexts[1];\n  assert.equal(const1.isSafe, false);\n  assert.deepEqual(const1.dependencies, [\n    {\n      name: 'arg1',\n      origin: 'argument',\n      isSafe: false\n    },{\n      origin: 'literal',\n      value: 3,\n      isSafe: true,\n    }\n  ]);\n  assert.equal(const2.isSafe, false);\n  assert.deepEqual(const2.dependencies, [\n    {\n      name: 'const1',\n      origin: 'declaration',\n      isSafe: false\n    },{\n      origin: 'literal',\n      value: 2,\n      isSafe: true,\n    }\n  ]);\n  assert.equal(const3.isSafe, false);\n  assert.deepEqual(const3.dependencies, [\n    {\n      name: 'const2',\n      origin: 'declaration',\n      isSafe: false\n    },{\n      origin: 'literal',\n      value: 1,\n      isSafe: true,\n    }\n  ]);\n});\n\n\ntest('safe from multiplication', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const const1 = 555;\n    const const2 = const1 + .555;\n    const const3 = const2 * .1;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1] });\n\n  node.toString();\n  const { const1, const2, const3 } = node.contexts[1];\n  assert.deepEqual(const1.dependencies, [\n    {\n      origin: 'literal',\n      value: 555,\n      isSafe: true,\n    }\n  ]);\n  assert.deepEqual(const2.dependencies, [\n    {\n      name: 'const1',\n      origin: 'declaration',\n      isSafe: true,\n    },{\n      origin: 'literal',\n      value: .555,\n      isSafe: true,\n    }\n  ]);\n  assert.deepEqual(const3.dependencies, [\n    {\n      name: 'const2',\n      origin: 'declaration',\n      isSafe: false,\n    },{\n      origin: 'literal',\n      value: .1,\n      isSafe: false,\n    }\n  ]);\n});\n\n\ntest('safe from multiplication deep', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const const1 = 555 * 1;\n    const const2 = const1 + .555;\n    const const3 = .1 - const2;\n    const const4 = const3 - .1;\n    const const5 = .1 - const4; \n    const const6 = const5 - .1;\n    const const7 = .1 - const6;\n    const const8 = const7 - .1;\n    const const9 = .1 - const8;\n    const const10 = const9 + 10;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1] });\n\n  node.toString();\n  const { const1, const10 } = node.contexts[1];\n  assert.ok(const1.dependencies.every(dependency => dependency.isSafe === false));\n  assert.deepEqual(const10.dependencies, [\n    {\n      name: 'const9',\n      origin: 'declaration',\n      isSafe: false,\n    },{\n      origin: 'literal',\n      value: 10,\n      isSafe: true,\n    }\n  ]);\n});\n\n\ntest('safe from division', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const const1 = 555;\n    const const2 = const1 + .555;\n    const const3 = const2 / .1;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1] });\n\n  node.toString();\n  const { const1, const2, const3 } = node.contexts[1];\n  assert.deepEqual(const1.dependencies, [\n    {\n      origin: 'literal',\n      value: 555,\n      isSafe: true,\n    }\n  ]);\n  assert.deepEqual(const2.dependencies, [\n    {\n      name: 'const1',\n      origin: 'declaration',\n      isSafe: true,\n    },{\n      origin: 'literal',\n      value: .555,\n      isSafe: true,\n    }\n  ]);\n  assert.deepEqual(const3.dependencies, [\n    {\n      name: 'const2',\n      origin: 'declaration',\n      isSafe: false,\n    },{\n      origin: 'literal',\n      value: .1,\n      isSafe: false,\n    }\n  ]);\n});\n\n\ntest('safe from division deep', () => {\n  const node = new WebGLFunctionNode(`function kernel() {\n    const const1 = 555 / 1;\n    const const2 = const1 + .555;\n    const const3 = .1 - const2;\n    const const4 = .1 - const3; \n    const const5 = const4 - .1;\n    const const6 = const5 - .1;\n    const const7 = .1 - const6;\n    const const8 = const7 - .1;\n    const const9 = .1 - const8;\n    const const10 = const9 + 10;\n    let sum = 0;\n    for (let i = 0; i < const3; i++) {\n      sum += const3;\n    }\n  }`, { output: [1] });\n\n  node.toString();\n  const { const1, const10 } = node.contexts[1];\n  assert.ok(const1.dependencies.every(dependency => dependency.isSafe === false));\n  assert.deepEqual(const10.dependencies, [\n    {\n      name: 'const9',\n      origin: 'declaration',\n      isSafe: false,\n    },{\n      origin: 'literal',\n      value: 10,\n      isSafe: true,\n    }\n  ]);\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/firstAvailableTypeFromAst.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.getType()');\n\nfunction run(value, settings) {\n  const node = new WebGLFunctionNode(`function fn(value, value2, value3) {\n    ${ value };\n  }`, Object.assign({ output: [1] }, settings));\n\n  const ast = node.getJsAST();\n  node.traceFunctionAST(ast);\n  assert.equal(ast.type, 'FunctionExpression');\n  assert.equal(ast.body.type, 'BlockStatement');\n  assert.equal(ast.body.body[0].type, 'ExpressionStatement');\n\n  return node.getType(ast.body.body[0].expression);\n}\n\ntest('literal 0', () => {\n  assert.equal(run('0'), 'LiteralInteger');\n});\n\ntest('literal 0.1', () => {\n  assert.equal(run('0.1'), 'Number');\n});\n\ntest('unknown value from arguments', () => {\n  assert.throws(() => {\n    run('value');\n  });\n});\n\ntest('value Number from arguments', () => {\n  assert.equal(run('value', {\n    argumentNames: ['value'],\n    argumentTypes: ['Fake Type']\n  }), 'Fake Type');\n});\n\ntest('value[] Number from arguments', () => {\n  assert.equal(run('value[0]', {\n    argumentNames: ['value'],\n    argumentTypes: ['Array']\n  }), 'Number');\n});\n\ntest('value[][] Number from arguments', () => {\n  assert.equal(run('value[0][0]', {\n    argumentNames: ['value'],\n    argumentTypes: ['Array']\n  }), 'Number');\n});\n\ntest('value[][][] Number from arguments', () => {\n  assert.equal(run('value[0][0][0]', {\n    argumentNames: ['value'],\n    argumentTypes: ['Array']\n  }), 'Number');\n});\n\ntest('this.constants.value Integer', () => {\n  assert.equal(run('this.constants.value', {\n    constants: { value: 1 },\n    constantTypes: { value: 'Integer' }\n  }), 'Integer');\n});\n\ntest('this.constants.value[] Number', () => {\n  assert.equal(run('this.constants.value[0]', {\n    constants: { value: [1] },\n    constantTypes: { value: 'Array' }\n  }), 'Number');\n});\n\ntest('this.constants.value[][] Number', () => {\n  assert.equal(run('this.constants.value[0][0]', {\n    constants: { value: [[1]] },\n    constantTypes: { value: 'Array' }\n  }), 'Number');\n});\n\ntest('this.constants.value[][][] Number', () => {\n  assert.equal(run('this.constants.value[0][0][0]', {\n    constants: { value: [[[1]]] },\n    constantTypes: { value: 'Array' }\n  }), 'Number');\n});\n\ntest('this.thread.x', () => {\n  assert.equal(run('this.thread.x'), 'Integer');\n});\n\ntest('this.thread.y', () => {\n  assert.equal(run('this.thread.y'), 'Integer');\n});\n\ntest('this.thread.z', () => {\n  assert.equal(run('this.thread.z'), 'Integer');\n});\n\ntest('this.output.x', () => {\n  assert.equal(run('this.output.x'), 'LiteralInteger');\n});\n\ntest('this.output.y', () => {\n  assert.equal(run('this.output.y'), 'LiteralInteger');\n});\n\ntest('this.output.y', () => {\n  assert.equal(run('this.output.y'), 'LiteralInteger');\n});\n\ntest('bogus this.outputs.y', () => {\n  assert.throws(() => {\n    run('this.outputs.y');\n  });\n});\n\ntest('bogus this.threads.y', () => {\n  assert.throws(() => {\n    run('this.threads.y');\n  });\n});\n\ntest('unknown function call', () => {\n  assert.throws(() => {\n    assert.equal(run('value()'), null);\n  });\n});\n\ntest('function call', () => {\n  assert.equal(run('value()', {\n    lookupReturnType: (name, ast, node) => name === 'value' ? 'Fake Type' : null,\n  }), 'Fake Type');\n});\n\ntest('simple unknown expression', () => {\n  assert.throws(() => {\n    run('value + value2');\n  });\n});\n\ntest('simple expression', () => {\n  assert.equal(run('value + otherValue', {\n    argumentNames: ['value'],\n    argumentTypes: ['Number']\n  }), 'Number');\n});\n\ntest('simple right expression', () => {\n  assert.equal(run('value + value2', {\n    argumentNames: ['value', 'value2'],\n    argumentTypes: ['Number', 'Number']\n  }), 'Number');\n});\n\ntest('function call expression', () => {\n  assert.equal(run('otherFunction() + value', {\n    lookupReturnType: (name, ast, node) => name === 'otherFunction' ? 'Fake Type' : null,\n  }), 'Fake Type');\n});\n\ntest('Math.E', () => {\n  assert.equal(run('Math.E'), 'Number');\n});\n\ntest('Math.PI', () => {\n  assert.equal(run('Math.PI'), 'Number');\n});\n\ntest('Math.SQRT2', () => {\n  assert.equal(run('Math.SQRT2'), 'Number');\n});\n\ntest('Math.SQRT1_2', () => {\n  assert.equal(run('Math.SQRT1_2'), 'Number');\n});\n\ntest('Math.LN2', () => {\n  assert.equal(run('Math.LN2'), 'Number');\n});\n\ntest('Math.LN10', () => {\n  assert.equal(run('Math.LN10'), 'Number');\n});\n\ntest('Math.LOG2E', () => {\n  assert.equal(run('Math.LOG2E'), 'Number');\n});\n\ntest('Math.LOG10E', () => {\n  assert.equal(run('Math.LOG10E'), 'Number');\n});\n\ntest('Math.abs(value)', () => {\n  assert.equal(run('Math.abs(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.acos(value)', () => {\n  assert.equal(run('Math.acos(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.atan(value)', () => {\n  assert.equal(run('Math.atan(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.atan2(value, value2)', () => {\n  assert.equal(run('Math.atan2(value, value2)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.ceil(value)', () => {\n  assert.equal(run('Math.ceil(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.cos(value)', () => {\n  assert.equal(run('Math.cos(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.exp(value)', () => {\n  assert.equal(run('Math.exp(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.floor(value)', () => {\n  assert.equal(run('Math.floor(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.log(value)', () => {\n  assert.equal(run('Math.log(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.max(value, value2, value3)', () => {\n  assert.equal(run('Math.max(value, value2, value3)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.min(value, value2, value3)', () => {\n  assert.equal(run('Math.min(value, value2, value3)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.pow(value, value2)', () => {\n  assert.equal(run('Math.pow(value, value2)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.random()', () => {\n  assert.equal(run('Math.random()', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.round(value)', () => {\n  assert.equal(run('Math.random(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.sin(value)', () => {\n  assert.equal(run('Math.sin(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.sqrt(value)', () => {\n  assert.equal(run('Math.sqrt(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.tan(value)', () => {\n  assert.equal(run('Math.tan(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n\ntest('Math.tanh(value)', () => {\n  assert.equal(run('Math.tanh(value)', { argumentTypes: ['Number', 'Number', 'Number'] }), 'Number');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/getVariableSignature.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.getVariableSignature()');\n\nfunction run(value) {\n  const mockInstance = {\n    source: `function() { ${value}; }`,\n    traceFunctionAST: () => {}\n  };\n  const ast = WebGLFunctionNode.prototype.getJsAST.call(mockInstance);\n  const expression = ast.body.body[0].expression;\n  return WebGLFunctionNode.prototype.getVariableSignature.call({\n    isAstVariable: WebGLFunctionNode.prototype.isAstVariable\n  }, expression);\n}\n\ntest('value', () => {\n  assert.equal(run('value'), 'value');\n});\ntest('value[number]', () => {\n  assert.equal(run('value[0]'), 'value[]');\n});\ntest('value[variable]', () => {\n  assert.equal(run('value[a]'), 'value[]');\n});\ntest('value[variable] with conflicting names x', () => {\n  assert.equal(run('value[x]'), 'value[]');\n});\ntest('value[variable] with conflicting names y', () => {\n  assert.equal(run('value[y]'), 'value[]');\n});\ntest('value[variable] with conflicting names z', () => {\n  assert.equal(run('value[z]'), 'value[]');\n});\ntest('value[this.thread.x]', () => {\n  assert.equal(run('value[this.thread.x]'), 'value[]');\n});\ntest('value[this.thread.x][variable]', () => {\n  assert.equal(run('value[this.thread.x][a]'), 'value[][]');\n});\ntest('value[variable] with conflicting names constants', () => {\n  assert.equal(run('value[constants]'), 'value[]');\n});\ntest('value[variable] with conflicting names constants', () => {\n  assert.equal(run('value[output]'), 'value[]');\n});\ntest('value[variable] with conflicting names thread', () => {\n  assert.equal(run('value[thread]'), 'value[]');\n});\ntest('value[number][number]', () => {\n  assert.equal(run('value[0][0]'), 'value[][]');\n});\ntest('value[variable][variable]', () => {\n  assert.equal(run('value[a][b]'), 'value[][]');\n});\ntest('value[number][number][number]', () => {\n  assert.equal(run('value[0][0][0]'), 'value[][][]');\n});\ntest('value[variable][variable][variable]', () => {\n  assert.equal(run('value[a][b][c]'), 'value[][][]');\n});\n\ntest('this.thread.value', () => {\n  assert.equal(run('this.thread.x'), 'this.thread.value');\n  assert.equal(run('this.thread.y'), 'this.thread.value');\n  assert.equal(run('this.thread.z'), 'this.thread.value');\n});\n\ntest('this.output.value', () => {\n  assert.equal(run('this.output.x'), 'this.output.value');\n  assert.equal(run('this.output.y'), 'this.output.value');\n  assert.equal(run('this.output.z'), 'this.output.value');\n});\n\ntest('this.constants.value', () => {\n  assert.equal(run('this.constants.value'), 'this.constants.value');\n});\ntest('this.constants.value[]', () => {\n  assert.equal(run('this.constants.value[0]'), 'this.constants.value[]');\n});\ntest('this.constants.value[][]', () => {\n  assert.equal(run('this.constants.value[0][0]'), 'this.constants.value[][]');\n});\ntest('this.constants.value[][][]', () => {\n  assert.equal(run('this.constants.value[0][0][0]'), 'this.constants.value[][][]');\n});\ntest('this.constants.texture[this.thread.z][this.thread.y][this.thread.x]', () => {\n  assert.equal(run('this.constants.texture[this.thread.z][this.thread.y][this.thread.x]'), 'this.constants.value[][][]');\n});\ntest('this.whatever.value', () => {\n  assert.equal(run('this.whatever.value'), null);\n});\ntest('this.constants.value[][][][]', () => {\n  assert.equal(run('this.constants.value[0][0][0][0]'), 'this.constants.value[][][][]');\n});\ntest('this.constants.value.something', () => {\n  assert.equal(run('this.constants.value.something'), null);\n});\ntest('this.constants.value[].something', () => {\n  assert.equal(run('this.constants.value[0].something'), null);\n});\ntest('this.constants.value[][].something', () => {\n  assert.equal(run('this.constants.value[0][0].something'), null);\n});\ntest('this.constants.value[][][][]', () => {\n  assert.equal(run('this.constants.value[0][0][0].something'), null);\n});\ntest('complex nested this.constants.value[][][]', () => {\n  assert.equal(run(`\n  this.constants.value[\n    this.constants.value[i + 1]\n  ]\n  [\n    Math.random() * 1 + this.thread.x\n  ]\n  [\n    this.thread.x - 100\n  ]\n`), 'this.constants.value[][][]');\n});\ntest('complex nested with function call this.constants.value[][][]', () => {\n  assert.equal(run(`\n  this.constants.value[\n    something()\n  ]\n  [\n    Math.random() * 1 + this.thread.x\n  ]\n  [\n    this.thread.x - 100\n  ]\n`), 'this.constants.value[][][]')\n});\ntest('non-existent something', () => {\n  assert.equal(run('this.constants.value[0][0].something'), null);\n});\ntest('non-existent constants', () => {\n  assert.equal(run('this.constants.value[0][0].constants'), null);\n});\ntest('function call', () => {\n  assert.throws(() => {\n    run('Math.random()');\n  });\n});\ntest('binary expression add', () => {\n  assert.throws(() => {\n    run('value[0] + value[0]');\n  });\n});\ntest('binary expression divide', () => {\n  assert.throws(() => {\n    run('value[0] / value[0]');\n  });\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/function-node/getVariableType.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode, GLKernel } = require(process.cwd() + '/src');\n\ndescribe('WebGLFunctionNode.getVariableType()');\n\ntest('Native function > detects same native argument type float, and no cast', () => {\n  const nativeFunction = `float nativeFunction(float value) { return value + 1.0; }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(value);\n  }`, {\n    output: [1],\n    argumentTypes: ['Number'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_value) {'\n    + '\\nreturn nativeFunction(user_value);'\n    + '\\n}');\n});\n\ntest('Native function > detects same native argument type int, and no cast', () => {\n  const nativeFunction = `float nativeFunction(int value) { return float(value + 1); }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(value);\n  }`, {\n    output: [1],\n    argumentTypes: ['Integer'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_value) {'\n    + '\\nreturn nativeFunction(user_value);'\n    + '\\n}');\n});\n\ntest('Native function > detects different native argument type int from literal, and cast to it from float', () => {\n  const nativeFunction = `float nativeFunction(int value) { return float(value + 1); }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(1.5);\n  }`, {\n    output: [1],\n    argumentTypes: ['Number'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_value) {'\n    + '\\nreturn nativeFunction(int(1.5));'\n    + '\\n}');\n});\n\ntest('Native function > detects different native argument type float from literal, and cast to it from int', () => {\n  const nativeFunction = `float nativeFunction(int value) { return float(value + 1); }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(1);\n  }`, {\n    output: [1],\n    argumentTypes: ['Number'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_value) {'\n    + '\\nreturn nativeFunction(1);'\n    + '\\n}');\n});\n\ntest('Native function > detects different native argument type int, and cast to it from float', () => {\n  const nativeFunction = `float nativeFunction(int value) { return float(value + 1); }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(value);\n  }`, {\n    output: [1],\n    argumentTypes: ['Number'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(float user_value) {'\n    + '\\nreturn nativeFunction(int(user_value));'\n    + '\\n}');\n});\n\ntest('Native function > detects different native argument type float, and cast to it from int', () => {\n  const nativeFunction = `float nativeFunction(float value) { return value + 1.0; }`;\n  const returnType = GLKernel.nativeFunctionReturnType(nativeFunction);\n  const { argumentTypes } = GLKernel.nativeFunctionArguments(nativeFunction);\n  const node = new WebGLFunctionNode(`function kernel(value) {\n    return nativeFunction(value);\n  }`, {\n    output: [1],\n    argumentTypes: ['Integer'],\n    lookupReturnType: (name, ast, node) => {\n      if (name === 'nativeFunction') return returnType;\n      throw new Error('unknown function');\n    },\n    lookupFunctionArgumentTypes: (functionName) => {\n      if (functionName === 'nativeFunction') return argumentTypes;\n      throw new Error('unknown function');\n    },\n    needsArgumentType: () => false\n  });\n\n  assert.equal(node.toString(), 'float kernel(int user_value) {'\n    + '\\nreturn nativeFunction(float(user_value));'\n    + '\\n}');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel/index.js",
    "content": "const sinon = require('sinon');\nconst { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGLKernel } = require('../../../../../src');\n\ndescribe('internal: WebGLKernel');\n\n(typeof global !== 'undefined' ? test : skip)('.setupFeatureChecks() if context is available, but .getExtension() is falsey', () => {\n  const mockContext = {\n    getExtension: null // this is important\n  };\n  const mockElement = {\n    getContext: () => mockContext,\n  };\n  const mockDocument = {\n    createElement: () => {\n      return mockElement;\n    }\n  };\n  global.document = mockDocument;\n\n  WebGLKernel.setupFeatureChecks();\n  assert.ok(true);\n\n  delete global.document;\n});\n\ntest('.validateSettings() checks output texture size - too large', () => {\n  const mockContext = {\n    constructor: {\n      features: {\n        maxTextureSize: 1,\n      },\n    },\n    checkOutput: () => {},\n    output: [2],\n    validate: true,\n    checkTextureSize: WebGLKernel.prototype.checkTextureSize,\n  };\n  assert.throws(() => {\n    WebGLKernel.prototype.validateSettings.apply(mockContext, []);\n  }, new Error('Texture size [1,2] generated by kernel is larger than supported size [1,1]'));\n});\n\ntest('.validateSettings() checks output texture size - ok', () => {\n  const mockContext = {\n    constructor: {\n      features: {\n        maxTextureSize: 1,\n      },\n    },\n    checkOutput: () => {},\n    output: [1],\n    validate: true,\n    checkTextureSize: WebGLKernel.prototype.checkTextureSize,\n  };\n  WebGLKernel.prototype.validateSettings.apply(mockContext, []);\n  assert.ok(true);\n});\n\ntest('.build() checks if already built, and returns early if true', () => {\n  const mockContext = {\n    built: true,\n    initExtensions: sinon.spy(),\n  };\n  WebGLKernel.prototype.build.apply(mockContext);\n  assert.equal(mockContext.initExtensions.callCount, 0);\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel/setupArguments.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGLKernel, input } = require('../../../../../src');\n\ndescribe('internal WebGLKernel.setupArguments Array');\nconst gl = {\n  TEXTURE0: 0,\n  TEXTURE_2D: 'TEXTURE_2D',\n  RGBA: 'RGBA',\n  UNSIGNED_BYTE: 'UNSIGNED_BYTE',\n  FLOAT: 'FLOAT',\n  TEXTURE_WRAP_S: 'TEXTURE_WRAP_S',\n  CLAMP_TO_EDGE: 'CLAMP_TO_EDGE',\n  TEXTURE_WRAP_T: 'TEXTURE_WRAP_T',\n  TEXTURE_MIN_FILTER: 'TEXTURE_MIN_FILTER',\n  TEXTURE_MAG_FILTER: 'TEXTURE_MAG_FILTER',\n  NEAREST: 'NEAREST',\n  UNPACK_FLIP_Y_WEBGL: 'UNPACK_FLIP_Y_WEBGL',\n};\nfunction setupArgumentsTestSuite(testSuiteSettings) {\n  const {\n    gpuSettings,\n    argument,\n    expectedPixels,\n    expectedBitRatio,\n    expectedDim,\n    expectedSize,\n    expectedType,\n    expectedArgumentTextureCount,\n    expectedPixelStorei,\n  } = testSuiteSettings;\n  let texImage2DCalled = false;\n  let activeTextureCalled = false;\n  let bindTextureCalled = false;\n  let texParameteriCalls = 0;\n  let getUniformLocationCalls = 0;\n  let uniform3ivCalled = false;\n  let uniform2ivCalled = false;\n  let uniform1iCalled = false;\n  const mockContext = Object.assign({\n    activeTexture: (index) => {\n      assert.equal(index, 0);\n      activeTextureCalled = true;\n    },\n    bindTexture: (target, texture) => {\n      assert.equal(target, 'TEXTURE_2D');\n      assert.equal(texture, 'TEXTURE');\n      bindTextureCalled = true;\n    },\n    texParameteri: (target, pname, param) => {\n      switch (texParameteriCalls) {\n        case 0:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_S');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 1:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_T');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 2:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MIN_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        case 3:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MAG_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        default:\n          throw new Error('called too many times');\n      }\n    },\n    pixelStorei: (pname, param) => {\n      assert.equal(pname, 'UNPACK_FLIP_Y_WEBGL');\n      assert.equal(param, expectedPixelStorei);\n    },\n    createTexture: () => 'TEXTURE',\n    getUniformLocation: (program, name) => {\n      assert.equal(program, 'program');\n      if (getUniformLocationCalls > 3) {\n        throw new Error('called too many times');\n      }\n      getUniformLocationCalls++;\n      return {\n        user_vDim: 'user_vDimLocation',\n        user_vSize: 'user_vSizeLocation',\n        user_v: 'user_vLocation',\n      }[name];\n    },\n    uniform3iv: (location, value) => {\n      assert.equal(location, 'user_vDimLocation');\n      assert.deepEqual(value, expectedDim);\n      uniform3ivCalled = true;\n    },\n    uniform2iv: (location, value) => {\n      assert.equal(location, 'user_vSizeLocation');\n      assert.deepEqual(value, expectedSize);\n      uniform2ivCalled = true;\n    },\n    uniform1i: (location, value) => {\n      assert.equal(location, 'user_vLocation');\n      assert.equal(value, 0);\n      uniform1iCalled = true;\n    },\n    texImage2D: (target, level, internalFormat, width, height, border, format, type, pixels) => {\n      assert.equal(target, gl.TEXTURE_2D);\n      assert.equal(level, 0);\n      assert.equal(internalFormat, gl.RGBA);\n      assert.equal(width, expectedSize[0]);\n      assert.equal(height, expectedSize[1]);\n      assert.equal(border, 0);\n      assert.equal(format, gl.RGBA);\n      assert.equal(type, expectedType);\n      assert.equal(pixels.length, expectedPixels.length);\n      assert.deepEqual(pixels, expectedPixels);\n      texImage2DCalled = true;\n    }\n  }, gl);\n  const source = `function(v) { return v[this.thread.x]; }`;\n  const settings = {\n    context: mockContext,\n  };\n  const kernel = new WebGLKernel(source, Object.assign({}, settings, gpuSettings));\n  kernel.constructor = {\n    lookupKernelValueType: WebGLKernel.lookupKernelValueType,\n    features: {\n      maxTextureSize: 9999\n    }\n  };\n  const args = [argument];\n  kernel.program = 'program';\n  assert.equal(kernel.argumentTextureCount, 0);\n  kernel.setupArguments(args);\n  assert.equal(kernel.argumentBitRatios[0], expectedBitRatio);\n  assert.equal(kernel.kernelArguments.length, 1);\n  kernel.kernelArguments[0].updateValue(argument);\n  assert.equal(kernel.argumentTextureCount, expectedArgumentTextureCount);\n  assert.ok(texImage2DCalled);\n  assert.ok(activeTextureCalled);\n  assert.ok(bindTextureCalled);\n  assert.equal(texParameteriCalls, 4);\n  assert.equal(getUniformLocationCalls, 1);\n  assert.notOk(uniform3ivCalled);\n  assert.notOk(uniform2ivCalled);\n  assert.ok(uniform1iCalled);\n}\n\n// NOTE: Take special note of how the `argument` and `expectedPixels` are formatted\n\n// requires at least 5 entire pixels\ntest('Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ndescribe('internal WebGLKernel.setupArguments Input');\n// requires at least 5 entire pixels\ntest('Input(Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]), [5]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0,\n    ]), [2,3]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      1, 2, 3, 4,\n      5,0\n    ]),[2, 3]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]),[2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]), [33]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]), [33]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 3x3\n        1,2,3,4,       5,6,7,8,      9,10,11,12,\n        13,14,15,16,   17,18,19,20,  21,22,23,24,\n        25,26, 27,28,  29,30,31,32,  33,0,0,0\n      ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33\n    ]), [33]),\n    // upconverted to float32\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel/setupConstants.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGLKernel, input } = require('../../../../../src');\n\ndescribe('internal WebGLKernel.setupConstants Array');\nconst gl = {\n  TEXTURE0: 0,\n  TEXTURE_2D: 'TEXTURE_2D',\n  RGBA: 'RGBA',\n  UNSIGNED_BYTE: 'UNSIGNED_BYTE',\n  FLOAT: 'FLOAT',\n  TEXTURE_WRAP_S: 'TEXTURE_WRAP_S',\n  CLAMP_TO_EDGE: 'CLAMP_TO_EDGE',\n  TEXTURE_WRAP_T: 'TEXTURE_WRAP_T',\n  TEXTURE_MIN_FILTER: 'TEXTURE_MIN_FILTER',\n  TEXTURE_MAG_FILTER: 'TEXTURE_MAG_FILTER',\n  NEAREST: 'NEAREST',\n  UNPACK_FLIP_Y_WEBGL: 'UNPACK_FLIP_Y_WEBGL',\n};\nfunction setupConstantsTestSuite(testSuiteSettings) {\n  const {\n    gpuSettings,\n    constant,\n    expectedPixels,\n    expectedBitRatio,\n    expectedDim,\n    expectedSize,\n    expectedType,\n    expectedConstantTextureCount,\n    expectedPixelStorei,\n  } = testSuiteSettings;\n  let texImage2DCalled = false;\n  let activeTextureCalled = false;\n  let bindTextureCalled = false;\n  let texParameteriCalls = 0;\n  let getUniformLocationCalls = 0;\n  let uniform3ivCalled = false;\n  let uniform2ivCalled = false;\n  let uniform1iCalled = false;\n  const mockContext = Object.assign({\n    activeTexture: (index) => {\n      assert.equal(index, 0);\n      activeTextureCalled = true;\n    },\n    bindTexture: (target, texture) => {\n      assert.equal(target, 'TEXTURE_2D');\n      assert.equal(texture, 'TEXTURE');\n      bindTextureCalled = true;\n    },\n    texParameteri: (target, pname, param) => {\n      switch (texParameteriCalls) {\n        case 0:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_S');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 1:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_T');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 2:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MIN_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        case 3:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MAG_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        default:\n          throw new Error('called too many times');\n      }\n    },\n    pixelStorei: (pname, param) => {\n      assert.equal(pname, 'UNPACK_FLIP_Y_WEBGL');\n      assert.equal(param, expectedPixelStorei);\n    },\n    createTexture: () => 'TEXTURE',\n    getUniformLocation: (program, name) => {\n      assert.equal(program, 'program');\n      if (getUniformLocationCalls > 3) {\n        throw new Error('called too many times');\n      }\n      getUniformLocationCalls++;\n      return {\n        constants_vDim: 'constants_vDimLocation',\n        constants_vSize: 'constants_vSizeLocation',\n        constants_v: 'constants_vLocation',\n      }[name];\n    },\n    uniform3iv: (location, value) => {\n      assert.equal(location, 'constants_vDimLocation');\n      assert.deepEqual(value, expectedDim);\n      uniform3ivCalled = true;\n    },\n    uniform2iv: (location, value) => {\n      assert.equal(location, 'constants_vSizeLocation');\n      assert.deepEqual(value, expectedSize);\n      uniform2ivCalled = true;\n    },\n    uniform1i: (location, value) => {\n      assert.equal(location, 'constants_vLocation');\n      assert.equal(value, 0);\n      uniform1iCalled = true;\n    },\n    texImage2D: (target, level, internalFormat, width, height, border, format, type, pixels) => {\n      assert.equal(target, gl.TEXTURE_2D);\n      assert.equal(level, 0);\n      assert.equal(internalFormat, gl.RGBA);\n      assert.equal(width, expectedSize[0]);\n      assert.equal(height, expectedSize[1]);\n      assert.equal(border, 0);\n      assert.equal(format, gl.RGBA);\n      assert.equal(type, expectedType);\n      assert.equal(pixels.length, expectedPixels.length);\n      assert.deepEqual(pixels, expectedPixels);\n      texImage2DCalled = true;\n    }\n  }, gl);\n  const source = `function(v) { return this.constants.v[this.thread.x]; }`;\n  const settings = {\n    context: mockContext,\n  };\n  const kernel = new WebGLKernel(source, Object.assign({ constants: { v: constant } }, settings, gpuSettings));\n  kernel.constructor = {\n    lookupKernelValueType: WebGLKernel.lookupKernelValueType,\n    features: {\n      maxTextureSize: 9999\n    }\n  };\n  kernel.program = 'program';\n  assert.equal(kernel.constantTextureCount, 0);\n  kernel.setupConstants();\n  assert.equal(kernel.constantBitRatios.v, expectedBitRatio);\n  assert.equal(kernel.kernelConstants.length, 1);\n  kernel.kernelConstants[0].updateValue(constant);\n  assert.equal(kernel.constantTextureCount, expectedConstantTextureCount);\n  assert.ok(texImage2DCalled);\n  assert.ok(activeTextureCalled);\n  assert.ok(bindTextureCalled);\n  assert.equal(texParameteriCalls, 4);\n  assert.equal(getUniformLocationCalls, 1);\n  assert.notOk(uniform3ivCalled);\n  assert.notOk(uniform2ivCalled);\n  assert.ok(uniform1iCalled);\n}\n\n// NOTE: Take special note of how the `constant` and `expectedPixels` are formatted\n\n// requires at least 5 entire pixels\ntest('Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ndescribe('internal WebGL2Kernel.setupConstants Input');\n// requires at least 5 entire pixels\ntest('Input(Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]), [5]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0,\n    ]), [2,3]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      1, 2, 3, 4,\n      5,0\n    ]),[2, 3]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]),[2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]), [33]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]), [33]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 3x3\n        1,2,3,4,       5,6,7,8,      9,10,11,12,\n        13,14,15,16,   17,18,19,20,  21,22,23,24,\n        25,26, 27,28,  29,30,31,32,  33,0,0,0\n      ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33\n    ]), [33]),\n    // upconverted to float32\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicHTMLImage');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.HTMLImage({ width: 1, height: 1 }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImage',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue({ width: 1, height: 2 });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  const v = new webGLKernelValueMaps.unsigned.dynamic.HTMLImage({ width: 1, height: 1 }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImage',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      height: 1,\n      width: 2,\n    })\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.HTMLImage({ width: 2, height: 2 }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImage',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    height: 1,\n    width: 1,\n  });\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicHTMLImage');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicMemoryOptimizedNumberTexture');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.MemoryOptimizedNumberTexture({ size: [1, 1] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'MemoryOptimizedNumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue({ size: [1, 2] });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  const v = new webGLKernelValueMaps.unsigned.dynamic.MemoryOptimizedNumberTexture({ size: [1, 1] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'MemoryOptimizedNumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      size: [2,1]\n    })\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.MemoryOptimizedNumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'MemoryOptimizedNumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    size: [1,1],\n    context: mockContext,\n    texture: {}\n  });\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicMemoryOptimizedNumberTexture');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicNumberTexture');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.NumberTexture({ size: [1, 1] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'NumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue({ size: [1, 2] });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  const v = new webGLKernelValueMaps.unsigned.dynamic.NumberTexture({ size: [1, 1] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'NumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      size: [2,1]\n    })\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.NumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'NumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    size: [1,1],\n    context: mockContext,\n    texture: {}\n  });\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicNumberTexture');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-single-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleArray');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.single.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue(new Array([1,2,3,4,5,6,7,8]));\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue(new Array([2,1]));\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicSingleArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-single-array1d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleArray1DI');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array1D(2)\"]([[1,2]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array1D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue([[1,2],[3,4],[5,6],[7,8]]);\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array1D(2)\"]([[1,2]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array1D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue(new Array(new Array([2,1])));\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicSingleArray1DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-single-array2d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleArray2DI');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array2D(2)\"]([[[1,2]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array2D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue([[[1,2],[3,4],[5,6],[7,8]]]);\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array2D(2)\"]([[[1,2]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array2D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue([[[2,1]]]);\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicSingleArray2DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-single-array3d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleArray3DI');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array3D(2)\"]([[[[1,2]]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array3D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue([[[[1,2],[3,4],[5,6],[7,8]]]]);\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.dynamic[\"Array3D(2)\"]([[[[1,2]]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array3D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue([[[[2,1]]]]);\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicSingleArray3DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-single-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleInput');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.single.dynamic.Input({ size: [1, 1], value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue({ size: [4,4] });\n  }, new Error('Argument texture height and width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  const v = new webGLKernelValueMaps.single.dynamic.Input({ size: [1, 1], value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      size: [3,3]\n    })\n  }, new Error('Argument texture width of 3 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.dynamic.Input({ size: [2,2], context: mockContext, value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    size: [1,1],\n    context: mockContext,\n    value: [0]\n  });\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicSingleInput');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-unsigned-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicUnsignedArray');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue(new Array([1,2,3,4,5,6,7,8]));\n  }, new Error('Argument texture width of 4 larger than maximum size of 2 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue(new Array([2,1]));\n\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicUnsignedArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/dynamic-unsigned-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueUnsignedSingleInput');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n  };\n\n  const v = new webGLKernelValueMaps.unsigned.dynamic.Input({ size: [1, 1], value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      size: [8,8],\n      value: [0]\n    });\n  }, new Error('Argument texture height and width of 8 larger than maximum size of 4 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.dynamic.Input({ size: [2,2], context: mockContext, value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    size: [1,1],\n    context: mockContext,\n    value: [0]\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueDynamicUnsignedInput');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/html-image.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueHTMLImage');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.HTMLImage({ width: 1, height: 2 }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'HTMLImage',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.HTMLImage({ width: 2, height: 1 }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'HTMLImage',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.HTMLImage({ width: 2, height: 2 }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImage',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueHTMLImage');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/memory-optimized-number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueMemoryOptimizedNumberTexture');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.MemoryOptimizedNumberTexture({ size: [1, 2] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'MemoryOptimizedNumberTexture',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.MemoryOptimizedNumberTexture({ size: [2, 1] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'MemoryOptimizedNumberTexture',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.MemoryOptimizedNumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'MemoryOptimizedNumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueMemoryOptimizedNumberTexture');\n});\n\ntest('.updateValue() should set uploadValue when a pipeline kernel has no texture', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    pipeline: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.MemoryOptimizedNumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'MemoryOptimizedNumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  const newMockTexture = {}\n  v.updateValue({ size: [2,2], context: mockContext, texture: newMockTexture })\n  assert.equal(v.uploadValue, newMockTexture)\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/number-texture.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueNumberTexture');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.NumberTexture({ size: [1, 2] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'NumberTexture',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.NumberTexture({ size: [2, 1] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'NumberTexture',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.NumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'NumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueNumberTexture');\n});\n\ntest('.updateValue() should set uploadValue when a pipeline kernel has no texture', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    pipeline: true,\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.NumberTexture({ size: [2,2], context: mockContext }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'NumberTexture',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  const newMockTexture = {}\n  v.updateValue({ size: [2,2], context: mockContext, texture: newMockTexture })\n  assert.equal(v.uploadValue, newMockTexture)\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/single-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueSingleArray');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    const row = new Float32Array(5);\n    new webGLKernelValueMaps.single.static.Array(row, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.static.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueSingleArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/single-array1d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueSingleArray1DI');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    const row = new Float32Array(5);\n    new webGLKernelValueMaps.single.static[\"Array1D(2)\"]([row], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array1D(2)',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.static[\"Array1D(2)\"]([[1,2]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array1D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueSingleArray1DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/single-array2d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueSingleArray2DI');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const row = new Float32Array(5);\n  assert.throws(() => {\n    new webGLKernelValueMaps.single.static[\"Array2D(2)\"]([[row]], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array2D(2)',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.static[\"Array2D(2)\"]([[[1,2]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array2D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueSingleArray2DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/single-array3d-i.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueSingleArray3DI');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    const row = new Float32Array(5);\n    new webGLKernelValueMaps.single.static[\"Array3D(2)\"]([[row]], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array3D(2)',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.static[\"Array3D(2)\"]([[[1,2]]], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array3D(2)',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueSingleArray3DI');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/single-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueSingleInput');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.single.static.Input({ size: [5,1], value: [1,2] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.single.static.Input({ size: [1,2], value: [1,2] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueSingleInput');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/unsigned-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueUnsignedArray');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.Array([1,2], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height and width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueUnsignedArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl/kernel-value/unsigned-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGLKernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueUnsignedInput');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGLKernelValueMaps.unsigned.static.Input({ size: [2,1], value: [1,2] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height and width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGLKernelValueMaps.unsigned.static.Input({ size: [1,2], value: [1,2] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGLKernelValueUnsignedInput');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel/index.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGL2Kernel } = require('../../../../../src');\n\ndescribe('internal: WebGL2Kernel');\n\n(typeof global !== 'undefined' ? test : skip)('.setupFeatureChecks() if context is available, but .getExtension() is falsey', () => {\n  const mockContext = {\n    getExtension: null // this is important\n  };\n  const mockElement = {\n    getContext: () => mockContext,\n  };\n  const mockDocument = {\n    createElement: () => {\n      return mockElement;\n    }\n  };\n  global.document = mockDocument;\n\n  WebGL2Kernel.setupFeatureChecks();\n  assert.ok(true);\n\n  delete global.document;\n});\n\ntest('.validateSettings() checks output texture size - too large', () => {\n  const mockContext = {\n    constructor: {\n      features: {\n        maxTextureSize: 1,\n      },\n    },\n    checkOutput: () => {},\n    output: [2],\n    validate: true,\n    checkTextureSize: WebGL2Kernel.prototype.checkTextureSize,\n  };\n  assert.throws(() => {\n    WebGL2Kernel.prototype.validateSettings.apply(mockContext, []);\n  }, new Error('Texture size [1,2] generated by kernel is larger than supported size [1,1]'));\n});\n\ntest('.validateSettings() checks output texture size - ok', () => {\n  const mockContext = {\n    constructor: {\n      features: {\n        maxTextureSize: 1,\n      },\n    },\n    checkOutput: () => {},\n    output: [1],\n    validate: true,\n    checkTextureSize: WebGL2Kernel.prototype.checkTextureSize,\n  };\n  WebGL2Kernel.prototype.validateSettings.apply(mockContext, []);\n  assert.ok(true);\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel/setupArguments.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGL2Kernel, input } = require('../../../../../src');\n\ndescribe('internal WebGL2Kernel.setupArguments Array');\nconst gl = {\n  TEXTURE0: 0,\n  TEXTURE_2D: 'TEXTURE_2D',\n  RGBA: 'RGBA',\n  RGBA32F: 'RGBA32F',\n  UNSIGNED_BYTE: 'UNSIGNED_BYTE',\n  FLOAT: 'FLOAT',\n  TEXTURE_WRAP_S: 'TEXTURE_WRAP_S',\n  CLAMP_TO_EDGE: 'CLAMP_TO_EDGE',\n  TEXTURE_WRAP_T: 'TEXTURE_WRAP_T',\n  TEXTURE_MIN_FILTER: 'TEXTURE_MIN_FILTER',\n  TEXTURE_MAG_FILTER: 'TEXTURE_MAG_FILTER',\n  NEAREST: 'NEAREST',\n  UNPACK_FLIP_Y_WEBGL: 'UNPACK_FLIP_Y_WEBGL',\n};\nfunction setupArgumentsTestSuite(testSuiteSettings) {\n  const {\n    gpuSettings,\n    argument,\n    expectedPixels,\n    expectedBitRatio,\n    expectedDim,\n    expectedSize,\n    expectedType,\n    expectedArgumentTextureCount,\n    expectedPixelStorei,\n  } = testSuiteSettings;\n  let texImage2DCalled = false;\n  let activeTextureCalled = false;\n  let bindTextureCalled = false;\n  let texParameteriCalls = 0;\n  let getUniformLocationCalls = 0;\n  let uniform3ivCalled = false;\n  let uniform2ivCalled = false;\n  let uniform1iCalled = false;\n  const mockContext = Object.assign({\n    activeTexture: (index) => {\n      assert.equal(index, 0);\n      activeTextureCalled = true;\n    },\n    bindTexture: (target, texture) => {\n      assert.equal(target, 'TEXTURE_2D');\n      assert.equal(texture, 'TEXTURE');\n      bindTextureCalled = true;\n    },\n    texParameteri: (target, pname, param) => {\n      switch (texParameteriCalls) {\n        case 0:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_S');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 1:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_T');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 2:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MIN_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        case 3:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MAG_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        default:\n          throw new Error('called too many times');\n      }\n    },\n    pixelStorei: (pname, param) => {\n      assert.equal(pname, 'UNPACK_FLIP_Y_WEBGL');\n      assert.equal(param, expectedPixelStorei);\n    },\n    createTexture: () => 'TEXTURE',\n    getUniformLocation: (program, name) => {\n      assert.equal(program, 'program');\n      if (getUniformLocationCalls > 3) {\n        throw new Error('called too many times');\n      }\n      getUniformLocationCalls++;\n      return {\n        user_vDim: 'user_vDimLocation',\n        user_vSize: 'user_vSizeLocation',\n        user_v: 'user_vLocation',\n      }[name];\n    },\n    uniform3iv: (location, value) => {\n      assert.equal(location, 'user_vDimLocation');\n      assert.deepEqual(value, expectedDim);\n      uniform3ivCalled = true;\n    },\n    uniform2iv: (location, value) => {\n      assert.equal(location, 'user_vSizeLocation');\n      assert.deepEqual(value, expectedSize);\n      uniform2ivCalled = true;\n    },\n    uniform1i: (location, value) => {\n      assert.equal(location, 'user_vLocation');\n      assert.equal(value, 0);\n      uniform1iCalled = true;\n    },\n    texImage2D: (target, level, internalFormat, width, height, border, format, type, pixels) => {\n      assert.equal(target, gl.TEXTURE_2D);\n      assert.equal(level, 0);\n      assert.equal(internalFormat, gpuSettings.precision === 'single' ? gl.RGBA32F : gl.RGBA);\n      assert.equal(width, expectedSize[0]);\n      assert.equal(height, expectedSize[1]);\n      assert.equal(border, 0);\n      assert.equal(format, gl.RGBA);\n      assert.equal(type, expectedType);\n      assert.equal(pixels.length, expectedPixels.length);\n      assert.deepEqual(pixels, expectedPixels);\n      texImage2DCalled = true;\n    }\n  }, gl);\n  const source = `function(v) { return v[this.thread.x]; }`;\n  const settings = {\n    context: mockContext,\n  };\n  const kernel = new WebGL2Kernel(source, Object.assign({}, settings, gpuSettings));\n  kernel.constructor = {\n    lookupKernelValueType: WebGL2Kernel.lookupKernelValueType,\n    features: {\n      maxTextureSize: 9999\n    }\n  };\n  const args = [argument];\n  kernel.program = 'program';\n  assert.equal(kernel.argumentTextureCount, 0);\n  kernel.setupArguments(args);\n  assert.equal(kernel.argumentBitRatios[0], expectedBitRatio);\n  assert.equal(kernel.kernelArguments.length, 1);\n  kernel.kernelArguments[0].updateValue(argument);\n  assert.equal(kernel.argumentTextureCount, expectedArgumentTextureCount);\n  assert.ok(texImage2DCalled);\n  assert.ok(activeTextureCalled);\n  assert.ok(bindTextureCalled);\n  assert.equal(texParameteriCalls, 4);\n  assert.equal(getUniformLocationCalls, 1);\n  assert.notOk(uniform3ivCalled);\n  assert.notOk(uniform2ivCalled);\n  assert.ok(uniform1iCalled);\n}\n\n// NOTE: Take special note of how the `argument` and `expectedPixels` are formatted\n\n// requires at least 5 entire pixels\ntest('Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ndescribe('internal WebGL2Kernel.setupArguments Input');\n// requires at least 5 entire pixels\ntest('Input(Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]), [5]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0,\n    ]), [2,3]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with unsigned precision 5 length', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      1, 2, 3, 4,\n      5,0\n    ]),[2, 3]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]),[2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with single precision', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]), [33]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with unsigned precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]), [33]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 3x3\n        1,2,3,4,       5,6,7,8,      9,10,11,12,\n        13,14,15,16,   17,18,19,20,  21,22,23,24,\n        25,26, 27,28,  29,30,31,32,  33,0,0,0\n      ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with single precision length 33', () => {\n  setupArgumentsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    argument: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33\n    ]), [33]),\n    // upconverted to float32\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedArgumentTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel/setupConstants.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGL2Kernel, input } = require('../../../../../src');\n\ndescribe('internal WebGL2Kernel.setupConstants Array');\nconst gl = {\n  TEXTURE0: 0,\n  TEXTURE_2D: 'TEXTURE_2D',\n  RGBA: 'RGBA',\n  RGBA32F: 'RGBA32F',\n  UNSIGNED_BYTE: 'UNSIGNED_BYTE',\n  FLOAT: 'FLOAT',\n  TEXTURE_WRAP_S: 'TEXTURE_WRAP_S',\n  CLAMP_TO_EDGE: 'CLAMP_TO_EDGE',\n  TEXTURE_WRAP_T: 'TEXTURE_WRAP_T',\n  TEXTURE_MIN_FILTER: 'TEXTURE_MIN_FILTER',\n  TEXTURE_MAG_FILTER: 'TEXTURE_MAG_FILTER',\n  NEAREST: 'NEAREST',\n  UNPACK_FLIP_Y_WEBGL: 'UNPACK_FLIP_Y_WEBGL',\n};\nfunction setupConstantsTestSuite(testSuiteSettings) {\n  const {\n    gpuSettings,\n    constant,\n    expectedPixels,\n    expectedBitRatio,\n    expectedDim,\n    expectedSize,\n    expectedType,\n    expectedConstantTextureCount,\n    expectedPixelStorei,\n  } = testSuiteSettings;\n  let texImage2DCalled = false;\n  let activeTextureCalled = false;\n  let bindTextureCalled = false;\n  let texParameteriCalls = 0;\n  let getUniformLocationCalls = 0;\n  let uniform3ivCalled = false;\n  let uniform2ivCalled = false;\n  let uniform1iCalled = false;\n  const mockContext = Object.assign({\n    activeTexture: (index) => {\n      assert.equal(index, 0);\n      activeTextureCalled = true;\n    },\n    bindTexture: (target, texture) => {\n      assert.equal(target, 'TEXTURE_2D');\n      assert.equal(texture, 'TEXTURE');\n      bindTextureCalled = true;\n    },\n    texParameteri: (target, pname, param) => {\n      switch (texParameteriCalls) {\n        case 0:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_S');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 1:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_WRAP_T');\n          assert.equal(param, 'CLAMP_TO_EDGE');\n          texParameteriCalls++;\n          break;\n        case 2:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MIN_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        case 3:\n          assert.equal(target, 'TEXTURE_2D');\n          assert.equal(pname, 'TEXTURE_MAG_FILTER');\n          assert.equal(param, 'NEAREST');\n          texParameteriCalls++;\n          break;\n        default:\n          throw new Error('called too many times');\n      }\n    },\n    pixelStorei: (pname, param) => {\n      assert.equal(pname, 'UNPACK_FLIP_Y_WEBGL');\n      assert.equal(param, expectedPixelStorei);\n    },\n    createTexture: () => 'TEXTURE',\n    getUniformLocation: (program, name) => {\n      assert.equal(program, 'program');\n      if (getUniformLocationCalls > 3) {\n        throw new Error('called too many times');\n      }\n      getUniformLocationCalls++;\n      return {\n        constants_vDim: 'constants_vDimLocation',\n        constants_vSize: 'constants_vSizeLocation',\n        constants_v: 'constants_vLocation',\n      }[name];\n    },\n    uniform3iv: (location, value) => {\n      assert.equal(location, 'constants_vDimLocation');\n      assert.deepEqual(value, expectedDim);\n      uniform3ivCalled = true;\n    },\n    uniform2iv: (location, value) => {\n      assert.equal(location, 'constants_vSizeLocation');\n      assert.deepEqual(value, expectedSize);\n      uniform2ivCalled = true;\n    },\n    uniform1i: (location, value) => {\n      assert.equal(location, 'constants_vLocation');\n      assert.equal(value, 0);\n      uniform1iCalled = true;\n    },\n    texImage2D: (target, level, internalFormat, width, height, border, format, type, pixels) => {\n      assert.equal(target, gl.TEXTURE_2D);\n      assert.equal(level, 0);\n      assert.equal(internalFormat, gpuSettings.precision === 'single' ? gl.RGBA32F : gl.RGBA);\n      assert.equal(width, expectedSize[0]);\n      assert.equal(height, expectedSize[1]);\n      assert.equal(border, 0);\n      assert.equal(format, gl.RGBA);\n      assert.equal(type, expectedType);\n      assert.equal(pixels.length, expectedPixels.length);\n      assert.deepEqual(pixels, expectedPixels);\n      texImage2DCalled = true;\n    }\n  }, gl);\n  const source = `function(v) { return this.constants.v[this.thread.x]; }`;\n  const settings = {\n    context: mockContext,\n  };\n  const kernel = new WebGL2Kernel(source, Object.assign({ constants: { v: constant } }, settings, gpuSettings));\n  kernel.constructor = {\n    lookupKernelValueType: WebGL2Kernel.lookupKernelValueType,\n    features: {\n      maxTextureSize: 9999\n    }\n  };\n  kernel.program = 'program';\n  assert.equal(kernel.constantTextureCount, 0);\n  kernel.setupConstants();\n  assert.equal(kernel.constantBitRatios.v, expectedBitRatio);\n  assert.equal(kernel.kernelConstants.length, 1);\n  kernel.kernelConstants[0].updateValue(constant);\n  assert.equal(kernel.constantTextureCount, expectedConstantTextureCount);\n  assert.ok(texImage2DCalled);\n  assert.ok(activeTextureCalled);\n  assert.ok(bindTextureCalled);\n  assert.equal(texParameteriCalls, 4);\n  assert.equal(getUniformLocationCalls, 1);\n  assert.notOk(uniform3ivCalled);\n  assert.notOk(uniform2ivCalled);\n  assert.ok(uniform1iCalled);\n}\n\n// NOTE: Take special note of how the `constant` and `expectedPixels` are formatted\n\n// requires at least 5 entire pixels\ntest('Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      1, 2, 3, 4,\n      5\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Float32Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint16Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Uint8Array with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: [\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ],\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Float32Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint16Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Uint8Array with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ndescribe('internal WebGL2Kernel.setupConstants Input');\n// requires at least 5 entire pixels\ntest('Input(Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]).buffer),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5\n    ]), [5]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([5,1,1]),\n    expectedSize: new Int32Array([4,2]), // 4 * 2 * 1 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [4]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1, 2, 3, 4,\n      5, 0,\n    ]), [2,3]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        1,2,3,4,\n        5,0,0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([2,2]), // 2 * 2 * 2 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with unsigned precision 5 length', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      1, 2, 3, 4,\n      5,0\n    ]),[2, 3]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]), // 1 * 2 * 4 = 8\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ], [2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Float32Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]),[2,3]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedTextureWidth: 1,\n    expectedTextureHeight: 2,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint16Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\ntest('Input(Uint8Array) with single precision', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [4]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0\n    ]), [2,3]),\n    // upconverted from 1\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      1,2,3,4,\n      5,0,0,0\n    ]),\n    expectedDim: new Int32Array([2,3,1]),\n    expectedSize: new Int32Array([1,2]),\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n      // NOTE: 6x6\n      1,    2,   3,  4,   5,   6,\n      7,    8,   9,  10,  11,  12,\n      13,  14,  15,  16,  17,  18,\n      19,  20,  21,  22,  23,  24,\n      25,  26,  27,  28,  29,  30,\n      31,  32,  33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Uint8Array(\n      new Float32Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 1 per RGBA, so only 1 of the 4 channels is used\n        // NOTE: 6x6\n        1,    2,   3,  4,   5,   6,\n        7,    8,   9,  10,  11,  12,\n        13,  14,  15,  16,  17,  18,\n        19,  20,  21,  22,  23,  24,\n        25,  26,  27,  28,  29,  30,\n        31,  32,  33,  0,   0,   0,\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([6,6]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 4x5\n      1,2,    3,4,    5,6,    7,8,\n      9,10,   11,12,  13,14,  15,16,\n      17,18,  19,20,  21,22,  23,24,\n      25,26,  27,28,  29,30,  31,32,\n      33,\n    ]), [33]),\n    expectedBitRatio: 2,\n    expectedPixels: new Uint8Array(\n      new Uint16Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 2 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 4x5\n        1,2,    3,4,    5,6,    7,8,\n        9,10,   11,12,  13,14,  15,16,\n        17,18,  19,20,  21,22,  23,24,\n        25,26,  27,28,  29,30,  31,32,\n        33,0,   0,0,    0,0,    0,0\n      ]).buffer\n    ),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([4,5]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with unsigned precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'unsigned',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,      9,10,11,12,\n      13,14,15,16,   17,18,19,20,  21,22,23,24,\n      25,26, 27,28,  29,30,31,32,  33\n    ]), [33]),\n    expectedBitRatio: 1,\n    expectedPixels: new Uint8Array(\n      new Uint8Array([\n        // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n        // NOTE: Packing is 4 per RGBA, so only 2 of the 4 channels is used\n        // NOTE: 3x3\n        1,2,3,4,       5,6,7,8,      9,10,11,12,\n        13,14,15,16,   17,18,19,20,  21,22,23,24,\n        25,26, 27,28,  29,30,31,32,  33,0,0,0\n      ]).buffer),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.UNSIGNED_BYTE,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ], [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Float32Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 34\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint16Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Uint16Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33\n    ]), [33]),\n    // upconverted from 2\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA, so 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,       5,6,7,8,         9,10,11,12,\n      13,14,15,16,   17,18,19,20,     21,22,23,24,\n      25,26,27,28,   29,30,31,32,     33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n\ntest('Input(Uint8Array) with single precision length 33', () => {\n  setupConstantsTestSuite({\n    gpuSettings: {\n      precision: 'single',\n      output: [5]\n    },\n    constant: input(new Uint8Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33\n    ]), [33]),\n    // upconverted to float32\n    expectedBitRatio: 4,\n    expectedPixels: new Float32Array([\n      // NOTE: formatted like rectangle on purpose, so you can see how the texture should look\n      // NOTE: Packing is 4 per RGBA (8 bit, but upconverted to float32), so only 4 of the 4 channels is used\n      // NOTE: 3x3\n      1,2,3,4,        5,6,7,8,        9,10,11,12,\n      13,14,15,16,    17,18,19,20,    21,22,23,24,\n      25,26,27,28,    29,30,31,32,    33,0,0,0\n    ]),\n    expectedDim: new Int32Array([33,1,1]),\n    expectedSize: new Int32Array([3,3]), // 3 * 3 = 9 * 4 = 36\n    expectedType: gl.FLOAT,\n    expectedConstantTextureCount: 1,\n    expectedPixelStorei: false,\n  });\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel-value/dynamic-html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGL2KernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGL2KernelValueDynamicHTMLImage');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGL2KernelValueMaps.unsigned.dynamic.HTMLImageArray([{ width: 1, height: 1 }], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImage',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue([{ width: 1, height: 2 }]);\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n\n  const v = new webGL2KernelValueMaps.unsigned.dynamic.HTMLImageArray([{ width: 1, height: 1 }], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImageArray',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue([{\n      height: 1,\n      width: 2,\n    }])\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage3D: () => {},\n    texSubImage3D: () => {},\n  };\n  const v = new webGL2KernelValueMaps.unsigned.dynamic.HTMLImageArray([{ width: 2, height: 2 }], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImageArray',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue([{\n    height: 1,\n    width: 1,\n  }]);\n\n  assert.equal(v.constructor.name, 'WebGL2KernelValueDynamicHTMLImageArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel-value/dynamic-single-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGL2KernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGL2KernelValueDynamicSingleArray');\n\ntest('.updateValue() checks too large', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  const v = new webGL2KernelValueMaps.single.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue(new Array([1,2,3,4,5,6,7,8]));\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.updateValue() checks ok', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGL2KernelValueMaps.single.dynamic.Array([1,2], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue(new Array([2,1]));\n\n  assert.equal(v.constructor.name, 'WebGL2KernelValueDynamicSingleArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel-value/dynamic-single-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGL2KernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGLKernelValueDynamicSingleInput');\n\ntest('.updateValue() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n  };\n  const v = new webGL2KernelValueMaps.single.dynamic.Input({ size: [5, 5], value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n\n  assert.throws(() => {\n    v.updateValue({ size: [16,16] });\n  }, new Error('Argument texture height and width of 8 larger than maximum size of 4 for your GPU'));\n});\n\ntest('.updateValue() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n  };\n\n  const v = new webGL2KernelValueMaps.single.dynamic.Input({ size: [1, 1], value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.throws(() => {\n    v.updateValue({\n      size: [12,12]\n    })\n  }, new Error('Argument texture height and width of 6 larger than maximum size of 4 for your GPU'));\n});\n\ntest('.updateValue() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGL2KernelValueMaps.single.dynamic.Input({ size: [2,2], context: mockContext, value: [0] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Input',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  v.updateValue({\n    size: [1,1],\n    context: mockContext,\n    value: [0]\n  });\n\n  assert.equal(v.constructor.name, 'WebGL2KernelValueDynamicSingleInput');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel-value/html-image-array.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGL2KernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGL2KernelValueHTMLImageArray');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGL2KernelValueMaps.unsigned.static.HTMLImageArray([{ width: 1, height: 2 }], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'HTMLImageArray',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks too large width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGL2KernelValueMaps.unsigned.static.HTMLImageArray([{ width: 2, height: 1 }], {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'HTMLImageArray',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture width of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 2 },\n    },\n    validate: true,\n  };\n  const v = new webGL2KernelValueMaps.unsigned.static.HTMLImageArray([{ width: 2, height: 2 }], {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'HTMLImageArray',\n    origin: 'user',\n    tactic: 'speed',\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGL2KernelValueHTMLImageArray');\n});\n"
  },
  {
    "path": "test/internal/backend/web-gl2/kernel-value/single-input.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { webGL2KernelValueMaps } = require('../../../../../src');\n\ndescribe('internal: WebGL2KernelValueSingleInput');\n\ntest('.constructor() checks too large height', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 1 },\n    },\n    validate: true,\n  };\n  assert.throws(() => {\n    new webGL2KernelValueMaps.single.static.Input({ size: [8,1], value: [1,2] }, {\n      kernel: mockKernel,\n      name: 'test',\n      type: 'Array',\n      origin: 'user',\n      tactic: 'speed',\n      onRequestContextHandle: () => 1,\n      onRequestTexture: () => null,\n      onRequestIndex: () => 1\n    });\n  }, new Error('Argument texture height of 2 larger than maximum size of 1 for your GPU'));\n});\n\ntest('.constructor() checks ok height & width', () => {\n  const mockKernel = {\n    constructor: {\n      features: { maxTextureSize: 4 },\n    },\n    validate: true,\n    setUniform3iv: () => {},\n    setUniform2iv: () => {},\n    setUniform1i: () => {},\n  };\n  const mockContext = {\n    activeTexture: () => {},\n    bindTexture: () => {},\n    texParameteri: () => {},\n    pixelStorei: () => {},\n    texImage2D: () => {},\n  };\n  const v = new webGL2KernelValueMaps.single.static.Input({ size: [1,2], value: [1,2] }, {\n    kernel: mockKernel,\n    name: 'test',\n    type: 'Array',\n    origin: 'user',\n    tactic: 'speed',\n    context: mockContext,\n    onRequestContextHandle: () => 1,\n    onRequestTexture: () => null,\n    onRequestIndex: () => 1\n  });\n  assert.equal(v.constructor.name, 'WebGL2KernelValueSingleInput');\n});\n"
  },
  {
    "path": "test/internal/boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: boolean');\n\nfunction booleanLiteral(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const v = true === true;\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel();\n  assert.ok(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean literal auto', () => {\n  booleanLiteral();\n});\n\ntest('boolean literal gpu', () => {\n  booleanLiteral('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean literal webgl', () => {\n  booleanLiteral('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean literal webgl2', () => {\n  booleanLiteral('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean literal headlessgl', () => {\n  booleanLiteral('headlessgl');\n});\n\ntest('boolean literal cpu', () => {\n  booleanLiteral('cpu');\n});\n\n\nfunction booleanArgumentTrue(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel(true);\n  assert.ok(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean argument true auto', () => {\n  booleanArgumentTrue();\n});\n\ntest('boolean argument true gpu', () => {\n  booleanArgumentTrue('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean argument true webgl', () => {\n  booleanArgumentTrue('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean argument true webgl2', () => {\n  booleanArgumentTrue('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean argument true headlessgl', () => {\n  booleanArgumentTrue('headlessgl');\n});\n\ntest('boolean argument true cpu', () => {\n  booleanArgumentTrue('cpu');\n});\n\n\nfunction booleanArgumentFalse(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel(false);\n  assert.notOk(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean argument false auto', () => {\n  booleanArgumentFalse();\n});\n\ntest('boolean argument false gpu', () => {\n  booleanArgumentFalse('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean argument false webgl', () => {\n  booleanArgumentFalse('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean argument false webgl2', () => {\n  booleanArgumentFalse('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean argument false headlessgl', () => {\n  booleanArgumentFalse('headlessgl');\n});\n\ntest('boolean argument false cpu', () => {\n  booleanArgumentFalse('cpu');\n});\n\n\nfunction booleanVariableTrue(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const v = true;\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel();\n  assert.ok(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean variable true auto', () => {\n  booleanVariableTrue();\n});\n\ntest('boolean variable true gpu', () => {\n  booleanVariableTrue('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean variable true webgl', () => {\n  booleanVariableTrue('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean variable true webgl2', () => {\n  booleanVariableTrue('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean variable true headlessgl', () => {\n  booleanVariableTrue('headlessgl');\n});\n\ntest('boolean variable true cpu', () => {\n  booleanVariableTrue('cpu');\n});\n\nfunction booleanVariableFalse(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const v = false;\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel();\n  assert.notOk(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean variable false auto', () => {\n  booleanVariableFalse();\n});\n\ntest('boolean variable false gpu', () => {\n  booleanVariableFalse('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean variable false webgl', () => {\n  booleanVariableFalse('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean variable false webgl2', () => {\n  booleanVariableFalse('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean variable false headlessgl', () => {\n  booleanVariableFalse('headlessgl');\n});\n\ntest('boolean variable false cpu', () => {\n  booleanVariableFalse('cpu');\n});\n\nfunction booleanExpressionTrue(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const v = 1 > 0;\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel();\n  assert.ok(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean expression true auto', () => {\n  booleanExpressionTrue();\n});\n\ntest('boolean expression true gpu', () => {\n  booleanExpressionTrue('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean expression true webgl', () => {\n  booleanExpressionTrue('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean expression true webgl2', () => {\n  booleanExpressionTrue('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean expression true headlessgl', () => {\n  booleanExpressionTrue('headlessgl');\n});\n\ntest('boolean expression true cpu', () => {\n  booleanExpressionTrue('cpu');\n});\n\n\nfunction booleanExpressionFalse(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    const v = 1 < 0;\n    if (v) {\n      return 1;\n    }\n    return 0;\n  }, {\n    output: [1],\n  });\n  const result = kernel();\n  assert.notOk(result[0]);\n  gpu.destroy();\n}\n\ntest('boolean expression false auto', () => {\n  booleanExpressionFalse();\n});\n\ntest('boolean expression false gpu', () => {\n  booleanExpressionFalse('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('boolean expression false webgl', () => {\n  booleanExpressionFalse('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('boolean expression false webgl2', () => {\n  booleanExpressionFalse('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('boolean expression false headlessgl', () => {\n  booleanExpressionFalse('headlessgl');\n});\n\ntest('boolean expression false cpu', () => {\n  booleanExpressionFalse('cpu');\n});\n"
  },
  {
    "path": "test/internal/casting.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: casting');\n\nfunction castingOffsetByThreadXAndOutputX(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (value) {\n    // value will be a number\n    // this.thread.x is an integer\n    // this.output.x is treated as a literal, so can be either integer or float\n    // return value will be float\n    return this.thread.x + (this.output.x * value);\n  }, {\n    output: [1],\n    strictIntegers: true,\n  });\n  const result = kernel(1);\n  assert.equal(result[0], 1);\n  assert.deepEqual(kernel.argumentTypes, ['Integer']);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('casting offset by this.thread.x and this.output.x webgl', () => {\n  castingOffsetByThreadXAndOutputX('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('casting offset by this.thread.x and this.output.x webgl2', () => {\n  castingOffsetByThreadXAndOutputX('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('casting offset by this.thread.x and this.output.x headlessgl', () => {\n  castingOffsetByThreadXAndOutputX('headlessgl');\n});\n\nfunction handleCastingIntsWithNativeFunctions(mode) {\n  const gpu = new GPU({ mode });\n  gpu.addNativeFunction('add', `\n    int add(int value1, int value2) {\n      return value1 + value2;\n    }\n  `);\n  const kernel = gpu.createKernel(function(value1, value2) {\n    return add(value1, value2);\n  }, { output: [1] });\n  const result = kernel(0.5, 2.5);\n  assert.deepEqual(Array.from(result), [2]);\n  assert.deepEqual(kernel.argumentTypes, ['Float', 'Float']);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('handle casting ints with native functions webgl', () => {\n  handleCastingIntsWithNativeFunctions('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('handle casting ints with native functions webgl2', () => {\n  handleCastingIntsWithNativeFunctions('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('handle casting ints with native functions headlessgl', () => {\n  handleCastingIntsWithNativeFunctions('headlessgl');\n});\n\n\nfunction handleCastingFloatsWithNativeFunctions(mode) {\n  const gpu = new GPU({ mode });\n  gpu.addNativeFunction('add', `\n    float add(float value1, float value2) {\n      return value1 + value2;\n    }\n  `);\n  const kernel = gpu.createKernel(function(value1, value2) {\n    return add(value1, value2);\n  }, {\n    argumentTypes: ['Integer', 'Integer'],\n    output: [1],\n    strictIntegers: true,\n  });\n  const result = kernel(1, 2);\n  assert.deepEqual(Array.from(result), [3]);\n  assert.deepEqual(kernel.argumentTypes, ['Integer', 'Integer']);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('handle casting floats with native functions webgl', () => {\n  handleCastingFloatsWithNativeFunctions('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('handle casting floats with native functions webgl2', () => {\n  handleCastingFloatsWithNativeFunctions('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('handle casting floats with native functions headlessgl', () => {\n  handleCastingFloatsWithNativeFunctions('headlessgl');\n});\n\n\nfunction handleCastingMixedWithNativeFunctions(mode) {\n  const gpu = new GPU({ mode });\n  gpu.addNativeFunction('add', `\n    float add(float value1, int value2) {\n      return value1 + float(value2);\n    }\n  `);\n  const kernel = gpu.createKernel(function(value1, value2) {\n    return add(value1, value2);\n  }, {\n    output: [1],\n    strictIntegers: true,\n  });\n  const result = kernel(1, 2.5);\n  assert.deepEqual(Array.from(result), [3]);\n  assert.deepEqual(kernel.argumentTypes, ['Integer', 'Float']);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('handle casting mixed with native functions webgl', () => {\n  handleCastingMixedWithNativeFunctions('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('handle casting mixed with native functions webgl2', () => {\n  handleCastingMixedWithNativeFunctions('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('handle casting mixed with native functions headlessgl', () => {\n  handleCastingMixedWithNativeFunctions('headlessgl');\n});\n\nfunction handleCastingFloat(mode) {\n  const gpu = new GPU({ mode });\n  function add(value1, value2) {\n    return value1 + value2;\n  }\n  gpu.addFunction(add, {\n    argumentTypes: ['Float', 'Float'],\n    returnType: 'Float'\n  });\n  const kernel = gpu.createKernel(function(value1, value2) {\n    return add(value1, value2);\n  }, {\n    output: [1],\n    argumentTypes: ['Integer', 'Integer'],\n  });\n  const result = kernel(1, 2);\n  assert.equal(result[0], 3);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('handle casting float webgl', () => {\n  handleCastingFloat('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('handle casting float webgl2', () => {\n  handleCastingFloat('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('handle casting float headlessgl', () => {\n  handleCastingFloat('headlessgl');\n});\n\n\nfunction handleCastingBeforeReturn(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(v) {\n    return v + v;\n  }\n  gpu.addFunction(addOne, {\n    argumentTypes: { v: 'Float' },\n    returnType: 'Integer',\n  });\n  const kernel = gpu.createKernel(function(v) {\n    return addOne(v);\n  }, { output: [1] });\n  assert.equal(kernel(1)[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('handle casting before return webgl', () => {\n  handleCastingBeforeReturn('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('handle casting before return webgl2', () => {\n  handleCastingBeforeReturn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('handle casting before return headlessgl', () => {\n  handleCastingBeforeReturn('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/constants-texture-switching.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: constants texture switching');\n\nfunction testArray1D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(2) (GPU only) auto', () => {\n  testArray1D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(2) (GPU only) gpu', () => {\n  testArray1D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(2) (GPU only) webgl', () => {\n  testArray1D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(2) (GPU only) webgl2', () => {\n  testArray1D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(2) (GPU only) headlessgl', () => {\n  testArray1D2('headlessgl');\n});\n\nfunction testArray1D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1, this.thread.x + 2]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) auto', () => {\n  testArray1D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) gpu', () => {\n  testArray1D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(3) (GPU only) webgl', () => {\n  testArray1D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(3) (GPU only) webgl2', () => {\n  testArray1D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(3) (GPU only) headlessgl', () => {\n  testArray1D3('headlessgl');\n});\n\nfunction testArray1D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x + 1, this.thread.x + 2, this.thread.x + 3]; })\n      .setOutput([10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) auto', () => {\n  testArray1D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) gpu', () => {\n  testArray1D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array1D(4) (GPU only) webgl', () => {\n  testArray1D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array1D(4) (GPU only) webgl2', () => {\n  testArray1D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array1D(4) (GPU only) headlessgl', () => {\n  testArray1D4('headlessgl');\n});\n\nfunction testArray2D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y]; })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(2) (GPU only) auto', () => {\n  testArray2D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(2) (GPU only) gpu', () => {\n  testArray2D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(2) (GPU only) webgl', () => {\n  testArray2D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(2) (GPU only) webgl2', () => {\n  testArray2D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(2) (GPU only) headlessgl', () => {\n  testArray2D2('headlessgl');\n});\n\nfunction testArray2D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y, this.thread.x * this.thread.y]; })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(3) (GPU only) auto', () => {\n  testArray2D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(3) (GPU only) gpu', () => {\n  testArray2D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(3) (GPU only) webgl', () => {\n  testArray2D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(3) (GPU only) webgl2', () => {\n  testArray2D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(3) (GPU only) headlessgl', () => {\n  testArray2D3('headlessgl');\n});\n\nfunction testArray2D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() {\n      return [\n        this.thread.x,\n        this.thread.y,\n        this.thread.x * this.thread.y,\n        this.thread.x / this.thread.y\n      ];\n    })\n      .setOutput([10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array2D(4) (GPU only) auto', () => {\n  testArray2D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array1D(4) (GPU only) gpu', () => {\n  testArray2D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array2D(4) (GPU only) webgl', () => {\n  testArray2D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array2D(4) (GPU only) webgl2', () => {\n  testArray2D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array2D(4) (GPU only) headlessgl', () => {\n  testArray2D4('headlessgl');\n});\n\nfunction testArray3D2(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.x * this.thread.y * this.thread.z]; })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(2) (GPU only) auto', () => {\n  testArray3D2();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(2) (GPU only) gpu', () => {\n  testArray3D2('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(2) (GPU only) webgl', () => {\n  testArray3D2('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(2) (GPU only) webgl2', () => {\n  testArray3D2('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(2) (GPU only) headlessgl', () => {\n  testArray3D2('headlessgl');\n});\n\nfunction testArray3D3(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() { return [this.thread.x, this.thread.y, this.thread.z]; })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(3) (GPU only) auto', () => {\n  testArray3D3();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(3) (GPU only) gpu', () => {\n  testArray3D3('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(3) (GPU only) webgl', () => {\n  testArray3D3('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(3) (GPU only) webgl2', () => {\n  testArray3D3('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(3) (GPU only) headlessgl', () => {\n  testArray3D3('headlessgl');\n});\n\nfunction testArray3D4(mode) {\n  const gpu = new GPU({ mode });\n  const texture = (\n    gpu.createKernel(function() {\n      return [\n        this.thread.x,\n        this.thread.y,\n        this.thread.z,\n        this.thread.x * this.thread.y * this.thread.z\n      ];\n    })\n      .setOutput([10, 10, 10])\n      .setPipeline(true)\n      .setPrecision('single')\n  )();\n  const expected = texture.toArray();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setConstants({\n      value: texture\n    })\n    .setConstantTypes({\n      value: 'Array1D(2)'\n    })\n    .setOutput([10, 10, 10])\n    .setPipeline(false)\n    .setPrecision('single');\n\n  assert.notEqual(texture.constructor, Array);\n  assert.equal(expected.constructor, Array);\n  assert.deepEqual(kernel(), expected);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(4) (GPU only) auto', () => {\n  testArray3D4();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Array3D(4) (GPU only) gpu', () => {\n  testArray3D4('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Array3D(4) (GPU only) webgl', () => {\n  testArray3D4('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Array3D(4) (GPU only) webgl2', () => {\n  testArray3D4('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Array3D(4) (GPU only) headlessgl', () => {\n  testArray3D4('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/constructor-features.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: constructor features');\n\nfunction channelCount(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, { output: [1] });\n  kernel();\n  assert.ok(kernel.kernel.constructor.features.channelCount >= 1);\n  gpu.destroy();\n}\n\n(GPU.isGPUSupported ? test : skip)('channelCount auto', () => {\n  channelCount();\n});\n\n(GPU.isGPUSupported ? test : skip)('channelCount gpu', () => {\n  channelCount('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('channelCount webgl', () => {\n  channelCount('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('channelCount webgl2', () => {\n  channelCount('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('channelCount headlessgl', () => {\n  channelCount('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/context-inheritance.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, WebGLKernel, WebGL2Kernel, HeadlessGLKernel } = require('../../src');\n\ndescribe('internal: context inheritance');\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  const gpu = new GPU({ context: context });\n  const simpleKernel = gpu.createKernel(function() {\n      return 1 + 1;\n  }, {\n      output: [1]\n  });\n  assert.equal(simpleKernel()[0], 2);\n  assert.equal(gpu.Kernel, WebGLKernel);\n  assert.equal(simpleKernel.context, context);\n  gpu.destroy();\n});\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  const gpu = new GPU({ context: context });\n  const simpleKernel = gpu.createKernel(function() {\n    return 1 + 1;\n  }, {\n    output: [1]\n  });\n  assert.equal(simpleKernel()[0], 2);\n  assert.equal(gpu.Kernel, WebGL2Kernel);\n  assert.equal(simpleKernel.context, context);\n  gpu.destroy();\n});\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  const context = require('gl')(1,1);\n  const gpu = new GPU({ context: context });\n  const simpleKernel = gpu.createKernel(function() {\n    return 1 + 1;\n  }, {\n    output: [1]\n  });\n  assert.equal(simpleKernel()[0], 2);\n  assert.equal(gpu.Kernel, HeadlessGLKernel);\n  assert.equal(simpleKernel.context, context);\n  gpu.destroy();\n});\n"
  },
  {
    "path": "test/internal/deep-types.js",
    "content": "const sinon = require('sinon');\nconst { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU, FunctionBuilder } = require('../../src');\n\ndescribe('internal: deep types');\n\nfunction oneLayerDeepFloat(mode) {\n  const gpu = new GPU({ mode });\n  function childFunction(childFunctionArgument1) {\n    return childFunctionArgument1 + 1;\n  }\n  gpu.addFunction(childFunction);\n\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return childFunction(kernelArgument1);\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(1.5);\n    assert.equal(result[0], 2.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 1);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'childFunction');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('one layer deep float WebGL', () => {\n  oneLayerDeepFloat('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('one layer deep float WebGL2', () => {\n  oneLayerDeepFloat('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('one layer deep float HeadlessGL', () => {\n  oneLayerDeepFloat('headlessgl');\n});\n\nfunction twoLayerDeepFloat(mode) {\n  const gpu = new GPU({ mode });\n  function child1Function(child1FunctionArgument1) {\n    return child2Function(child1FunctionArgument1);\n  }\n  function child2Function(child2FunctionArgument1) {\n    return child2FunctionArgument1 + 1;\n  }\n  gpu\n    .addFunction(child1Function)\n    .addFunction(child2Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(kernelArgument1);\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(1.5);\n    assert.equal(result[0], 2.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 3);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('two layer deep float WebGL', () => {\n  twoLayerDeepFloat('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('two layer deep float WebGL2', () => {\n  twoLayerDeepFloat('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('two layer deep float HeadlessGL', () => {\n  twoLayerDeepFloat('headlessgl');\n});\n\nfunction twoArgumentLayerDeepFloat(mode) {\n  const gpu = new GPU({ mode });\n  function child1Function(child1FunctionArgument1) {\n    return child1FunctionArgument1 + 1;\n  }\n  function child2Function(child2FunctionArgument1) {\n    return child2FunctionArgument1 + 1;\n  }\n  gpu\n    .addFunction(child1Function)\n    .addFunction(child2Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(child2Function(kernelArgument1));\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(1.5);\n    assert.equal(kernel.returnType, 'Float');\n    assert.equal(result[0], 3.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 3);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child1Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child2Function');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('two argument layer deep float WebGL', () => {\n  twoArgumentLayerDeepFloat('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('two argument layer deep float WebGL2', () => {\n  twoArgumentLayerDeepFloat('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('two argument layer deep float HeadlessGL', () => {\n  twoArgumentLayerDeepFloat('headlessgl');\n});\n\n\nfunction threeLayerDeepFloat(mode) {\n  const gpu = new GPU({ mode });\n  function child1Function(child1FunctionArgument1) {\n    return child2Function(child1FunctionArgument1);\n  }\n  function child2Function(child2FunctionArgument1) {\n    return child3Function(child2FunctionArgument1 + 1);\n  }\n  function child3Function(child3FunctionArgument1) {\n    return child3FunctionArgument1 + 1;\n  }\n  gpu\n    .addFunction(child1Function)\n    .addFunction(child2Function)\n    .addFunction(child3Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(kernelArgument1);\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(1.5);\n    assert.equal(result[0], 3.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child1Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child3Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('three layer deep float WebGL', () => {\n  threeLayerDeepFloat('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('three layer deep float WebGL2', () => {\n  threeLayerDeepFloat('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('three layer deep float HeadlessGL', () => {\n  threeLayerDeepFloat('headlessgl');\n});\n\nfunction threeArgumentLayerDeepFloat(mode) {\n  const gpu = new GPU({ mode });\n  function child1Function(child1FunctionArgument1) {\n    return child1FunctionArgument1 + 1;\n  }\n  function child2Function(child2FunctionArgument1) {\n    return child2FunctionArgument1 + 1;\n  }\n  function child3Function(child3FunctionArgument1) {\n    return child3FunctionArgument1 + 1;\n  }\n  gpu\n    .addFunction(child1Function)\n    .addFunction(child2Function)\n    .addFunction(child3Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(child2Function(child3Function(kernelArgument1)));\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(1.5);\n    assert.equal(result[0], 4.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child3Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child1Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isWebGLSupported ? test : skip)('three argument layer deep float WebGL', () => {\n  threeArgumentLayerDeepFloat('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('three argument layer deep float WebGL2', () => {\n  threeArgumentLayerDeepFloat('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('three argument layer deep float HeadlessGL', () => {\n  threeArgumentLayerDeepFloat('headlessgl');\n});\n\nfunction threeArgumentLayerDeepNumberTexture1(mode) {\n  const gpu = new GPU({ mode });\n  const texture = gpu.createKernel(function() {\n    return 1.5;\n  }, { output: [1], pipeline: true, precision: 'single' })();\n  function child1Function(child1FunctionArgument1) {\n    return child1FunctionArgument1 + 1;\n  }\n  function child2Function(child2FunctionArgument1) {\n    return child2FunctionArgument1 + 1;\n  }\n  function child3Function(child3FunctionArgument1) {\n    return child3FunctionArgument1[this.thread.x] + 1;\n  }\n  gpu\n    .addFunction(child1Function)\n    .addFunction(child2Function)\n    .addFunction(child3Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(child2Function(child3Function(kernelArgument1)));\n  }, { output: [1] });\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n  try {\n    const result = kernel(texture);\n    assert.equal(result[0], 4.5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'child3Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'child1Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'child2Function');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'child3Function');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('three argument layer deep NumberTexture(1) WebGL', () => {\n  threeArgumentLayerDeepNumberTexture1('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('three argument layer deep NumberTexture(1) WebGL2', () => {\n  threeArgumentLayerDeepNumberTexture1('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('three argument layer deep NumberTexture(1) HeadlessGL', () => {\n  threeArgumentLayerDeepNumberTexture1('headlessgl');\n});\n\nfunction circlicalLogic(mode) {\n  const gpu = new GPU({ mode });\n  function child1Function(child1FunctionArgument1) {\n    return child1Function(child1FunctionArgument1);\n  }\n  gpu\n    .addFunction(child1Function);\n  const kernel = gpu.createKernel(function(kernelArgument1) {\n    return child1Function(kernelArgument1);\n  }, { output: [1] });\n  assert.throws(() => {\n    kernel(1.5);\n  });\n}\n\n(GPU.isWebGLSupported ? test : skip)('circlical logic webgl', () => {\n  circlicalLogic('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('circlical logic webgl2', () => {\n  circlicalLogic('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('circlical logic headlessgl', () => {\n  circlicalLogic('headlessgl');\n});\n\nfunction arrayTexture1(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(functionValue) {\n    return functionValue[this.thread.x] + 1;\n  }\n  gpu.addFunction(addOne);\n  const texture1 = gpu.createKernel(function() {\n    return 1;\n  }, {\n    output: [1],\n    precision: 'single',\n    pipeline: true,\n  })();\n  if (mode !== 'cpu') {\n    assert.equal(texture1.type, 'ArrayTexture(1)');\n  }\n\n  const kernel = gpu.createKernel(function(kernelValue) {\n    return addOne(kernelValue);\n  }, { output: [1] });\n  const result = kernel(texture1);\n  assert.equal(result[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(1) auto', ()=> {\n  arrayTexture1();\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(1) gpu', ()=> {\n  arrayTexture1('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('ArrayTexture(1) webgl', ()=> {\n  arrayTexture1('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('ArrayTexture(1) webgl2', ()=> {\n  arrayTexture1('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('ArrayTexture(1) headlessgl', ()=> {\n  arrayTexture1('headlessgl');\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(1) cpu', ()=> {\n  arrayTexture1('cpu');\n});\n\nfunction arrayTexture2(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(functionValue) {\n    const declaredValue = functionValue[this.thread.x];\n    return declaredValue[0] + 1 + declaredValue[1] + 1;\n  }\n  gpu.addFunction(addOne);\n  const texture1 = gpu.createKernel(function() {\n    return [1,2];\n  }, {\n    output: [1],\n    precision: 'single',\n    pipeline: true,\n  })();\n  if (mode !== 'cpu') {\n    assert.equal(texture1.type, 'ArrayTexture(2)');\n  }\n\n  const kernel = gpu.createKernel(function(kernelValue) {\n    return addOne(kernelValue);\n  }, { output: [1] });\n  const result = kernel(texture1);\n  assert.equal(result[0], 5);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(2) auto', ()=> {\n  arrayTexture2();\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(2) gpu', ()=> {\n  arrayTexture2('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('ArrayTexture(2) webgl', ()=> {\n  arrayTexture2('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('ArrayTexture(2) webgl2', ()=> {\n  arrayTexture2('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('ArrayTexture(2) headlessgl', ()=> {\n  arrayTexture2('headlessgl');\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(2) cpu', ()=> {\n  arrayTexture2('cpu');\n});\n\nfunction arrayTexture3(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(functionValue) {\n    const declaredValue = functionValue[this.thread.x];\n    return declaredValue[0] + 1\n      + declaredValue[1] + 1\n      + declaredValue[2] + 1;\n  }\n  gpu.addFunction(addOne);\n  const texture1 = gpu.createKernel(function() {\n    return [1,2,3];\n  }, {\n    output: [1],\n    precision: 'single',\n    pipeline: true,\n  })();\n  if (mode !== 'cpu') {\n    assert.equal(texture1.type, 'ArrayTexture(3)');\n  }\n\n  const kernel = gpu.createKernel(function(kernelValue) {\n    return addOne(kernelValue);\n  }, { output: [1] });\n  const result = kernel(texture1);\n  assert.equal(result[0], 9);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(3) auto', ()=> {\n  arrayTexture3();\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(3) gpu', ()=> {\n  arrayTexture3('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('ArrayTexture(3) webgl', ()=> {\n  arrayTexture3('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('ArrayTexture(3) webgl2', ()=> {\n  arrayTexture3('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('ArrayTexture(3) headlessgl', ()=> {\n  arrayTexture3('headlessgl');\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(3) cpu', ()=> {\n  arrayTexture3('cpu');\n});\n\nfunction arrayTexture4(mode) {\n  const gpu = new GPU({ mode });\n  function addOne(functionValue) {\n    const declaredValue = functionValue[this.thread.x];\n    return declaredValue[0] + 1\n     + declaredValue[1] + 1\n     + declaredValue[2] + 1\n     + declaredValue[3] + 1;\n  }\n  gpu.addFunction(addOne);\n  const texture1 = gpu.createKernel(function() {\n    return [1,2,3,4];\n  }, {\n    output: [1],\n    precision: 'single',\n    pipeline: true,\n  })();\n  if (mode !== 'cpu') {\n    assert.equal(texture1.type, 'ArrayTexture(4)');\n  }\n\n  const kernel = gpu.createKernel(function(kernelValue) {\n    return addOne(kernelValue);\n  }, { output: [1] });\n  const result = kernel(texture1);\n  assert.equal(result[0], 14);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(4) auto', ()=> {\n  arrayTexture4();\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(4) gpu', ()=> {\n  arrayTexture4('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('ArrayTexture(4) webgl', ()=> {\n  arrayTexture4('webgl');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('ArrayTexture(4) webgl2', ()=> {\n  arrayTexture4('webgl2');\n});\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('ArrayTexture(4) headlessgl', ()=> {\n  arrayTexture4('headlessgl');\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('ArrayTexture(4) cpu', ()=> {\n  arrayTexture4('cpu');\n});\n\nfunction testTortureTest(mode) {\n  const gpu = new GPU({ mode });\n  function addFloatArray(addFloatArrayArgument1, addFloatArrayArgument2) {\n    return addFloatArrayArgument1 + addFloatArrayArgument2[this.thread.x];\n  }\n  function addArrayFloat(addArrayFloatArgument1, addArrayFloatArgument2) {\n    return addArrayFloatArgument1[this.thread.x] + addArrayFloatArgument2;\n  }\n  function addArrayArray(addArrayArrayArgument1, addArrayArrayArgument2) {\n    return addArrayArrayArgument1[this.thread.x] + addArrayArrayArgument2[this.thread.x];\n  }\n  function addFloatFloat(addFloatFloatArgument1, addFloatFloatArgument2) {\n    return addFloatFloatArgument1 + addFloatFloatArgument2;\n  }\n  gpu\n    .addFunction(addFloatArray)\n    .addFunction(addArrayFloat)\n    .addFunction(addArrayArray)\n    .addFunction(addFloatFloat);\n\n  const texture = gpu.createKernel(function() { return 2; }, { output: [1], precision: 'single' })();\n  // sinon.spy(FunctionBuilder.prototype, 'lookupArgumentType');\n  sinon.spy(FunctionBuilder.prototype, 'lookupReturnType');\n\n  try {\n    const kernel = gpu.createKernel(function (v1, v2, v3, v4, v5) {\n      return addFloatFloat(v4, addArrayFloat(v3, addFloatArray(addArrayArray(v1, v5), v2)));\n    }, {output: [1]});\n\n    const result = kernel([1], texture, [3], 4, new Float32Array([5]));\n    assert.equal(result[0], 1 + 2 + 3 + 4 + 5);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.callCount, 7);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues.length, 7);\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[0][0], 'addArrayArray');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[0], 'Number');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[1][0], 'addFloatArray');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[1], 'Number');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[2][0], 'addArrayFloat');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[2], 'Number');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[3][0], 'addFloatFloat');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[3], 'Float');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[4][0], 'addArrayFloat');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[4], 'Number');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[5][0], 'addFloatArray');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[5], 'Number');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.args[6][0], 'addArrayArray');\n    assert.equal(FunctionBuilder.prototype.lookupReturnType.returnValues[6], 'Number');\n  } finally {\n    FunctionBuilder.prototype.lookupReturnType.restore();\n  }\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('torture test auto', () => {\n  testTortureTest();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('torture test gpu', () => {\n  testTortureTest('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('torture test webgl', () => {\n  testTortureTest('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('torture test webgl2', () => {\n  testTortureTest('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('torture test headlessgl', () => {\n  testTortureTest('headlessgl');\n});\n\ntest('torture test cpu', () => {\n  testTortureTest('cpu');\n});\n\nfunction testKernelMap(mode) {\n  const gpu = new GPU({ mode });\n  function calc1(v1, v2) {\n    return v2[this.thread.x] - v1;\n  }\n  function calc2(v1, v2) {\n    return v1 * v2;\n  }\n  const kernelMap = gpu.createKernelMap({\n    calc1,\n    calc2,\n  }, function (outputs, targets) {\n    const output = outputs[this.thread.x];\n    return calc2(calc1(output, targets), output);\n  }, { output: [1], pipeline: true });\n  try {\n    const result = kernelMap([1], [3]);\n    assert.equal(result.calc1.toArray()[0], 2);\n    assert.equal(result.calc2.toArray()[0], 2);\n    assert.equal(result.result.toArray()[0], 2);\n  } finally {\n    gpu.destroy();\n  }\n}\n\n\n(GPU.isWebGLSupported ? test : skip)('kernel map webgl', () => {\n  testKernelMap('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('kernel map webgl2', () => {\n  testKernelMap('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('kernel map headlessgl', () => {\n  testKernelMap('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/deprecated.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU, Kernel } = require('../../src');\n\ndescribe('internal: deprecated');\n\ntest('GPU.createKernel settings floatOutput true', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { floatOutput: true });\n  assert.equal(kernel.precision, 'single');\n  assert.notOk(kernel.kernel.hasOwnProperty('floatOutput'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings floatOutput false', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { floatOutput: false });\n  assert.equal(kernel.precision, 'unsigned');\n  assert.notOk(kernel.kernel.hasOwnProperty('floatOutput'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings outputToTexture true', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { outputToTexture: true });\n  assert.equal(kernel.pipeline, true);\n  assert.notOk(kernel.kernel.hasOwnProperty('outputToTexture'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings outputToTexture false', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { outputToTexture: false });\n  assert.equal(kernel.pipeline, false);\n  assert.notOk(kernel.kernel.hasOwnProperty('outputToTexture'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings outputImmutable true', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { outputImmutable: true });\n  assert.equal(kernel.immutable, true);\n  assert.notOk(kernel.kernel.hasOwnProperty('outputImmutable'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings outputImmutable false', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { outputImmutable: false });\n  assert.equal(kernel.immutable, false);\n  assert.notOk(kernel.kernel.hasOwnProperty('outputImmutable'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings floatTextures true', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { floatTextures: true });\n  assert.equal(kernel.optimizeFloatMemory, true);\n  assert.notOk(kernel.kernel.hasOwnProperty('floatTextures'));\n  gpu.destroy();\n});\n\ntest('GPU.createKernel settings floatTextures false', () => {\n  const gpu = new GPU();\n  const kernel = gpu.createKernel(function() {}, { floatTextures: false });\n  assert.equal(kernel.optimizeFloatMemory, false);\n  assert.notOk(kernel.kernel.hasOwnProperty('floatTextures'));\n  gpu.destroy();\n});\n\ntest('Kernel.getCanvas', () => {\n  const canvas = {};\n  const kernel = new Kernel(`function() {}`);\n  kernel.initContext = () => {};\n  kernel.initPlugins = () => {};\n  kernel.mergeSettings({\n    canvas\n  });\n  assert.equal(kernel.getCanvas(), canvas);\n});\n\ntest('Kernel.getWebGl', () => {\n  const canvas = {};\n  const context = {};\n  const kernel = new Kernel(`function() {}`);\n  kernel.initContext = () => {};\n  kernel.initPlugins = () => {};\n  kernel.mergeSettings({\n    canvas,\n    context\n  });\n  assert.equal(kernel.getWebGl(), context);\n});\n\ntest('Kernel.setOutputToTexture', () => {\n  const kernel = new Kernel(`function() {}`);\n  kernel.setOutputToTexture(true);\n  assert.equal(kernel.pipeline, true);\n});\n"
  },
  {
    "path": "test/internal/different-texture-cloning.js",
    "content": "const { assert, test, module: describe, only, skip } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: different texture cloning');\n\nfunction testArrayThenArray1D4(mode) {\n  const gpu = new GPU({ mode });\n  function createTextureOf(value, type) {\n    return (gpu.createKernel(function(value) {\n      return value[this.thread.x];\n    }, {\n      output: [1],\n      pipeline: true,\n      argumentTypes: { value: type }\n    }))(value);\n  }\n  const arrayTexture = createTextureOf([1], 'Array');\n  const arrayTextureClone = arrayTexture.clone();\n  const array4Texture = createTextureOf([[1,2,3,4]], 'Array1D(4)');\n  const array4TextureClone = array4Texture.clone();\n  assert.notEqual(arrayTextureClone, array4TextureClone);\n  assert.deepEqual(arrayTextureClone.toArray(), new Float32Array([1]));\n  assert.deepEqual(array4TextureClone.toArray(), [new Float32Array([1,2,3,4])]);\n  gpu.destroy();\n}\n\ntest('Array then Array1D(4) auto', () => {\n  testArrayThenArray1D4();\n});\n\n(GPU.isWebGLSupported ? test : skip)('Array then Array1D(4) webgl', () => {\n  testArrayThenArray1D4('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Array then Array1D(4) webgl2', () => {\n  testArrayThenArray1D4('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Array then Array1D(4) headlessgl', () => {\n  testArrayThenArray1D4('headlessgl');\n});\n\nfunction testArray1D4ThenArray(mode) {\n  const gpu = new GPU({ mode });\n  function createTextureOf(value, type) {\n    return (gpu.createKernel(function(value) {\n      return value[this.thread.x];\n    }, {\n      output: [1],\n      pipeline: true,\n      argumentTypes: { value: type }\n    }))(value);\n  }\n  const array4Texture = createTextureOf([[1,2,3,4]], 'Array1D(4)');\n  const array4TextureClone = array4Texture.clone();\n  const arrayTexture = createTextureOf([1], 'Array');\n  const arrayTextureClone = arrayTexture.clone();\n  assert.notEqual(array4TextureClone, arrayTextureClone);\n  assert.deepEqual(array4TextureClone.toArray(), [new Float32Array([1,2,3,4])]);\n  assert.deepEqual(arrayTextureClone.toArray(), new Float32Array([1]));\n  gpu.destroy();\n}\n\ntest('Array1D(4) then Array auto', () => {\n  testArray1D4ThenArray();\n});\n\n(GPU.isWebGLSupported ? test : skip)('Array1D(4) then Array webgl', () => {\n  testArray1D4ThenArray('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Array1D(4) then Array webgl2', () => {\n  testArray1D4ThenArray('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Array1D(4) then Array headlessgl', () => {\n  testArray1D4ThenArray('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/function-builder.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { FunctionBuilder, CPUFunctionNode, WebGL2FunctionNode, WebGLFunctionNode } = require('../../src');\n\ndescribe('internal: function builder');\n\n// Three layer template for multiple tests\nfunction threeLayerTemplate(FunctionNode) {\n  function layerOne() {\n    return 42;\n  }\n\n  function layerTwo() {\n    return layerOne() * 2;\n  }\n\n  function layerThree() {\n    return layerTwo() * 2;\n  }\n\n  // Create a function hello node\n  return new FunctionBuilder({\n    functionNodes: [\n      new FunctionNode(layerOne.toString(), {\n        output: [1],\n        lookupReturnType: () => 'Number',\n        lookupFunctionArgumentTypes: () => {}\n      }),\n      new FunctionNode(layerTwo.toString(), {\n        output: [1],\n        lookupReturnType: () => 'Number',\n        lookupFunctionArgumentTypes: () => {}\n      }),\n      new FunctionNode(layerThree.toString(), {\n        output: [1],\n        lookupReturnType: () => 'Number',\n        lookupFunctionArgumentTypes: () => {}\n      }),\n    ],\n    output: [1]\n  });\n}\n\n/// Test the function tracing of 3 layers\ntest('traceFunctionCalls: 3 layer test cpu', () => {\n  const builder = threeLayerTemplate(CPUFunctionNode);\n  assert.deepEqual(builder.traceFunctionCalls('layerOne'), ['layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerTwo'), ['layerTwo', 'layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerThree'), ['layerThree', 'layerTwo', 'layerOne']);\n});\n\ntest('traceFunctionCalls: 3 layer test webgl', () => {\n  const builder = threeLayerTemplate(WebGLFunctionNode);\n  assert.deepEqual(builder.traceFunctionCalls('layerOne'), ['layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerTwo'), ['layerTwo', 'layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerThree'), ['layerThree', 'layerTwo', 'layerOne']);\n});\n\ntest('traceFunctionCalls: 3 layer test webgl2', () => {\n  const builder = threeLayerTemplate(WebGL2FunctionNode);\n  assert.deepEqual(builder.traceFunctionCalls('layerOne'), ['layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerTwo'), ['layerTwo', 'layerOne']);\n  assert.deepEqual(builder.traceFunctionCalls('layerThree'), ['layerThree', 'layerTwo', 'layerOne']);\n});\n\n/// Test the function tracing of 3 layers\ntest('webglString: 3 layer test cpu', () => {\n  const builder = threeLayerTemplate(CPUFunctionNode);\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne']),\n    'function layerOne() {\\nreturn 42;\\n}'\n  );\n  assert.equal(\n    builder.getString('layerOne'),\n    builder.getStringFromFunctionNames(['layerOne'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo']),\n    'function layerOne() {\\nreturn 42;\\n}\\nfunction layerTwo() {\\nreturn (layerOne()*2);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerTwo'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree']),\n    'function layerOne() {\\nreturn 42;\\n}\\nfunction layerTwo() {\\nreturn (layerOne()*2);\\n}\\nfunction layerThree() {\\nreturn (layerTwo()*2);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerThree'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree'])\n  );\n  assert.equal(\n    builder.getString(null),\n    builder.getString('layerThree')\n  );\n});\n\ntest('webglString: 3 layer test webgl', () => {\n  const builder = threeLayerTemplate(WebGLFunctionNode);\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne']),\n    'float layerOne() {\\nreturn 42.0;\\n}'\n  );\n  assert.equal(\n    builder.getString('layerOne'),\n    builder.getStringFromFunctionNames(['layerOne'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo']),\n    'float layerOne() {\\nreturn 42.0;\\n}\\nfloat layerTwo() {\\nreturn (layerOne()*2.0);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerTwo'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree']),\n    'float layerOne() {\\nreturn 42.0;\\n}\\nfloat layerTwo() {\\nreturn (layerOne()*2.0);\\n}\\nfloat layerThree() {\\nreturn (layerTwo()*2.0);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerThree'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree'])\n  );\n  assert.equal(\n    builder.getString(null),\n    builder.getString('layerThree')\n  );\n});\n\ntest('webglString: 3 layer test webgl2', () => {\n  const builder = threeLayerTemplate(WebGL2FunctionNode);\n  assert.notEqual(builder, null, 'class creation check');\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne']),\n    'float layerOne() {\\nreturn 42.0;\\n}'\n  );\n  assert.equal(\n    builder.getString('layerOne'),\n    builder.getStringFromFunctionNames(['layerOne'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo']),\n    'float layerOne() {\\nreturn 42.0;\\n}\\nfloat layerTwo() {\\nreturn (layerOne()*2.0);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerTwo'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo'])\n  );\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree']),\n    'float layerOne() {\\nreturn 42.0;\\n}\\nfloat layerTwo() {\\nreturn (layerOne()*2.0);\\n}\\nfloat layerThree() {\\nreturn (layerTwo()*2.0);\\n}'\n  );\n  assert.equal(\n    builder.getString('layerThree'),\n    builder.getStringFromFunctionNames(['layerOne','layerTwo','layerThree'])\n  );\n  assert.equal(\n    builder.getString(null),\n    builder.getString('layerThree')\n  );\n});\n"
  },
  {
    "path": "test/internal/function-composition.js",
    "content": "const { assert, test, skip, module: describe, only } = require('qunit');\nconst sinon = require('sinon');\nconst { CPUFunctionNode, FunctionBuilder, GPU, WebGL2FunctionNode, WebGLFunctionNode } = require('../../src');\n\ndescribe('internal: function composition return values');\n\nfunction functionCompositionReturnValuesTest(mode) {\n  const gpu = new GPU({ mode });\n  return gpu.createKernel(function(oneToFour, fourToOne) {\n    function add(left, right) {\n      return left[this.thread.x] + right[this.thread.x];\n    }\n    return add(oneToFour, fourToOne);\n  }, { output: [4] })([1,2,3,4], [4,3,2,1]);\n}\n\ntest('auto', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest()), [5,5,5,5]);\n});\ntest('gpu', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest('gpu')), [5,5,5,5]);\n});\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest('webgl')), [5,5,5,5]);\n});\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest('webgl2')), [5,5,5,5]);\n});\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest('headlessgl')), [5,5,5,5]);\n});\ntest('cpu', () => {\n  assert.deepEqual(Array.from(functionCompositionReturnValuesTest('cpu')), [5,5,5,5]);\n});\n\n\ndescribe('internal: function composition FunctionNode');\n\nfunction functionCompositionFunctionNode(FunctionNode) {\n  const output = [1];\n  const node = new FunctionNode(`function kernel() {\n    function inner() { return 1; }\n    \n    return inner();\n  }`, {\n    output,\n    onNestedFunction: sinon.spy(),\n    lookupReturnType: () => 'Number',\n    lookupFunctionArgumentTypes: () => {}\n  });\n\n  const string = node.toString();\n  assert.equal(node.onNestedFunction.callCount, 1);\n  return string;\n}\n\ntest('CPUFunctionNode', () => {\n  assert.equal(functionCompositionFunctionNode(CPUFunctionNode), 'function kernel() {'\n    + '\\n'\n    + '\\nreturn inner();'\n    + '\\n}');\n});\ntest('WebGLFunctionNode', () => {\n  assert.equal(functionCompositionFunctionNode(WebGLFunctionNode), 'float kernel() {'\n    + '\\n'\n    + '\\nreturn inner();'\n    + '\\n}');\n});\ntest('WebGL2FunctionNode', () => {\n  assert.equal(functionCompositionFunctionNode(WebGL2FunctionNode), 'float kernel() {'\n    + '\\n'\n    + '\\nreturn inner();'\n    + '\\n}');\n});\n\ndescribe('internal: number function composition FunctionBuilder');\n\nfunction numberFunctionCompositionFunctionBuilder(FunctionNode) {\n  const output = [1];\n  const builder = FunctionBuilder.fromKernel({\n    source: `function kernel() {\n    function inner() { return 1; }\n    \n    return inner();\n  }`,\n    argumentTypes: [],\n    argumentNames: [],\n    kernelArguments: [],\n    kernelConstants: [],\n    output,\n    leadingReturnStatement: 'resultX[x] = '\n  }, FunctionNode);\n\n  return builder.getPrototypeString('kernel');\n}\n\ntest('CPUFunctionNode', () => {\n  assert.equal(numberFunctionCompositionFunctionBuilder(CPUFunctionNode), 'function inner() {'\n    + '\\nreturn 1;'\n    + '\\n}'\n    + '\\nresultX[x] = inner();\\ncontinue;');\n});\ntest('WebGLFunctionNode', () => {\n  assert.equal(numberFunctionCompositionFunctionBuilder(WebGLFunctionNode), 'float inner() {'\n    + '\\nreturn 1.0;'\n    + '\\n}'\n    + '\\nvoid kernel() {'\n    + '\\n'\n    + '\\nkernelResult = inner();return;'\n    + '\\n}');\n});\ntest('WebGL2FunctionNode', () => {\n  assert.equal(numberFunctionCompositionFunctionBuilder(WebGL2FunctionNode), 'float inner() {'\n    + '\\nreturn 1.0;'\n    + '\\n}'\n    + '\\nvoid kernel() {'\n    + '\\n'\n    + '\\nkernelResult = inner();return;'\n    + '\\n}');\n});\n\ndescribe('internal: Array(2) function composition FunctionBuilder');\n\nfunction array2FunctionCompositionFunctionBuilder(FunctionNode) {\n  const output = [1];\n  const builder = FunctionBuilder.fromKernel({\n    source: `function kernel() {\n    function inner() { return [1,2,3,4]; }\n    \n    return inner()[0];\n  }`,\n    argumentTypes: [],\n    argumentNames: [],\n    kernelArguments: [],\n    kernelConstants: [],\n    output,\n    leadingReturnStatement: 'resultX[x] = '\n  }, FunctionNode);\n\n  return builder.getPrototypeString('kernel');\n}\n\ntest('CPUFunctionNode', () => {\n  assert.equal(array2FunctionCompositionFunctionBuilder(CPUFunctionNode), 'function inner() {'\n    + '\\nreturn new Float32Array([1, 2, 3, 4]);'\n    + '\\n}'\n    + '\\nresultX[x] = inner()[0];\\ncontinue;');\n});\ntest('WebGLFunctionNode', () => {\n  assert.equal(array2FunctionCompositionFunctionBuilder(WebGLFunctionNode), 'vec4 inner() {'\n  + '\\nreturn vec4(1.0, 2.0, 3.0, 4.0);'\n  + '\\n}'\n  + '\\nvoid kernel() {'\n  + '\\n'\n  + '\\nkernelResult = inner()[0];return;'\n  + '\\n}');\n});\ntest('WebGL2FunctionNode', () => {\n  assert.equal(array2FunctionCompositionFunctionBuilder(WebGL2FunctionNode), 'vec4 inner() {'\n    + '\\nreturn vec4(1.0, 2.0, 3.0, 4.0);'\n    + '\\n}'\n    + '\\nvoid kernel() {'\n    + '\\n'\n    + '\\nkernelResult = inner()[0];return;'\n    + '\\n}');\n});\n"
  },
  {
    "path": "test/internal/function-node.js",
    "content": "const { assert, test, module: describe, only } = require('qunit');\nconst { CPUFunctionNode, WebGLFunctionNode, WebGL2FunctionNode } = require('../../src');\n\ndescribe('internal: function node');\n\n/// Test the creation of a hello_world function\ntest('hello_world: just return magic 42 cpu', () => {\n  // Create a function hello node\n  const node = new CPUFunctionNode(\n    (function() {\n      return 42;\n    }).toString(), { name: 'hello_world', output: [1] }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'function hello_world() {'\n    + '\\nreturn 42;'\n    + '\\n}',\n    'function conversion check'\n  );\n});\n\ntest('hello_world: just return magic 42 webgl', () => {\n  // Create a function hello node\n  const node = new WebGLFunctionNode(\n    (function() {\n      return 42;\n    }).toString(), { name: 'hello_world', output: [1] }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float hello_world() {'\n    + '\\nreturn 42.0;'\n    + '\\n}',\n    'function conversion check'\n  );\n});\n\ntest('hello_world: just return magic 42 webgl2', () => {\n  // Create a function hello node\n  const node = new WebGL2FunctionNode(\n    (function() {\n      return 42;\n    }).toString(), { name: 'hello_world', output: [1] }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float hello_world() {'\n    + '\\nreturn 42.0;'\n    + '\\n}',\n    'function conversion check'\n  );\n});\n\n/// Test creation of function, that calls another function\ntest('hello_inner: call a function inside a function cpu', () => {\n  function inner() {\n    return 42;\n  }\n\n  // Create a function hello node\n  const node = new CPUFunctionNode(\n    (function() {\n      return inner();\n    }).toString(),\n    {\n      name: 'hello_inner',\n      output: [1],\n      lookupReturnType: () => 'Number',\n      lookupFunctionArgumentTypes: () => {}\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'function hello_inner() {'\n    + '\\nreturn inner();'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['inner'] );\n});\n\ntest('hello_inner: call a function inside a function webgl', () => {\n  function inner() {\n    return 42;\n  }\n\n  // Create a function hello node\n  const node = new WebGLFunctionNode(\n    (function() {\n      return inner();\n    }).toString(), {\n      name: 'hello_inner',\n      output: [1],\n      lookupReturnType: () => 'Number',\n      lookupFunctionArgumentTypes: () => {}\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float hello_inner() {'\n    + '\\nreturn inner();'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['inner'] );\n});\n\n/// Test creation of function, that calls another function\ntest('hello_inner: call a function inside a function webgl2', () => {\n  function inner() {\n    return 42;\n  }\n\n  // Create a function hello node\n  const node = new WebGL2FunctionNode(\n    (function() {\n      return inner();\n    }).toString(), {\n      name: 'hello_inner',\n      output: [1],\n      lookupReturnType: () => 'Number',\n      lookupFunctionArgumentTypes: () => {}\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float hello_inner() {'\n    + '\\nreturn inner();'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['inner'] );\n});\n\n/// Test creation of function, that calls another function, with ARGS\ntest('Math.round implementation: A function with arguments cpu', () => {\n  // Math.round node\n  const node = new CPUFunctionNode(\n    (function(a) {\n      return Math.floor(a + 0.5);\n    }).toString(),\n    {\n      name: 'foo',\n      output: [1],\n      argumentTypes: ['Number'],\n      lookupFunctionArgumentTypes: () => {},\n      triggerImplyArgumentType: () => {},\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'function foo(user_a) {'\n    + '\\nreturn Math.floor((user_a+0.5));'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['Math.floor']);\n});\n\ntest('Math.round implementation: A function with arguments webgl', () => {\n  // Math.round node\n  const node = new WebGLFunctionNode(\n    (function(a) {\n      return Math.floor(a + 0.5);\n    }).toString(), {\n      name: 'foo',\n      output: [1],\n      argumentTypes: ['Number']\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float foo(float user_a) {'\n    + '\\nreturn floor((user_a+0.5));'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['floor'] );\n});\n\ntest('Math.round implementation: A function with arguments webgl2', () => {\n  // Math.round node\n  const node = new WebGL2FunctionNode(\n    (function(a) {\n      return Math.floor(a + 0.5);\n    }).toString(), {\n      name: 'foo',\n      output: [1],\n      argumentTypes: ['Number']\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float foo(float user_a) {'\n    + '\\nreturn floor((user_a+0.5));'\n    + '\\n}',\n    'function conversion check'\n  );\n\n  assert.deepEqual(node.calledFunctions, ['floor'] );\n});\n\n/// Test creation of function, that calls another function, with ARGS\ntest('Two arguments test webgl', function(assert){\n  const node = new WebGLFunctionNode(\n    (function(a,b) {\n      return a+b;\n    }).toString(), {\n      name: 'add_together',\n      output: [1],\n      argumentTypes: ['Number', 'Number']\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float add_together(float user_a, float user_b) {'\n    + '\\nreturn (user_a+user_b);'\n    + '\\n}',\n    'function conversion check'\n  );\n});\n\ntest('Two arguments test webgl2', function(assert){\n  const node = new WebGL2FunctionNode(\n    (function(a,b) {\n      return a+b;\n    }).toString(), {\n      name: 'add_together',\n      output: [1],\n      argumentTypes: ['Number', 'Number']\n    }\n  );\n\n  assert.notEqual(node.getJsAST(), null, 'AST fetch check');\n\n  assert.equal(\n    node.toString(),\n    'float add_together(float user_a, float user_b) {'\n    + '\\nreturn (user_a+user_b);'\n    + '\\n}',\n    'function conversion check'\n  );\n});\n\n/// Test the creation of a hello_world function\ntest('Automatic naming support cpu', () => {\n  function hello_world() {\n    return 42;\n  }\n  // Create a function hello node\n  const node = new CPUFunctionNode(hello_world.toString(), { output: [1] });\n  assert.notEqual(node, null, 'class creation check');\n  assert.equal(node.name, 'hello_world');\n});\n\ntest('Automatic naming support webgl', () => {\n  function hello_world() {\n    return 42;\n  }\n  // Create a function hello node\n  const node = new WebGLFunctionNode(hello_world.toString(), { output: [1] });\n  assert.notEqual(node, null, 'class creation check');\n  assert.equal(node.name, 'hello_world');\n});\n\ntest('Automatic naming support webgl2', () => {\n  function hello_world() {\n    return 42;\n  }\n  // Create a function hello node\n  const node = new WebGL2FunctionNode(hello_world.toString(), { output: [1] });\n  assert.notEqual(node, null, 'class creation check');\n  assert.equal(node.name, 'hello_world');\n});\n"
  },
  {
    "path": "test/internal/function-return-type-detection.js",
    "content": "const { assert, test, skip, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: Function return type detection');\n\nfunction canDetectNumberFromAddedFunction(mode) {\n  const gpu = new GPU({ mode });\n  function number() {\n    return 1;\n  }\n  gpu.addFunction(number);\n  const kernel = gpu.createKernel(function() {\n    const values = number();\n    return values + values;\n  }, { output: [1] });\n\n  const result = kernel();\n  assert.equal(result[0], 2);\n\n  gpu.destroy();\n}\n\ntest('can detect Number auto', () => {\n  canDetectNumberFromAddedFunction();\n});\n\ntest('can detect Number gpu', () => {\n  canDetectNumberFromAddedFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('can detect Number webgl', () => {\n  canDetectNumberFromAddedFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('can detect Number webgl2', () => {\n  canDetectNumberFromAddedFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('can detect Number headlessgl', () => {\n  canDetectNumberFromAddedFunction('headlessgl');\n});\n\ntest('can detect Number cpu', () => {\n  canDetectNumberFromAddedFunction('cpu');\n});\n\nfunction canDetectArray2FromAddedFunction(mode) {\n  const gpu = new GPU({ mode });\n  function array2() {\n    return [1, 2];\n  }\n  gpu.addFunction(array2);\n  const kernel = gpu.createKernel(function() {\n    const values = array2();\n    return values[0] + values[1];\n  }, { output: [1] });\n\n  const result = kernel();\n  assert.equal(result[0], 3);\n\n  gpu.destroy();\n}\n\ntest('can detect Array(2) auto', () => {\n  canDetectArray2FromAddedFunction();\n});\n\ntest('can detect Array(2) gpu', () => {\n  canDetectArray2FromAddedFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('can detect Array(2) webgl', () => {\n  canDetectArray2FromAddedFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('can detect Array(2) webgl2', () => {\n  canDetectArray2FromAddedFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('can detect Array(2) headlessgl', () => {\n  canDetectArray2FromAddedFunction('headlessgl');\n});\n\ntest('can detect Array(2) cpu', () => {\n  canDetectArray2FromAddedFunction('cpu');\n});\n\n\nfunction canDetectArray3FromAddedFunction(mode) {\n  const gpu = new GPU({ mode });\n  function array2() {\n    return [1, 2, 3];\n  }\n  gpu.addFunction(array2);\n  const kernel = gpu.createKernel(function() {\n    const values = array2();\n    return values[0] + values[1] + values[2];\n  }, { output: [1] });\n\n  const result = kernel();\n  assert.equal(result[0], 6);\n\n  gpu.destroy();\n}\n\ntest('can detect Array(3) auto', () => {\n  canDetectArray3FromAddedFunction();\n});\n\ntest('can detect Array(3) gpu', () => {\n  canDetectArray3FromAddedFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('can detect Array(3) webgl', () => {\n  canDetectArray3FromAddedFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('can detect Array(3) webgl2', () => {\n  canDetectArray3FromAddedFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('can detect Array(3) headlessgl', () => {\n  canDetectArray3FromAddedFunction('headlessgl');\n});\n\ntest('can detect Array(3) cpu', () => {\n  canDetectArray3FromAddedFunction('cpu');\n});\n\n\nfunction canDetectArray4FromAddedFunction(mode) {\n  const gpu = new GPU({ mode });\n  function array2() {\n    return [1, 2, 3, 4];\n  }\n  gpu.addFunction(array2);\n  const kernel = gpu.createKernel(function() {\n    const values = array2();\n    return values[0] + values[1] + values[2] + values[3];\n  }, { output: [1] });\n\n  const result = kernel();\n  assert.equal(result[0], 10);\n\n  gpu.destroy();\n}\n\ntest('can detect Array(4) auto', () => {\n  canDetectArray4FromAddedFunction();\n});\n\ntest('can detect Array(4) gpu', () => {\n  canDetectArray4FromAddedFunction('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('can detect Array(4) webgl', () => {\n  canDetectArray4FromAddedFunction('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('can detect Array(4) webgl2', () => {\n  canDetectArray4FromAddedFunction('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('can detect Array(4) headlessgl', () => {\n  canDetectArray4FromAddedFunction('headlessgl');\n});\n\ntest('can detect Array(4) cpu', () => {\n  canDetectArray4FromAddedFunction('cpu');\n});\n"
  },
  {
    "path": "test/internal/function-tracer.js",
    "content": "const { assert, test, skip, module: describe, only } = require('qunit');\nconst sinon = require('sinon');\nconst acorn = require('acorn');\nconst { FunctionTracer } = require('../../src');\n\ndescribe('internal: FunctionTracer');\n\ntest('works with Program', () => {\n  const ast = acorn.parse(`var i;`);\n  const functionTracer = new FunctionTracer(ast);\n  assert.ok(functionTracer.functionContexts.length > 0);\n});\n\ntest('works with BlockStatement', () => {\n  const mockBody = {};\n  let called = false;\n  let calledBody = null;\n  const mockInstance = {\n    contexts: [],\n    runningContexts: [],\n    newContext: FunctionTracer.prototype.newContext,\n    scan: (body) => {\n      called = true;\n      calledBody = body;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'BlockStatement', body: mockBody });\n  assert.ok(called);\n  assert.equal(calledBody, mockBody);\n  assert.equal(mockInstance.contexts.length, 1);\n});\n\ntest('works with AssignmentExpression', () => {\n  const mockLeft = {};\n  const mockRight = {};\n  let called = false;\n  let calledSides = [];\n  const mockInstance = {\n    scan: (side) => {\n      called = true;\n      calledSides.push(side);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'AssignmentExpression', left: mockLeft, right: mockRight });\n  assert.ok(called);\n  assert.deepEqual(calledSides, [mockLeft, mockRight]);\n});\n\ntest('works with LogicalExpression', () => {\n  const mockLeft = {};\n  const mockRight = {};\n  let called = false;\n  let calledSides = [];\n  const mockInstance = {\n    scan: (side) => {\n      called = true;\n      calledSides.push(side);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'LogicalExpression', left: mockLeft, right: mockRight });\n  assert.ok(called);\n  assert.deepEqual(calledSides, [mockLeft, mockRight]);\n});\n\ntest('works with BinaryExpression', () => {\n  const mockLeft = {};\n  const mockRight = {};\n  let called = false;\n  let calledSides = [];\n  const mockInstance = {\n    scan: (side) => {\n      called = true;\n      calledSides.push(side);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'BinaryExpression', left: mockLeft, right: mockRight });\n  assert.ok(called);\n  assert.deepEqual(calledSides, [mockLeft, mockRight]);\n});\n\ntest('works with UpdateExpression', () => {\n  const mockArgument = {};\n  let called = false;\n  let calledBody = null;\n  const mockInstance = {\n    scan: (argument) => {\n      called = true;\n      calledBody = argument;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'UpdateExpression', argument: mockArgument });\n  assert.ok(called);\n  assert.equal(calledBody, mockArgument);\n});\n\ntest('works with UnaryExpression', () => {\n  const mockArgument = {};\n  let called = false;\n  let calledArgument = null;\n  const mockInstance = {\n    scan: (argument) => {\n      called = true;\n      calledArgument = argument;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'UpdateExpression', argument: mockArgument });\n  assert.ok(called);\n  assert.equal(calledArgument, mockArgument);\n});\n\ntest('works with VariableDeclaration', () => {\n  const mockDeclarations = [];\n  let called = false;\n  let calledDeclarations = null;\n  const mockInstance = {\n    scan: (declarations) => {\n      called = true;\n      calledDeclarations = declarations;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'VariableDeclaration', declarations: mockDeclarations });\n  assert.ok(called);\n  assert.deepEqual(calledDeclarations, mockDeclarations);\n});\n\ntest('works with generic VariableDeclarator', () => {\n  const ast = acorn.parse('var bob = 0;');\n\n  const functionTracer = new FunctionTracer(ast);\n  const { bob } = functionTracer.contexts[0];\n  assert.equal(bob.ast, ast.body[0].declarations[0]);\n  assert.equal(bob.context, functionTracer.contexts[0]);\n  assert.equal(bob.name, 'bob');\n  assert.equal(bob.origin, 'declaration');\n  assert.equal(bob.assignable, true);\n  assert.equal(bob.inForLoopTest, null);\n  assert.equal(bob.inForLoopInit, false);\n  assert.equal(functionTracer.declarations[0], bob);\n});\n\ntest('works with var VariableDeclarator', () => {\n  const ast = acorn.parse('var bob = 0;');\n\n  const functionTracer = new FunctionTracer(ast);\n  const { bob } = functionTracer.contexts[0];\n  assert.equal(bob.context['@contextType'], 'function');\n});\n\ntest('works with let VariableDeclarator', () => {\n  const ast = acorn.parse('let bob = 0;');\n\n  const functionTracer = new FunctionTracer(ast);\n  const { bob } = functionTracer.contexts[0];\n  assert.equal(bob.context['@contextType'], 'function');\n});\n\ntest('works with var & let VariableDeclarator together', () => {\n  const ast = acorn.parse(`var bob = 0;\n  for (let i = 0; i < 1; i++) { let pop = 0; }`);\n  const functionTracer = new FunctionTracer(ast);\n\n  assert.equal(functionTracer.contexts[0].bob.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[0].i, undefined);\n  assert.equal(functionTracer.contexts[0].pop, undefined);\n\n  assert.equal(functionTracer.contexts[1].bob.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[1].i.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[1].pop, undefined);\n\n  assert.equal(functionTracer.contexts[2].bob.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[2].i.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[2].pop, undefined);\n\n  assert.equal(functionTracer.contexts[3].bob.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[3].i.context['@contextType'], 'function');\n  assert.equal(functionTracer.contexts[3].pop.context['@contextType'], 'function');\n});\n\ntest('works with FunctionExpression when runningContexts.length = 0', () => {\n  const mockBody = {};\n  let called = false;\n  let calledBody = null;\n  const mockInstance = {\n    runningContexts: [],\n    functions: [],\n    scan: (body) => {\n      called = true;\n      calledBody = body;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'FunctionExpression', body: mockBody });\n  assert.ok(called);\n  assert.equal(calledBody, mockBody);\n  assert.equal(mockInstance.functions.length, 0);\n});\n\ntest('works with FunctionDeclaration when runningContexts.length = 0', () => {\n  const mockBody = {};\n  let called = false;\n  let calledBody = null;\n  const mockInstance = {\n    runningContexts: [],\n    functions: [],\n    scan: (body) => {\n      called = true;\n      calledBody = body;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'FunctionDeclaration', body: mockBody });\n  assert.ok(called);\n  assert.equal(calledBody, mockBody);\n  assert.equal(mockInstance.functions.length, 0);\n});\n\ntest('works with FunctionExpression when runningContexts.length > 0', () => {\n  const mockBody = {};\n  const mockInstance = {\n    functions: [],\n    runningContexts: [null],\n    scan: () => {\n      throw new Error('should not be called');\n    }\n  };\n  const mockAst = { type: 'FunctionExpression', body: mockBody };\n  FunctionTracer.prototype.scan.call(mockInstance, mockAst);\n  assert.equal(mockInstance.functions.length, 1);\n  assert.equal(mockInstance.functions[0], mockAst);\n});\n\ntest('works with FunctionDeclaration when runningContexts.length > 0', () => {\n  const mockBody = {};\n  const mockInstance = {\n    functions: [],\n    runningContexts: [null],\n    scan: () => {\n      throw new Error('should not be called');\n    }\n  };\n  const mockAst = { type: 'FunctionDeclaration', body: mockBody };\n  FunctionTracer.prototype.scan.call(mockInstance, mockAst);\n  assert.equal(mockInstance.functions.length, 1);\n  assert.equal(mockInstance.functions[0], mockAst);\n});\n\n\ntest('works with IfStatement', () => {\n  const mockTest = {};\n  const mockConsequent = {};\n  const mockAlternate = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, {\n    type: 'IfStatement',\n    test: mockTest,\n    consequent: mockConsequent,\n    alternate: mockAlternate,\n  });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockTest, mockConsequent, mockAlternate]);\n});\n\ntest('works with ForStatement', () => {\n  const ast = acorn.parse(`for (let i = 0; i < 1; i++) {\n  call();\n}`);\n  const functionTracer = new FunctionTracer(ast.body[0]);\n  assert.equal(functionTracer.declarations[0].name, 'i');\n  assert.equal(functionTracer.contexts.length, 4);\n});\n\ntest('works with DoWhileStatement', () => {\n  const mockBody = {};\n  const mockTest = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    contexts: [],\n    runningContexts: [],\n    newContext: FunctionTracer.prototype.newContext,\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'DoWhileStatement', body: mockBody, test: mockTest });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockBody, mockTest]);\n});\n\ntest('works with WhileStatement', () => {\n  const mockBody = {};\n  const mockTest = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    contexts: [],\n    runningContexts: [],\n    newContext: FunctionTracer.prototype.newContext,\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'WhileStatement', body: mockBody, test: mockTest });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockBody, mockTest]);\n});\n\ntest('works with Identifier', () => {\n  const mockCurrentContext = {};\n  const mockIsState = sinon.spy();\n  const mockGetDeclaration = sinon.spy(() => 123);\n  const mockInstance = {\n    identifiers: [],\n    currentContext: mockCurrentContext,\n    isState: mockIsState,\n    getDeclaration: mockGetDeclaration,\n  };\n  const mockAst = { type: 'Identifier', name: 'x' };\n  FunctionTracer.prototype.scan.call(mockInstance, mockAst);\n  assert.ok(mockGetDeclaration.called);\n  assert.equal(mockGetDeclaration.args[0][0], 'x');\n  assert.deepEqual(mockInstance.identifiers, [\n    {\n      context: mockInstance.currentContext,\n      ast: mockAst,\n      declaration: 123\n    }\n  ]);\n  assert.equal(mockIsState.args[0][0], 'trackIdentifiers');\n});\n\ntest('works with ReturnStatement', () => {\n  const mockArgument = {};\n  let called = false;\n  let calledArgument = null;\n  const mockInstance = {\n    returnStatements: [],\n    scan: (argument) => {\n      called = true;\n      calledArgument = argument;\n    }\n  };\n  const mockAst = { type: 'ReturnStatement', argument: mockArgument };\n  FunctionTracer.prototype.scan.call(mockInstance, mockAst);\n  assert.ok(called);\n  assert.equal(calledArgument, mockArgument);\n  assert.equal(mockInstance.returnStatements[0], mockAst);\n});\n\n\ntest('works with MemberExpression', () => {\n  const mockBody = {};\n  const mockProperty = {};\n  const mockPushState = sinon.spy();\n  const mockPopState = sinon.spy();\n  const mockScan = sinon.spy();\n  const mockInstance = {\n    scan: mockScan,\n    pushState: mockPushState,\n    popState: mockPopState,\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'MemberExpression', object: mockBody, property: mockProperty });\n  assert.ok(mockScan.called);\n  assert.equal(mockScan.args[0][0], mockBody);\n  assert.equal(mockScan.args[1][0], mockProperty);\n  assert.equal(mockPushState.args[0][0], 'memberExpression');\n  assert.equal(mockPopState.args[0][0], 'memberExpression');\n});\n\n\ntest('works with ExpressionStatement', () => {\n  const mockExpression = {};\n  let called = false;\n  let calledExpression = null;\n  const mockInstance = {\n    scan: (body) => {\n      called = true;\n      calledExpression = body;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'ExpressionStatement', expression: mockExpression });\n  assert.ok(called);\n  assert.equal(calledExpression, mockExpression);\n});\n\ntest('works with SequenceExpression', () => {\n  const mockExpression = {};\n  const mockExpressions = [mockExpression];\n  let called = false;\n  let calledExpression = null;\n  const mockInstance = {\n    scan: (body) => {\n      called = true;\n      calledExpression = body;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'SequenceExpression', expressions: mockExpressions });\n  assert.ok(called);\n  assert.equal(calledExpression, mockExpressions);\n});\n\ntest('works with CallExpression', () => {\n  const mockArguments = {};\n  let called = false;\n  let calledArguments = null;\n  const mockCurrentContext = {};\n  const mockInstance = {\n    currentContext: mockCurrentContext,\n    functionCalls: [],\n    scan: (_arguments) => {\n      called = true;\n      calledArguments = _arguments;\n    }\n  };\n  const mockAst = { type: 'CallExpression', arguments: mockArguments };\n  FunctionTracer.prototype.scan.call(mockInstance, mockAst);\n  assert.ok(called);\n  assert.equal(calledArguments, mockArguments);\n  assert.deepEqual(mockInstance.functionCalls, [\n    {\n      context: mockCurrentContext,\n      ast: mockAst\n    }\n  ]);\n});\n\ntest('works with ArrayExpression', () => {\n  const mockElements = {};\n  let called = false;\n  let calledElements = null;\n  const mockInstance = {\n    scan: (elements) => {\n      called = true;\n      calledElements = elements;\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'ArrayExpression', elements: mockElements });\n  assert.ok(called);\n  assert.equal(calledElements, mockElements);\n});\n\ntest('works with ConditionalExpression', () => {\n  const mockTest = {};\n  const mockAlternate = {};\n  const mockConsequent = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'ConditionalExpression', test: mockTest, alternate: mockAlternate, consequent: mockConsequent });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockTest, mockConsequent, mockConsequent]);\n});\n\ntest('works with SwitchStatement', () => {\n  const mockDiscriminant = {};\n  const mockCases = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'SwitchStatement', discriminant: mockDiscriminant, cases: mockCases });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockDiscriminant, mockCases]);\n});\n\ntest('works with SwitchCase', () => {\n  const mockTest = {};\n  const mockConsequent = {};\n  let called = false;\n  let calledArgs = [];\n  const mockInstance = {\n    scan: (arg) => {\n      called = true;\n      calledArgs.push(arg);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, { type: 'SwitchCase', test: mockTest, consequent: mockConsequent });\n  assert.ok(called);\n  assert.deepEqual(calledArgs, [mockTest, mockConsequent]);\n});\n\ntest('does nothing with un-scan-ables', () => {\n  let called = false;\n  const mockInstance = {\n    scan: () => {\n      called = true;\n    }\n  };\n  [\n    'ThisExpression',\n    'Literal',\n    'DebuggerStatement',\n    'EmptyStatement',\n    'BreakStatement',\n    'ContinueStatement'\n  ].forEach(type => {\n    FunctionTracer.prototype.scan.call(mockInstance, { type });\n  });\n  assert.ok(!called);\n});\n\ntest('when called with fake type, throws', () => {\n  assert.throws(() => {\n    FunctionTracer.prototype.scan.call({}, { type: 'Made Up' });\n  });\n});\n\ntest('can handle direct arrays', () => {\n  const mockBlockBody = {};\n  const mockProgramBody = {};\n  const asts = [\n    { type: 'BlockStatement' },\n    { type: 'Program' },\n  ];\n  const calledAsts = [];\n  const mockInstance = {\n    scan: (ast) => {\n      calledAsts.push(ast);\n    }\n  };\n  FunctionTracer.prototype.scan.call(mockInstance, asts);\n  assert.deepEqual(calledAsts, asts);\n});\n"
  },
  {
    "path": "test/internal/gpu-methods.js",
    "content": "const { assert, test, skip, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: GPU methods');\n\ntest('.createKernelMap() object map with settings', () => {\n  const gpu = new GPU();\n  let source = null;\n  let settings = null;\n  function bob() {}\n  function tom() {}\n  class MockKernel {\n    constructor(_source, _settings) {\n      source = _source;\n      settings = _settings;\n      this.context = 'context';\n      this.canvas = 'canvas';\n      this.subKernels = _settings.subKernels;\n    }\n  }\n  gpu.Kernel = MockKernel;\n  const subKernels = {\n    bobResult: bob,\n    tomResult: tom\n  };\n  const kernelSource = function() {};\n  const masterSettings = {};\n  const kernel = gpu.createKernelMap(subKernels, kernelSource, masterSettings);\n  assert.equal(source, kernelSource.toString());\n  assert.notEqual(settings, masterSettings);\n  assert.equal(gpu.canvas, 'canvas');\n  assert.equal(gpu.context, 'context');\n  assert.equal(settings.functions, gpu.functions);\n  assert.equal(settings.nativeFunctions, gpu.nativeFunctions);\n  assert.equal(settings.gpu, gpu);\n  assert.equal(settings.validate, true);\n  assert.deepEqual(kernel.subKernels, [\n    {\n      name: 'bob',\n      source: bob.toString(),\n      property: 'bobResult'\n    },\n    {\n      name: 'tom',\n      source: tom.toString(),\n      property: 'tomResult'\n    }\n  ]);\n});\n\ntest('.createKernelMap() array map with settings', () => {\n  const gpu = new GPU();\n  let source = null;\n  let settings = null;\n  function bob() {}\n  function tom() {}\n  class MockKernel {\n    constructor(_source, _settings) {\n      source = _source;\n      settings = _settings;\n      this.context = 'context';\n      this.canvas = 'canvas';\n      this.subKernels = _settings.subKernels;\n    }\n  }\n  gpu.Kernel = MockKernel;\n  const subKernels = [bob, tom];\n  const kernelSource = function() {};\n  const masterSettings = {};\n  const kernel = gpu.createKernelMap(subKernels, kernelSource, masterSettings);\n  assert.equal(source, kernelSource.toString());\n  assert.notEqual(settings, masterSettings);\n  assert.equal(gpu.canvas, 'canvas');\n  assert.equal(gpu.context, 'context');\n  assert.equal(settings.functions, gpu.functions);\n  assert.equal(settings.nativeFunctions, gpu.nativeFunctions);\n  assert.equal(settings.gpu, gpu);\n  assert.equal(settings.validate, true);\n  assert.deepEqual(kernel.subKernels, [\n    {\n      name: 'bob',\n      source: bob.toString(),\n      property: 0\n    },\n    {\n      name: 'tom',\n      source: tom.toString(),\n      property: 1\n    }\n  ]);\n});\n"
  },
  {
    "path": "test/internal/implied-else.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: Implied else');\n\nfunction neverReachedWhenEarlyReturn(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(check, v1, v2) {\n    if (check) {\n      return v1;\n    }\n    return v2;\n  }, { output: [1] });\n  const result = kernel(true, 123, 321);\n  assert.equal(result[0], 123);\n  gpu.destroy();\n}\n\ntest('never reached when early return auto', () => {\n  neverReachedWhenEarlyReturn();\n});\n\ntest('never reached when early return gpu', () => {\n  neverReachedWhenEarlyReturn('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('never reached when early return webgl', () => {\n  neverReachedWhenEarlyReturn('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('never reached when early return webgl2', () => {\n  neverReachedWhenEarlyReturn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('never reached when early return headlessgl', () => {\n  neverReachedWhenEarlyReturn('headlessgl');\n});\n\ntest('never reached when early return cpu', () => {\n  neverReachedWhenEarlyReturn('cpu');\n});\n\nfunction handlesImpliedElse(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(check, v1, v2) {\n    if (check) {\n      return v1;\n    }\n    return v2;\n  }, { output: [1] });\n  const result = kernel(true, 123, 321);\n  assert.equal(result[0], 123);\n  gpu.destroy();\n}\n\ntest('handles implied else auto', () => {\n  handlesImpliedElse();\n});\n\ntest('handles implied else gpu', () => {\n  handlesImpliedElse('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('handles implied else webgl', () => {\n  handlesImpliedElse('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('handles implied else webgl2', () => {\n  handlesImpliedElse('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('handles implied else headlessgl', () => {\n  handlesImpliedElse('headlessgl');\n});\n\ntest('handles implied else cpu', () => {\n  handlesImpliedElse('cpu');\n});\n"
  },
  {
    "path": "test/internal/kernel-run-shortcut.js",
    "content": "const { assert, test, module: describe, skip } = require('qunit');\nconst sinon = require('sinon');\nconst { GPU } = require('../../src');\n\ndescribe('internal: kernelRunShortcut');\n\nfunction testImmutableSavesSwitchedKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + 1;\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n  });\n  const one = kernel(new Float32Array([0]));\n  const arrayKernel = kernel.kernel;\n  const arrayKernelSpy = sinon.spy(arrayKernel, 'onRequestSwitchKernel');\n\n  // recompile kernel\n  const two = kernel(one);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  const textureKernel = kernel.kernel;\n  const textureKernelSpy = sinon.spy(textureKernel, 'onRequestSwitchKernel');\n  assert.ok(kernel.kernel !== arrayKernel);\n  assert.ok(kernel.kernel === textureKernel);\n\n  // reuse existing kernel a few times, ensure no overwriting\n  const three = kernel(two);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  assert.equal(textureKernelSpy.callCount, 0);\n  assert.ok(kernel.kernel === textureKernel);\n  const four = kernel(three);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  assert.equal(textureKernelSpy.callCount, 0);\n  assert.ok(kernel.kernel === textureKernel);\n  const five = kernel(four);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  assert.equal(textureKernelSpy.callCount, 0);\n  assert.ok(kernel.kernel === textureKernel);\n  const six = kernel(five);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  assert.equal(textureKernelSpy.callCount, 0);\n  assert.ok(kernel.kernel === textureKernel);\n\n  // switch back to original kernel, don't recompile\n  assert.deepEqual(six.toArray(), new Float32Array([6]));\n  const seven = kernel(six.toArray());\n  assert.ok(kernel.kernel === arrayKernel);\n  assert.equal(arrayKernelSpy.callCount, 1);\n  assert.equal(textureKernelSpy.callCount, 1);\n\n  // ensure output has been correct all along\n  assert.deepEqual(one.toArray(), new Float32Array([1]));\n  assert.deepEqual(two.toArray(), new Float32Array([2]));\n  assert.deepEqual(three.toArray(), new Float32Array([3]));\n  assert.deepEqual(four.toArray(), new Float32Array([4]));\n  assert.deepEqual(five.toArray(), new Float32Array([5]));\n  assert.deepEqual(six.toArray(), new Float32Array([6]));\n  assert.deepEqual(seven.toArray(), new Float32Array([7]));\n  gpu.destroy();\n}\n\ntest('immutable saves switched kernel auto', () => {\n  testImmutableSavesSwitchedKernel();\n});\n\ntest('immutable saves switched kernel gpu', () => {\n  testImmutableSavesSwitchedKernel('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable saves switched kernel webgl', () => {\n  testImmutableSavesSwitchedKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable saves switched kernel webgl2', () => {\n  testImmutableSavesSwitchedKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable saves switched kernel headlessgl', () => {\n  testImmutableSavesSwitchedKernel('headlessgl');\n});"
  },
  {
    "path": "test/internal/kernel.js",
    "content": "const { assert, test, module: describe, skip } = require('qunit');\nconst { GPU, CPUKernel, WebGLKernel, WebGL2Kernel, HeadlessGLKernel, Kernel } = require('../../src');\n\ndescribe('internal: kernel');\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction argumentTypesTest(Kernel) {\n  const kernel = new Kernel(`function(value) { return value[this.thread.x]; }`, {\n    output: [1],\n    functionBuilder: {\n      addKernel: function() {},\n      addFunctions: function() {},\n      getPrototypes: function() { return []; },\n      addNativeFunctions: function() {}\n    },\n  });\n  kernel.build([1]);\n  assert.equal(kernel.argumentTypes.length, 1);\n  assert.equal(kernel.argumentTypes[0], 'Array');\n  kernel.destroy();\n}\n\ntest('CPUKernel argumentTypes', () => {\n  argumentTypesTest(CPUKernel);\n});\n\n(GPU.isWebGLSupported ? test : skip)('WebGLKernel argumentTypes', () => {\n  argumentTypesTest(WebGLKernel);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('WebGL2Kernel argumentTypes', () => {\n  argumentTypesTest(WebGL2Kernel);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('HeadlessGLKernel argumentTypes', () => {\n  argumentTypesTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform1fTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform1f: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform1f('test', 1);\n  assert.equal(kernel.uniform1fCache['test'], 1);\n\n  throws = true;\n  kernel.setUniform1f('test', 1);\n  assert.equal(kernel.uniform1fCache['test'], 1);\n\n  throws = false;\n  kernel.setUniform1f('test', 2);\n  assert.equal(kernel.uniform1fCache['test'], 2);\n  kernel.destroy();\n}\n\ntest('WebGLKernel.setUniform1f only calls context when values change', () => {\n  setUniform1fTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform1f only calls context when values change', () => {\n  setUniform1fTest(WebGL2Kernel);\n});\n(GPU.isHeadlessGLSupported ? test : skip)('HeadlessGLKernel.setUniform1f only calls context when values change', () => {\n  setUniform1fTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform1iTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform1i: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform1i('test', 1);\n  assert.equal(kernel.uniform1iCache['test'], 1);\n\n  throws = true;\n  kernel.setUniform1i('test', 1);\n  assert.equal(kernel.uniform1iCache['test'], 1);\n\n  throws = false;\n  kernel.setUniform1i('test', 2);\n  assert.equal(kernel.uniform1iCache['test'], 2);\n  kernel.destroy();\n}\n\ntest('WebGLKernel.setUniform1i only calls context when values change', () => {\n  setUniform1iTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform1i only calls context when values change', () => {\n  setUniform1iTest(WebGL2Kernel);\n});\n(GPU.isHeadlessGLSupported ? test : skip)('HeadlessGLKernel.setUniform1i only calls context when values change', () => {\n  setUniform1iTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform2fTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform2f: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform2f('test', 1, 2);\n  assert.deepEqual(kernel.uniform2fCache['test'], [1, 2]);\n\n  throws = true;\n  kernel.setUniform2f('test', 1, 2);\n  assert.deepEqual(kernel.uniform2fCache['test'], [1, 2]);\n\n  throws = false;\n  kernel.setUniform2f('test', 3, 4);\n  assert.deepEqual(kernel.uniform2fCache['test'], [3, 4]);\n  kernel.destroy();\n}\ntest('WebGLKernel.setUniform2f only calls context when values change', () => {\n  setUniform2fTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform2f only calls context when values change', () => {\n  setUniform2fTest(WebGL2Kernel);\n});\n(GPU.isHeadlessGLSupported ? test : skip)('HeadlessGLKernel.setUniform2f only calls context when values change', () => {\n  setUniform2fTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform2fvTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform2fv: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform2fv('test', [1, 2]);\n  assert.deepEqual(kernel.uniform2fvCache['test'], [1, 2]);\n\n  throws = true;\n  kernel.setUniform2fv('test', [1, 2]);\n  assert.deepEqual(kernel.uniform2fvCache['test'], [1, 2]);\n\n  throws = false;\n  kernel.setUniform2fv('test', [2, 3]);\n  assert.deepEqual(kernel.uniform2fvCache['test'], [2, 3]);\n  kernel.destroy();\n}\ntest('WebGLKernel.setUniform2fv only calls context when values change', () => {\n  setUniform2fvTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform2fv only calls context when values change', () => {\n  setUniform2fvTest(WebGL2Kernel);\n});\ntest('HeadlessGLKernel.setUniform2fv only calls context when values change', () => {\n  setUniform2fvTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform3fvTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform3fv: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform3fv('test', [1, 2, 3]);\n  assert.deepEqual(kernel.uniform3fvCache['test'], [1, 2, 3]);\n\n  throws = true;\n  kernel.setUniform3fv('test', [1, 2, 3]);\n  assert.deepEqual(kernel.uniform3fvCache['test'], [1, 2, 3]);\n\n  throws = false;\n  kernel.setUniform3fv('test', [2, 3, 4]);\n  assert.deepEqual(kernel.uniform3fvCache['test'], [2, 3, 4]);\n  kernel.destroy();\n}\ntest('WebGLKernel.setUniform3fv only calls context when values change', () => {\n  setUniform3fvTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform3fv only calls context when values change', () => {\n  setUniform3fvTest(WebGL2Kernel);\n});\ntest('HeadlessGLKernel.setUniform3fv only calls context when values change', () => {\n  setUniform3fvTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform4ivTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform4iv: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform4iv('test', [1, 2, 3, 4]);\n  assert.deepEqual(kernel.uniform4ivCache['test'], [1, 2, 3, 4]);\n\n  throws = true;\n  kernel.setUniform4iv('test', [1, 2, 3, 4]);\n  assert.deepEqual(kernel.uniform4ivCache['test'], [1, 2, 3, 4]);\n\n  throws = false;\n  kernel.setUniform4iv('test', [2, 3, 4, 5]);\n  assert.deepEqual(kernel.uniform4ivCache['test'], [2, 3, 4, 5]);\n  kernel.destroy();\n}\ntest('WebGLKernel.setUniform4iv only calls context when values change', () => {\n  setUniform4ivTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform4iv only calls context when values change', () => {\n  setUniform4ivTest(WebGL2Kernel);\n});\ntest('HeadlessGLKernel.setUniform4iv only calls context when values change', () => {\n  setUniform4ivTest(HeadlessGLKernel);\n});\n\n/**\n *\n * @param {Kernel} Kernel\n */\nfunction setUniform4fvTest(Kernel) {\n  const canvas = {};\n  const context = {\n    uniform4fv: () => {\n      if (throws) new Error('This should not get called');\n    },\n    getUniformLocation: (name) => {\n      return name;\n    }\n  };\n  const kernel = new Kernel('function() {}', { canvas, context, output: [1] });\n  let throws = false;\n  kernel.setUniform4fv('test', [1, 2, 3, 4]);\n  assert.deepEqual(kernel.uniform4fvCache['test'], [1, 2, 3, 4]);\n\n  throws = true;\n  kernel.setUniform4fv('test', [1, 2, 3, 4]);\n  assert.deepEqual(kernel.uniform4fvCache['test'], [1, 2, 3, 4]);\n\n  throws = false;\n  kernel.setUniform4fv('test', [2, 3, 4, 5]);\n  assert.deepEqual(kernel.uniform4fvCache['test'], [2, 3, 4, 5]);\n  kernel.destroy();\n}\ntest('WebGLKernel.setUniform4fv only calls context when values change', () => {\n  setUniform4fvTest(WebGLKernel);\n});\ntest('WebGL2Kernel.setUniform4fv only calls context when values change', () => {\n  setUniform4fvTest(WebGL2Kernel);\n});\ntest('HeadlessGLKernel.setUniform4fv only calls context when values change', () => {\n  setUniform4fvTest(HeadlessGLKernel);\n});\n\ntest('functionToIFunction with function', () => {\n  const fn = function name() {};\n  const result = Kernel.prototype.functionToIGPUFunction(fn);\n  assert.deepEqual(result, {\n    name: 'name',\n    source: fn.toString(),\n    argumentTypes: [],\n    returnType: null\n  });\n});\n\ntest('functionToIFunction with function and argumentTypes array', () => {\n  const fn = function name(a, b) {};\n  const argumentTypes = ['number','string'];\n  const result = Kernel.prototype.functionToIGPUFunction(fn, { argumentTypes });\n  assert.deepEqual(result, {\n    name: 'name',\n    source: fn.toString(),\n    argumentTypes: ['number', 'string'],\n    returnType: null,\n  });\n});\n\ntest('functionToIFunction with function and argumentTypes object', () => {\n  const fn = function name(a, b) {};\n  const argumentTypes = { a: 'number', b: 'string' };\n  const result = Kernel.prototype.functionToIGPUFunction(fn, { argumentTypes });\n  assert.deepEqual(result, {\n    name: 'name',\n    source: fn.toString(),\n    argumentTypes: ['number', 'string'],\n    returnType: null,\n  });\n});\n\ntest('functionToIGPUFunction with function and returnType', () => {\n  const fn = function name(a, b) {};\n  const result = Kernel.prototype.functionToIGPUFunction(fn, { returnType: 'string' });\n  assert.deepEqual(result, {\n    name: 'name',\n    source: fn.toString(),\n    argumentTypes: [],\n    returnType: 'string',\n  });\n});\n"
  },
  {
    "path": "test/internal/loop-int.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, WebGLFunctionNode, WebGL2FunctionNode } = require('../../src');\n\ndescribe('internal: loop int');\ntest('loop int constant output webgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.max; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGLFunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    constantTypes: {\n      max: 'Integer'\n    },\n    argumentTypes: ['Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nfor (int user_i=0;(user_i<constants_max);user_i++){' +\n    '\\nuser_sum+=get32(user_a, user_aSize, user_aDim, 0, threadId.x, user_i);}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\ntest('loop int constant output webgl2', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.max; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGL2FunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    constantTypes: { max: 'Integer' },\n    argumentTypes: ['Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nfor (int user_i=0;(user_i<constants_max);user_i++){' +\n    '\\nuser_sum+=get32(user_a, user_aSize, user_aDim, 0, threadId.x, user_i);}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\n(GPU.isWebGLSupported ? test : skip)('loop int constant webgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.max; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl' });\n  const output = gpu.createKernel(kernel, {\n    constants: { max: 3 },\n    output: [1]\n  })([[1,2,3]]);\n\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('loop int constant webgl2', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.max; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl2' });\n  const output = gpu.createKernel(kernel, {\n    constants: { max: 3 },\n    output: [1]\n  })([[1,2,3]]);\n\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('loop int constant headlessgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.max; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'headlessgl' });\n  const output = gpu.createKernel(kernel, {\n    constants: { max: 3 },\n    output: [1]\n  })([[1,2,3]]);\n\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\ntest('loop int literal output webgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < 10; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGLFunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    argumentTypes: ['Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nfor (int user_i=0;(user_i<10);user_i++){' +\n    '\\nuser_sum+=get32(user_a, user_aSize, user_aDim, 0, threadId.x, user_i);}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\ntest('loop int literal output webgl2', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < 10; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGL2FunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    argumentTypes: ['Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nfor (int user_i=0;(user_i<10);user_i++){' +\n    '\\nuser_sum+=get32(user_a, user_aSize, user_aDim, 0, threadId.x, user_i);}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\n(GPU.isWebGLSupported ? test : skip)('loop int literal webgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < 3; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl' });\n  const output = gpu.createKernel(kernel, { output: [1] })([[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('loop int literal webgl2', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < 3; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl2' });\n  const output = gpu.createKernel(kernel, { output: [1] })([[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('loop int literal headlessgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < 3; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'headlessgl' });\n  const output = gpu.createKernel(kernel, { output: [1] })([[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\ntest('loop int parameter output webgl', () => {\n  function kernel(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGLFunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    argumentTypes: ['Number', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nint user_i=0;' +\n    '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){' +\n    '\\nif (!(user_i<int(user_a))) break;' +\n    '\\nuser_sum+=get32(user_b, user_bSize, user_bDim, 0, threadId.x, user_i);' +\n    '\\nuser_i++;}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\ntest('loop int parameter output webgl2', () => {\n  function kernel(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }\n  const functionNode = new WebGL2FunctionNode(kernel.toString(), {\n    isRootKernel: true,\n    output: [1],\n    argumentTypes: ['Number', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nint user_i=0;' +\n    '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){' +\n    '\\nif (!(user_i<int(user_a))) break;' +\n    '\\nuser_sum+=get32(user_b, user_bSize, user_bDim, 0, threadId.x, user_i);' +\n    '\\nuser_i++;}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\n(GPU.isWebGLSupported ? test : skip)('loop int parameter webgl', () => {\n  function kernel(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl' });\n  const output = gpu.createKernel(kernel, { output: [1] })(3, [[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('loop int parameter webgl2', () => {\n  function kernel(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl2' });\n  const output = gpu.createKernel(kernel, { output: [1] })(3, [[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('loop int parameter headlessgl', () => {\n  function kernel(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'headlessgl' });\n  const output = gpu.createKernel(kernel, { output: [1] })(3, [[1,2,3]]);\n  assert.equal(\n    output,\n    6\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGLSupported ? test : skip)('loop int dynamic output webgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.output.x; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl' });\n  const output = gpu.createKernel(kernel, {\n    dynamicOutput: true,\n    output: [1],\n  })([[3]]);\n\n  assert.deepEqual(\n    Array.from(output),\n    [3]\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('loop int dynamic output webgl2', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.output.x; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'webgl2' });\n  const output = gpu.createKernel(kernel, {\n    dynamicOutput: true,\n    output: [1]\n  })([[3]]);\n\n  assert.deepEqual(\n    Array.from(output),\n    [3]\n  );\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('loop int dynamic output headlessgl', () => {\n  function kernel(a) {\n    let sum = 0;\n    for (let i = 0; i < this.output.x; i++) {\n      sum += a[this.thread.x][i];\n    }\n    return sum;\n  }\n  const gpu = new GPU({ mode: 'headlessgl' });\n  const output = gpu.createKernel(kernel, {\n    dynamicOutput: true,\n    output: [1],\n  })([[3]]);\n\n  assert.deepEqual(\n    Array.from(output),\n    [3]\n  );\n  gpu.destroy();\n});\n"
  },
  {
    "path": "test/internal/loop-max.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, WebGLFunctionNode, WebGL2FunctionNode } = require('../../src');\n\ndescribe('internal: loop max');\n\ntest('loop max output webgl', () => {\n  const functionNode = new WebGLFunctionNode((function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }).toString(), {\n    isRootKernel: true,\n    name: 'kernel',\n    output: [1],\n    argumentTypes: ['Number', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nint user_i=0;' +\n    '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){' +\n    '\\nif (!(user_i<int(user_a))) break;' +\n    '\\nuser_sum+=get32(user_b, user_bSize, user_bDim, 0, threadId.x, user_i);' +\n    '\\nuser_i++;}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\ntest('loop max output webgl2', () => {\n  const functionNode = new WebGL2FunctionNode((function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }).toString(), {\n    isRootKernel: true,\n    name: 'kernel',\n    output: [1],\n    argumentTypes: ['Number', 'Array'],\n    lookupFunctionArgumentBitRatio: () => 4,\n  });\n\n  assert.equal(\n    functionNode.toString(),\n    'void kernel() {' +\n    '\\nfloat user_sum=0.0;' +\n    '\\nint user_i=0;' +\n    '\\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){' +\n    '\\nif (!(user_i<int(user_a))) break;' +\n    '\\nuser_sum+=get32(user_b, user_bSize, user_bDim, 0, threadId.x, user_i);' +\n    '\\nuser_i++;}' +\n    '\\n' +\n    '\\nkernelResult = user_sum;return;' +\n    '\\n}');\n});\n\n(GPU.isWebGLSupported ? test : skip)('loop max webgl', () => {\n  const gpu = new GPU({mode: 'webgl'});\n  const add = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }).setOutput([1]);\n\n  const output = add(1, [[1]]);\n  assert.equal(\n    output[0],\n    1\n  );\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('loop max webgl2', () => {\n  const gpu = new GPU({mode: 'webgl2'});\n  const add = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  }).setOutput([1]);\n\n  const output = add(1, [[1]]);\n  assert.equal(\n    output[0],\n    1\n  );\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('loop max headlessgl', () => {\n  const gpu = new GPU({ mode: 'headlessgl' });\n  const add = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < a; i++) {\n      sum += b[this.thread.x][i];\n    }\n    return sum;\n  })\n    .setOutput([1]);\n\n  const output = add(1, [[1]]);\n  assert.equal(\n    output[0],\n    1\n  );\n  gpu.destroy();\n});\n\n"
  },
  {
    "path": "test/internal/math.random.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst sinon = require('sinon');\nconst { GPU, plugins: { mathRandom } } = require('../../src');\n\ndescribe('Math.random() unique');\n\nfunction mathRandomUnique(mode) {\n  const gpu = new GPU({ mode });\n  const checkCount = 20;\n  let seed1 = Math.random();\n  let seed2 = Math.random();\n  let stub = sinon.stub(mathRandom, 'onBeforeRun').callsFake((kernel) => {\n    kernel.setUniform1f('randomSeed1', seed1);\n    kernel.setUniform1f('randomSeed2', seed2);\n  });\n  try {\n    gpu.addNativeFunction('getSeed', `highp float getSeed() {\n    return randomSeedShift;\n  }`);\n    const kernel = gpu.createKernel(function () {\n      const v = Math.random();\n      return getSeed();\n    }, {output: [1]});\n    const results = [];\n    for (let i = 0; i < checkCount; i++) {\n      const result = kernel();\n      assert.ok(results.indexOf(result[0]) === -1, `duplication at index ${results.indexOf(result[0])} from new value ${result[0]}.  Values ${JSON.stringify(results)}`);\n      results.push(result[0]);\n      seed2 = result[0];\n      assert.ok(stub.called);\n      stub.restore();\n      stub.callsFake((kernel) => {\n        kernel.setUniform1f('randomSeed1', seed1);\n        kernel.setUniform1f('randomSeed2', seed2);\n      });\n    }\n  } finally {\n    stub.restore();\n    gpu.destroy();\n  }\n}\n\ntest('unique every time auto', () => {\n  mathRandomUnique();\n});\n\ntest('unique every time gpu', () => {\n  mathRandomUnique('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unique every time webgl', () => {\n  mathRandomUnique('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unique every time webgl2', () => {\n  mathRandomUnique('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unique every time headlessgl', () => {\n  mathRandomUnique('headlessgl');\n});\n\ndescribe('never above 1');\n\nfunction mathRandomNeverAboveOne(mode) {\n  const gpu = new GPU({ mode });\n  const checkCount = 20;\n  const checkSource = [];\n\n  for (let i = 0; i < checkCount; i++) {\n    checkSource.push(`const check${ i } = Math.random();`);\n  }\n\n  for (let i = 0; i < checkCount; i++) {\n    for (let j = 0; j < checkCount; j++) {\n      if (i === j) continue;\n      checkSource.push(`if (check${i} >= 1) return 1;`);\n    }\n  }\n\n  const kernel = gpu.createKernel(`function() {\n    ${checkSource.join('\\n')}\n    return 0;\n  }`, { output: [1] });\n\n  const result = kernel();\n  assert.ok(result.every(value => value === 0));\n}\n\ntest('never above 1 every time auto', () => {\n  mathRandomNeverAboveOne();\n});\n\ntest('never above 1 every time gpu', () => {\n  mathRandomNeverAboveOne('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('never above 1 every time webgl', () => {\n  mathRandomNeverAboveOne('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('never above 1 every time webgl2', () => {\n  mathRandomNeverAboveOne('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('never above 1 every time headlessgl', () => {\n  mathRandomNeverAboveOne('headlessgl');\n});\n\ntest('never above 1 every time cpu', () => {\n  mathRandomNeverAboveOne('cpu');\n});\n"
  },
  {
    "path": "test/internal/matrix-multiply-precision.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: matrix multiply precision');\n\nfunction vanillaMatrixMultiply(a, b) {\n  const width = a.length;\n  const height = b.length;\n  const result = new Array(height);\n  for (let y = 0; y < height; y++) {\n    const row = new Float32Array(width);\n    for (let x = 0; x < width; x++) {\n      let sum = 0;\n      for (let i = 0; i < width; i++) {\n        sum += a[y][i] * b[i][x];\n      }\n      row[x] = sum;\n    }\n    result[y] = row;\n  }\n  return result;\n}\n\nfunction filledMatrix(width, height) {\n  const matrix = new Array(height);\n  for (let y = 0; y < height; y++) {\n    const row = matrix[y] = new Float32Array(width);\n    for (let x = 0; x < width; x++) {\n      row[x] = Math.random() * 10;\n    }\n  }\n  return matrix;\n}\n\nfunction test512x512Matrix(precision, mode) {\n  const width = 512;\n  const height = 512;\n  const a = filledMatrix(width, height);\n  const b = filledMatrix(width, height);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.width; i++) {\n      sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n  }, {\n    output: [width, height],\n    precision,\n    constants: {\n      width\n    }\n  });\n  const cpuResult = vanillaMatrixMultiply(a, b, width, height);\n  const gpuResult = kernel(a, b);\n  let closeEnough = true;\n  for (let y = 0; y < height; y++) {\n    for (let x = 0; x < width; x++) {\n      const singleGPUResult = gpuResult[y][x];\n      const singleCPUResult = cpuResult[y][x];\n      if (Math.abs(singleGPUResult - singleCPUResult) > 1) {\n        closeEnough = false;\n        break;\n      }\n    }\n  }\n  assert.ok(closeEnough);\n  gpu.destroy();\n}\n\ntest('512x512 unsigned precision auto', () => {\n  test512x512Matrix('unsigned');\n});\n\ntest('512x512 unsigned precision gpu', () => {\n  test512x512Matrix('unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('512x512 unsigned precision webgl', () => {\n  test512x512Matrix('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('512x512 unsigned precision webgl2', () => {\n  test512x512Matrix('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('512x512 unsigned precision headlessgl', () => {\n  test512x512Matrix('unsigned', 'headlessgl');\n});\n\ntest('512x512 unsigned precision cpu', () => {\n  test512x512Matrix('unsigned', 'cpu');\n});\n\nfunction test10x512Matrix(precision, mode) {\n  const width = 10;\n  const height = 512;\n  const a = filledMatrix(width, height);\n  const b = filledMatrix(width, height);\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a, b) {\n    let sum = 0;\n    for (let i = 0; i < this.constants.width; i++) {\n      sum += a[this.thread.y][i] * b[i][this.thread.x];\n    }\n    return sum;\n  }, {\n    output: [width, height],\n    precision,\n    constants: {\n      width\n    }\n  });\n  const cpuResult = vanillaMatrixMultiply(a, b, width, height);\n  const gpuResult = kernel(a, b);\n  let closeEnough = true;\n  for (let y = 0; y < height; y++) {\n    for (let x = 0; x < width; x++) {\n      const singleGPUResult = gpuResult[y][x];\n      const singleCPUResult = cpuResult[y][x];\n      if (Math.abs(singleGPUResult - singleCPUResult) > 1) {\n        closeEnough = false;\n        break;\n      }\n    }\n  }\n  assert.ok(closeEnough);\n  gpu.destroy();\n}\n\ntest('10x512 unsigned precision auto', () => {\n  test10x512Matrix('unsigned');\n});\n\ntest('10x512 unsigned precision gpu', () => {\n  test10x512Matrix('unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('10x512 unsigned precision webgl', () => {\n  test10x512Matrix('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('10x512 unsigned precision webgl2', () => {\n  test10x512Matrix('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('10x512 unsigned precision headlessgl', () => {\n  test10x512Matrix('unsigned', 'headlessgl');\n});\n\ntest('10x512 unsigned precision cpu', () => {\n  test10x512Matrix('unsigned', 'cpu');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('512x512 single precision auto', () => {\n  test512x512Matrix('single');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('512x512 single precision gpu', () => {\n  test512x512Matrix('single', 'gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('512x512 single precision webgl', () => {\n  test512x512Matrix('single', 'webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('512x512 single precision webgl2', () => {\n  test512x512Matrix('single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('512x512 single precision headlessgl', () => {\n  test512x512Matrix('single', 'headlessgl');\n});\n\ntest('512x512 single precision cpu', () => {\n  test512x512Matrix('single', 'cpu');\n});\n\n\n(GPU.isSinglePrecisionSupported ? test : skip)('10x512 single precision auto', () => {\n  test10x512Matrix('single');\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('10x512 single precision gpu', () => {\n  test10x512Matrix('single', 'gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('10x512 single precision webgl', () => {\n  test10x512Matrix('single', 'webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('10x512 single precision webgl2', () => {\n  test10x512Matrix('single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('10x512 single precision headlessgl', () => {\n  test10x512Matrix('single', 'headlessgl');\n});\n\ntest('10x512 single precision cpu', () => {\n  test10x512Matrix('precision', 'cpu');\n});\n"
  },
  {
    "path": "test/internal/mixed-memory-optimize.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: mixed memory optimize');\n\nfunction getOffKernel(gpu) {\n  return gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }) // getFloatFromSampler2D\n    .setPrecision('single')\n    .setOutput([10])\n    .setPipeline(true)\n    .setOptimizeFloatMemory(false);\n}\n\nfunction getOnKernel(gpu) {\n  return gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }) // getMemoryOptimized32\n    .setPrecision('single')\n    .setOutput([10])\n    .setPipeline(true)\n    .setOptimizeFloatMemory(true);\n}\n\nfunction offOnOff(mode) {\n  const gpu = new GPU({ mode });\n  const offKernel = getOffKernel(gpu);\n  const onKernel = getOnKernel(gpu);\n  const value = [1,2,3,4,5,6,7,8,9,10];\n  const textureResult = offKernel(value);\n  assert.deepEqual(Array.from(textureResult.toArray()), value);\n  assert.deepEqual(Array.from(onKernel(offKernel(value)).toArray()), value);\n  const result = offKernel(onKernel(offKernel(value))).toArray();\n  assert.deepEqual(Array.from(result), value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('off on off auto', () => {\n  offOnOff();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('off on off gpu', () => {\n  offOnOff('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('off on off webgl', () => {\n  offOnOff('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('off on off webgl2', () => {\n  offOnOff('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('off on off headlessgl', () => {\n  offOnOff('headlessgl');\n});\n\ntest('off on off cpu', () => {\n  assert.throws(() => {\n    offOnOff('cpu');\n  });\n});\n\n\nfunction onOffOn(mode) {\n  const gpu = new GPU({ mode });\n  const onKernel = getOnKernel(gpu);\n  const offKernel = getOffKernel(gpu);\n  const value = [1,2,3,4,5,6,7,8,9,10];\n\n  const textureResult1 = onKernel(value);\n  const textureResult2 = offKernel(onKernel(value));\n  const textureResult3 = onKernel(offKernel(onKernel(value)));\n\n  const result1 = Array.from(textureResult1.toArray());\n  const result2 = Array.from(textureResult2.toArray());\n  const result3 = Array.from(textureResult3.toArray());\n\n  assert.deepEqual(Array.from(result1), value);\n  assert.deepEqual(Array.from(result2), value);\n  assert.deepEqual(Array.from(result3), value);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('on off on auto', () => {\n  onOffOn();\n});\n\n(GPU.isGPUSupported && GPU.isSinglePrecisionSupported ? test : skip)('on off on gpu', () => {\n  onOffOn('gpu');\n});\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('on off on webgl', () => {\n  onOffOn('webgl');\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('on off on webgl2', () => {\n  onOffOn('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('on off on headlessgl', () => {\n  onOffOn('headlessgl');\n});\n\ntest('on off on cpu', () => {\n  assert.throws(() => {\n    onOffOn('cpu');\n  });\n});\n"
  },
  {
    "path": "test/internal/modes.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, WebGLKernel, WebGL2Kernel, HeadlessGLKernel, CPUKernel } = require('../../src');\n\ndescribe('internal: modes');\n\ntest('modes no settings auto', () => {\n  const gpu = new GPU();\n  if (GPU.isHeadlessGLSupported) {\n    assert.equal(gpu.Kernel, HeadlessGLKernel);\n  } else if (GPU.isWebGL2Supported) {\n    assert.equal(gpu.Kernel, WebGL2Kernel);\n  } else if (GPU.isWebGLSupported) {\n    assert.equal(gpu.Kernel, WebGLKernel);\n  }\n});\ntest('modes null settings auto', () => {\n  const gpu = new GPU(null);\n  if (GPU.isHeadlessGLSupported) {\n    assert.equal(gpu.Kernel, HeadlessGLKernel);\n  } else if (GPU.isWebGL2Supported) {\n    assert.equal(gpu.Kernel, WebGL2Kernel);\n  } else if (GPU.isWebGLSupported) {\n    assert.equal(gpu.Kernel, WebGLKernel);\n  }\n});\ntest('modes empty object auto', () => {\n  const gpu = new GPU({});\n  if (GPU.isHeadlessGLSupported) {\n    assert.equal(gpu.Kernel, HeadlessGLKernel);\n  } else if (GPU.isWebGL2Supported) {\n    assert.equal(gpu.Kernel, WebGL2Kernel);\n  } else if (GPU.isWebGLSupported) {\n    assert.equal(gpu.Kernel, WebGLKernel);\n  }\n});\ntest('modes gpu', () => {\n  const gpu = new GPU({ mode: 'gpu' });\n  if (GPU.isHeadlessGLSupported) {\n    assert.equal(gpu.Kernel, HeadlessGLKernel);\n  } else if (GPU.isWebGL2Supported) {\n    assert.equal(gpu.Kernel, WebGL2Kernel);\n  } else if (GPU.isWebGLSupported) {\n    assert.equal(gpu.Kernel, WebGLKernel);\n  }\n});\ntest('modes cpu', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  assert.equal(gpu.Kernel, CPUKernel);\n});\n(GPU.isWebGLSupported ? test : skip)('modes webgl', () => {\n  const gpu = new GPU({ mode: 'webgl' });\n  assert.equal(gpu.Kernel, WebGLKernel);\n});\n(GPU.isWebGL2Supported ? test : skip)('modes webgl2', () => {\n  const gpu = new GPU({ mode: 'webgl2' });\n  assert.equal(gpu.Kernel, WebGL2Kernel);\n});\n(GPU.isHeadlessGLSupported ? test : skip)('modes headlessgl', () => {\n  const gpu = new GPU({ mode: 'headlessgl' });\n  assert.equal(gpu.Kernel, HeadlessGLKernel\n  );\n});\n"
  },
  {
    "path": "test/internal/overloading.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: overloading');\n// TODO: planned for after v2, overload generated functions so as to cut down on casting\n// TODO: Complain with incompatible signatures\n// TODO: Cast actual return type to addFunction's returnType when they do not match.\n// TODO: Look into\ntest('with Han', () => {\n  const gpu = new GPU();\n  gpu.addFunction(function dbl(v) {\n    return v + v;\n  }, { returnType: \"Float\", argumentTypes: { v: \"Float\" } });\n  try {\n    const kernel = gpu.createKernel(function(v) {\n      // const output2 = dbl(2);\n      let sum = 0;\n      for (let i = 0; i < 1; i++) {\n        dbl(i);\n      }\n      // const output1\n      dbl(Math.PI);\n      return sum;\n    }, { output: [1] });\n  } finally {\n    gpu.destroy();\n  }\n  assert.ok(1);\n});\n"
  },
  {
    "path": "test/internal/precision.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: precision');\n\n(GPU.isWebGLSupported ? test : skip)('WebGL Decimal Precision', () => {\n  const gpu = new GPU({mode: 'webgl'});\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  }).setOutput([1]);\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n\n  const reflectValue = gpu.createKernel(function(a) {\n    return a;\n  }).setOutput([1]);\n\n  //Just for sanity's sake, recurse the value to see if it spirals out of control\n  for (let i = 0; i < 100; i++) {\n    const newAddResult = reflectValue(addResult)[0];\n    assert.equal(newAddResult, addResult);\n    addResult = newAddResult;\n  }\n  gpu.destroy();\n});\n\n(GPU.isWebGL2Supported ? test : skip)('WebGL2 Decimal Precision', () => {\n  const gpu = new GPU({mode: 'webgl2'});\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  }).setOutput([1]);\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n\n  const reflectValue = gpu.createKernel(function(a) {\n    return a;\n  }).setOutput([1]);\n\n  //Just for sanity's sake, recurse the value to see if it spirals out of control\n  for (let i = 0; i < 100; i++) {\n    const newAddResult = reflectValue(addResult)[0];\n    assert.equal(newAddResult, addResult);\n    addResult = newAddResult;\n  }\n  gpu.destroy();\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('HeadlessGL Decimal Precision', () => {\n  const gpu = new GPU({mode: 'headlessgl'});\n  const add = gpu.createKernel(function(a, b) {\n    return a + b;\n  }).setOutput([1]);\n  let addResult = add(0.1, 0.2)[0];\n  assert.equal(addResult.toFixed(7), (0.1 + 0.2).toFixed(7));\n\n  const reflectValue = gpu.createKernel(function(a) {\n    return a;\n  }).setOutput([1]);\n\n  //Just for sanity's sake, recurse the value to see if it spirals out of control\n  for (let i = 0; i < 100; i++) {\n    const newAddResult = reflectValue(addResult)[0];\n    assert.equal(newAddResult, addResult);\n    addResult = newAddResult;\n  }\n  gpu.destroy();\n});\n"
  },
  {
    "path": "test/internal/recycling.js",
    "content": "const sinon = require('sinon');\nconst { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: recycling');\n\nfunction testImmutableKernelTextureRecycling(precision, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(v) {\n    return v[0] + 1;\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n    precision,\n  });\n  let result = kernel([0]);\n  const newTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'newTexture');\n  for (let i = 0; i < 10; i++) {\n    let lastResult = result;\n    result = kernel(result);\n    lastResult.delete();\n  }\n  assert.deepEqual(result.toArray(), new Float32Array([11]));\n  assert.equal(newTextureSpy.callCount, 1);\n  assert.equal(gpu.kernels.length, 2);\n  newTextureSpy.restore();\n  gpu.destroy();\n}\n\ntest('immutable single precision kernel auto', () => {\n  testImmutableKernelTextureRecycling('single')\n});\n\ntest('immutable single precision kernel gpu', () => {\n  testImmutableKernelTextureRecycling('single', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel webgl', () => {\n  testImmutableKernelTextureRecycling('single', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel webgl2', () => {\n  testImmutableKernelTextureRecycling('single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel headlessgl', () => {\n  testImmutableKernelTextureRecycling('single', 'headlessgl');\n});\n\ntest('immutable unsigned precision kernel auto', () => {\n  testImmutableKernelTextureRecycling('unsigned')\n});\n\ntest('immutable unsigned precision kernel gpu', () => {\n  testImmutableKernelTextureRecycling('unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel webgl', () => {\n  testImmutableKernelTextureRecycling('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel webgl2', () => {\n  testImmutableKernelTextureRecycling('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision headlessgl', () => {\n  testImmutableKernelTextureRecycling('unsigned', 'headlessgl');\n});\n\nfunction testImmutableMappedKernelTextureRecycling(precision, mode) {\n  const gpu = new GPU({ mode });\n  function oneOff(value) {\n    return value;\n  }\n  const kernel = gpu.createKernelMap({\n    oneOffValue: oneOff\n  },function(value1, value2) {\n    oneOff(value2[0] - 1);\n    return value1[0] + 1;\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n    precision,\n  });\n  let map = kernel([0], [11]);\n  const newTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'newTexture');\n  for (let i = 0; i < 10; i++) {\n    let lastResults = map;\n    map = kernel(map.result, map.oneOffValue);\n    lastResults.result.delete();\n    lastResults.oneOffValue.delete();\n  }\n  assert.deepEqual(map.result.toArray(), new Float32Array([11]));\n  assert.deepEqual(map.oneOffValue.toArray(), new Float32Array([0]));\n  assert.equal(newTextureSpy.callCount, 2);\n  assert.equal(gpu.kernels.length, 2);\n  newTextureSpy.restore();\n  gpu.destroy();\n}\n\ntest('immutable single precision mapped kernel auto', () => {\n  testImmutableMappedKernelTextureRecycling('single')\n});\n\ntest('immutable single precision mapped kernel gpu', () => {\n  testImmutableMappedKernelTextureRecycling('single', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable single precision mapped kernel webgl', () => {\n  testImmutableMappedKernelTextureRecycling('single', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable single precision mapped kernel webgl2', () => {\n  testImmutableMappedKernelTextureRecycling('single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision mapped kernel headlessgl', () => {\n  testImmutableMappedKernelTextureRecycling('single', 'headlessgl');\n});\n\ntest('immutable unsigned precision mapped kernel auto', () => {\n  testImmutableMappedKernelTextureRecycling('unsigned')\n});\n\ntest('immutable unsigned precision mapped kernel gpu', () => {\n  testImmutableMappedKernelTextureRecycling('unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision mapped kernel webgl', () => {\n  testImmutableMappedKernelTextureRecycling('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision mapped kernel webgl2', () => {\n  testImmutableMappedKernelTextureRecycling('unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision mapped kernel headlessgl', () => {\n  testImmutableMappedKernelTextureRecycling('unsigned', 'headlessgl');\n});\n\nfunction testImmutableTextureDelete(precision, done, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.thread.x;\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n    precision,\n  });\n  const result = kernel();\n  assert.equal(result.texture._refs, 2);\n  const clone1 = result.clone();\n  assert.equal(result.texture._refs, 3);\n  const clone2 = result.clone();\n  assert.equal(result.texture._refs, 4);\n  const clone3 = result.clone();\n  assert.equal(result.texture._refs, 5);\n  const clone4 = result.clone();\n  assert.equal(result.texture._refs, 6);\n  const clone5 = result.clone();\n  assert.equal(result.texture._refs, 7);\n\n  clone1.delete();\n  assert.equal(result.texture._refs, 6);\n  clone2.delete();\n  assert.equal(result.texture._refs, 5);\n  clone3.delete();\n  assert.equal(result.texture._refs, 4);\n  clone4.delete();\n  assert.equal(result.texture._refs, 3);\n  clone5.delete();\n  assert.equal(result.texture._refs, 2);\n  result.delete();\n  assert.equal(result.texture._refs, 1);\n  const spy = sinon.spy(kernel.kernel.context, 'deleteTexture');\n  gpu.destroy()\n    .then(() => {\n      assert.equal(result.texture._refs, 0);\n      assert.equal(spy.callCount, 1);\n      assert.ok(spy.calledWith(result.texture));\n      spy.restore();\n      done();\n    });\n}\n\ntest('immutable single precision texture delete auto', t => {\n  testImmutableTextureDelete('single', t.async());\n});\n\ntest('immutable single precision texture delete gpu', t => {\n  testImmutableTextureDelete('single', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable single precision texture delete webgl', t => {\n  testImmutableTextureDelete('single', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable single precision texture delete webgl2', t => {\n  testImmutableTextureDelete('single', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision texture delete headlessgl', t => {\n  testImmutableTextureDelete('single', t.async(), 'headlessgl');\n});\n\ntest('immutable unsigned precision texture delete auto', t => {\n  testImmutableTextureDelete('unsigned', t.async() );\n});\n\ntest('immutable unsigned precision texture delete gpu', t => {\n  testImmutableTextureDelete('unsigned', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision texture delete webgl', t => {\n  testImmutableTextureDelete('unsigned', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision texture delete webgl2', t => {\n  testImmutableTextureDelete('unsigned', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision texture delete headlessgl', t => {\n  testImmutableTextureDelete('unsigned', t.async(), 'headlessgl');\n});\n\nfunction testImmutableKernelTextureDoesNotLeak(precision, done, mode) {\n  const gpu = new GPU({ mode });\n  const toTexture = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n    precision,\n  });\n  const one = toTexture([1]);\n  assert.equal(one.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.texture\n  assert.equal(toTexture.texture.texture, one.texture); // very important, a clone was mode, but not a deep clone\n  assert.notEqual(one, toTexture.texture);\n  const two = toTexture([2]);\n  assert.equal(one.texture._refs, 1); // was tracked on toTexture.texture, and deleted\n  assert.equal(toTexture.texture.texture, two.texture);\n  assert.notEqual(toTexture.texture.texture, one.texture);\n  assert.equal(two.texture._refs, 2);\n  one.delete();\n  two.delete();\n  assert.equal(one.texture._refs, 0);\n  assert.equal(two.texture._refs, 1); // still used by toTexture.texture\n  two.delete(); // already deleted\n  assert.equal(two.texture._refs, 1); // still used by toTexture\n  gpu.destroy()\n    .then(() => {\n      assert.equal(two.texture._refs, 0);\n      done();\n    });\n}\n\ntest('immutable unsigned precision kernel.texture does not leak auto', t => {\n  testImmutableKernelTextureDoesNotLeak('unsigned', t.async());\n});\n\ntest('immutable unsigned precision kernel.texture does not leak gpu', t => {\n  testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel.texture does not leak webgl', t => {\n  testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel.texture does not leak webgl2', t => {\n  testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision kernel.texture does not leak headlessgl', t => {\n  testImmutableKernelTextureDoesNotLeak('unsigned', t.async(), 'headlessgl');\n});\n\ntest('immutable single precision kernel.texture does not leak auto', t => {\n  testImmutableKernelTextureDoesNotLeak('single', t.async());\n});\n\ntest('immutable single precision kernel.texture does not leak gpu', t => {\n  testImmutableKernelTextureDoesNotLeak('single', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel.texture does not leak webgl', t => {\n  testImmutableKernelTextureDoesNotLeak('single', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel.texture does not leak webgl2', t => {\n  testImmutableKernelTextureDoesNotLeak('single', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel.texture does not leak headlessgl', t => {\n  testImmutableKernelTextureDoesNotLeak('single', t.async(), 'headlessgl');\n});\n\nfunction testImmutableKernelMappedTexturesDoesNotLeak(precision, done, mode) {\n  const gpu = new GPU({ mode });\n  function saveValue(value) {\n    return value;\n  }\n  const toTextures = gpu.createKernelMap([saveValue],function(value1, value2) {\n    saveValue(value1[this.thread.x]);\n    return value2[this.thread.x];\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n    precision,\n  });\n  const { result: one, 0: two } = toTextures([1], [2]);\n  assert.equal(one.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.texture\n  assert.equal(two.texture._refs, 2); // one's texture will be used in two places at first, in one and toTexture.mappedTextures[0]\n  assert.equal(toTextures.texture.texture, one.texture); // very important, a clone was mode, but not a deep clone\n  assert.equal(toTextures.mappedTextures[0].texture, two.texture); // very important, a clone was mode, but not a deep clone\n  assert.notEqual(one, toTextures.texture);\n  assert.notEqual(one, toTextures.mappedTextures[0]);\n  const { result: three, 0: four } = toTextures([3], [4]);\n  assert.equal(one.texture._refs, 1); // was tracked on toTexture.texture, and deleted\n  assert.equal(two.texture._refs, 1); // was tracked on toTexture.mappedTextures[0], and deleted\n  assert.equal(toTextures.texture.texture, three.texture);\n  assert.equal(toTextures.mappedTextures[0].texture, four.texture);\n  assert.notEqual(toTextures.texture.texture, one.texture);\n  assert.notEqual(toTextures.mappedTextures[0].texture, two.texture);\n  assert.equal(three.texture._refs, 2);\n  assert.equal(four.texture._refs, 2);\n  one.delete();\n  two.delete();\n  three.delete();\n  four.delete();\n  assert.equal(one.texture._refs, 0);\n  assert.equal(two.texture._refs, 0);\n  assert.equal(three.texture._refs, 1); // still used by toTexture.texture\n  assert.equal(four.texture._refs, 1); // still used by toTexture.mappedTextures[0]\n  three.delete(); // already deleted\n  four.delete(); // already deleted\n  assert.equal(three.texture._refs, 1); // still used by toTexture\n  assert.equal(four.texture._refs, 1); // still used by toTexture\n  gpu.destroy()\n    .then(() => {\n      assert.equal(one.texture._refs, 0);\n      assert.equal(two.texture._refs, 0);\n      assert.equal(three.texture._refs, 0);\n      assert.equal(four.texture._refs, 0);\n      done();\n    });\n}\n\ntest('immutable unsigned precision kernel.mappedTextures does not leak auto', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async());\n});\n\ntest('immutable unsigned precision kernel.mappedTextures does not leak gpu', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak webgl', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak webgl2', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable unsigned precision kernel.mappedTextures does not leak headlessgl', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('unsigned', t.async(), 'headlessgl');\n});\n\ntest('immutable single precision kernel.mappedTextures does not leak auto', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('single', t.async());\n});\n\ntest('immutable single precision kernel.mappedTextures does not leak gpu', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('immutable single precision kernel.mappedTextures does not leak webgl', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('immutable single precision kernel.mappedTextures does not leak webgl2', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('immutable single precision kernel.mappedTextures does not leak headlessgl', t => {\n  testImmutableKernelMappedTexturesDoesNotLeak('single', t.async(), 'headlessgl');\n});\n\nfunction testCloning(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[0] + 1;\n  }, { output: [1], pipeline: true });\n  const texture = kernel([1]);\n  const { size } = texture;\n\n  // set size to something unique, for tracking\n  texture.size = [size[0] + 0.1, size[1] + 0.2];\n  texture.cloneTexture();\n  assert.equal(texture._framebuffer.width, size[0] + 0.1);\n  assert.equal(texture._framebuffer.height, size[1] + 0.2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('cloning sets up framebuffer with correct size webgl', () => {\n  testCloning('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('cloning sets up framebuffer with correct size webgl2', () => {\n  testCloning('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('cloning sets up framebuffer with correct size headlessgl', () => {\n  testCloning('headlessgl');\n});\n\nfunction testMutableLeak(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, {\n    output: [1],\n    pipeline: true\n  });\n  kernel.build();\n  const cloneTextureSpy = sinon.spy(kernel.texture.constructor.prototype, 'beforeMutate');\n  const texture1 = kernel();\n  const texture2 = kernel();\n  assert.equal(cloneTextureSpy.callCount, 0);\n  assert.equal(texture1.texture._refs, 1);\n  assert.ok(texture1 === texture2);\n  cloneTextureSpy.restore();\n  gpu.destroy();\n}\n\ntest('test mutable leak auto', () => {\n  testMutableLeak();\n});\n\ntest('test mutable leak gpu', () => {\n  testMutableLeak('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('test mutable leak webgl', () => {\n  testMutableLeak('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('test mutable leak webgl2', () => {\n  testMutableLeak('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('test mutable leak headlessgl', () => {\n  testMutableLeak('headlessgl');\n});\n\ndescribe('internal: cpu recycling behaviour');\n\ntest('recycle CPU array', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return this.thread.x + v[0];\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: false,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0], 1);\n  const result2 = kernel(new Float32Array([2]));\n\n  assert.equal(result1[0], 2);\n  assert.equal(result1, result2);\n  gpu.destroy();\n});\n\ntest('recycle CPU matrix', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return (this.thread.x + (this.thread.y * this.output.x)) + v[0];\n  }, {\n    output: [2, 2],\n    pipeline: true,\n    immutable: false,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0][0], 1);\n  assert.equal(result1[0][1], 2);\n  assert.equal(result1[1][0], 3);\n  assert.equal(result1[1][1], 4);\n  const result2 = kernel(new Float32Array([2]));\n  assert.equal(result1[0][0], 2);\n  assert.equal(result1[0][1], 3);\n  assert.equal(result1[1][0], 4);\n  assert.equal(result1[1][1], 5);\n\n  assert.equal(result1, result2);\n  gpu.destroy();\n});\n\ntest('recycle CPU cube', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return (this.thread.x + (this.thread.y * this.output.x) + (this.thread.z * this.output.y * this.output.x)) + v[0];\n  }, {\n    output: [2, 2, 2],\n    pipeline: true,\n    immutable: false,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0][0][0], 1);\n  assert.equal(result1[0][0][1], 2);\n  assert.equal(result1[0][1][0], 3);\n  assert.equal(result1[0][1][1], 4);\n  assert.equal(result1[1][0][0], 5);\n  assert.equal(result1[1][0][1], 6);\n  assert.equal(result1[1][1][0], 7);\n  assert.equal(result1[1][1][1], 8);\n  const result2 = kernel(new Float32Array([2]));\n  assert.equal(result1[0][0][0], 2);\n  assert.equal(result1[0][0][1], 3);\n  assert.equal(result1[0][1][0], 4);\n  assert.equal(result1[0][1][1], 5);\n  assert.equal(result1[1][0][0], 6);\n  assert.equal(result1[1][0][1], 7);\n  assert.equal(result1[1][1][0], 8);\n  assert.equal(result1[1][1][1], 9);\n  assert.equal(result1, result2);\n  gpu.destroy();\n});\n\ndescribe('internal: cpu non-recycling behaviour');\n\ntest('non-recycle CPU array', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return this.thread.x + v[0];\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: true,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0], 1);\n  const result2 = kernel(new Float32Array([2]));\n\n  assert.equal(result1[0], 1);\n  assert.equal(result2[0], 2);\n  assert.notEqual(result1, result2);\n  gpu.destroy();\n});\n\ntest('non-recycle CPU matrix', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return (this.thread.x + (this.thread.y * this.output.x)) + v[0];\n  }, {\n    output: [2, 2],\n    pipeline: true,\n    immutable: true,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0][0], 1);\n  assert.equal(result1[0][1], 2);\n  assert.equal(result1[1][0], 3);\n  assert.equal(result1[1][1], 4);\n  const result2 = kernel(new Float32Array([2]));\n  // untouched\n  assert.equal(result1[0][0], 1);\n  assert.equal(result1[0][1], 2);\n  assert.equal(result1[1][0], 3);\n  assert.equal(result1[1][1], 4);\n\n  assert.equal(result2[0][0], 2);\n  assert.equal(result2[0][1], 3);\n  assert.equal(result2[1][0], 4);\n  assert.equal(result2[1][1], 5);\n\n  assert.notEqual(result1, result2);\n  gpu.destroy();\n});\n\ntest('non-recycle CPU cube', () => {\n  const gpu = new GPU({ mode: 'cpu' });\n  const kernel = gpu.createKernel(function(v) {\n    return (this.thread.x + (this.thread.y * this.output.x) + (this.thread.z * this.output.y * this.output.x)) + v[0];\n  }, {\n    output: [2, 2, 2],\n    pipeline: true,\n    immutable: true,\n  });\n  const result1 = kernel(new Float32Array([1]));\n  assert.equal(result1[0][0][0], 1);\n  assert.equal(result1[0][0][1], 2);\n  assert.equal(result1[0][1][0], 3);\n  assert.equal(result1[0][1][1], 4);\n  assert.equal(result1[1][0][0], 5);\n  assert.equal(result1[1][0][1], 6);\n  assert.equal(result1[1][1][0], 7);\n  assert.equal(result1[1][1][1], 8);\n  const result2 = kernel(new Float32Array([2]));\n  // untouched\n  assert.equal(result1[0][0][0], 1);\n  assert.equal(result1[0][0][1], 2);\n  assert.equal(result1[0][1][0], 3);\n  assert.equal(result1[0][1][1], 4);\n  assert.equal(result1[1][0][0], 5);\n  assert.equal(result1[1][0][1], 6);\n  assert.equal(result1[1][1][0], 7);\n  assert.equal(result1[1][1][1], 8);\n\n  assert.equal(result2[0][0][0], 2);\n  assert.equal(result2[0][0][1], 3);\n  assert.equal(result2[0][1][0], 4);\n  assert.equal(result2[0][1][1], 5);\n  assert.equal(result2[1][0][0], 6);\n  assert.equal(result2[1][0][1], 7);\n  assert.equal(result2[1][1][0], 8);\n  assert.equal(result2[1][1][1], 9);\n  assert.notEqual(result1, result2);\n  gpu.destroy();\n});\n\nfunction testSameSourceDestinationFromResultThrows(error, precision, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x] + 1;\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: false,\n    precision,\n  });\n  let result = kernel([0]);\n  assert.equal((result.toArray ? result.toArray() : result)[0], 1);\n  assert.throws(() => kernel(result), error);\n  gpu.destroy();\n}\n\nconst gpuError = new Error('Source and destination textures are the same.  Use immutable = true and manually cleanup kernel output texture memory with texture.delete()');\nconst cpuError = new Error('Source and destination arrays are the same.  Use immutable = true');\n\ntest('single precision same source and destination from result mutable throws auto', () => {\n  testSameSourceDestinationFromResultThrows(gpuError,'single');\n});\n\ntest('single precision same source and destination from result mutable throws gpu', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'single', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('single precision same source and destination from result mutable throws webgl', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'single', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('single precision same source and destination from result mutable throws webgl2', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('single precision same source and destination from result mutable throws headlessgl', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'single', 'headlessgl');\n});\n\ntest('single precision same source and destination from result mutable throws cpu', () => {\n  testSameSourceDestinationFromResultThrows(cpuError, 'single', 'cpu');\n});\n\ntest('unsigned precision same source and destination from result mutable throws auto', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'unsigned');\n});\n\ntest('unsigned precision same source and destination from result mutable throws gpu', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision same source and destination from result mutable throws webgl', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision same source and destination from result mutable throws webgl2', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision same source and destination from result mutable throws headlessgl', () => {\n  testSameSourceDestinationFromResultThrows(gpuError, 'unsigned', 'headlessgl');\n});\n\ntest('unsigned precision same source and destination from result mutable throws cpu', () => {\n  testSameSourceDestinationFromResultThrows(cpuError, 'unsigned', 'cpu');\n});\n\nfunction testSameSourceDestinationFromMappedResultThrows(error, precision, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap({\n    mappedResult: function map(v) {\n      return v;\n    }\n  }, function(value) {\n    return map(value[this.thread.x] + 1);\n  }, {\n    output: [1],\n    pipeline: true,\n    immutable: false,\n    precision,\n  });\n  let { result, mappedResult } = kernel([0]);\n  assert.equal((mappedResult.toArray ? mappedResult.toArray() : mappedResult)[0], 1);\n  assert.throws(() => kernel(mappedResult), error);\n  assert.throws(() => kernel(result), error);\n  gpu.destroy();\n}\n\ntest('single precision same source and destination from mapped result mutable throws auto', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'single');\n});\n\ntest('single precision same source and destination from mapped result mutable throws gpu', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('single precision same source and destination from mapped result mutable throws webgl', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('single precision same source and destination from mapped result mutable throws webgl2', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('single precision same source and destination from mapped result mutable throws headlessgl', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'single', 'headlessgl');\n});\n\ntest('single precision same source and destination from mapped result mutable throws cpu', () => {\n  testSameSourceDestinationFromMappedResultThrows(cpuError, 'single', 'cpu');\n});\n\ntest('unsigned precision same source and destination from mapped result mutable throws auto', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned');\n});\n\ntest('unsigned precision same source and destination from mapped result mutable throws gpu', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws webgl', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws webgl2', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('unsigned precision same source and destination from mapped result mutable throws headlessgl', () => {\n  testSameSourceDestinationFromMappedResultThrows(gpuError, 'unsigned', 'headlessgl');\n});\n\ntest('unsigned precision same source and destination from mapped result mutable throws cpu', () => {\n  testSameSourceDestinationFromMappedResultThrows(cpuError, 'unsigned', 'cpu');\n});\n\nfunction testOutputTextureIsClonedWhenRecompiling(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x] + 1;\n  }, { output: [1], immutable: true, pipeline: true });\n  const result1 = kernel([1]);\n  assert.equal(result1.toArray()[0], 2);\n  const result2 = kernel(result1);\n  result1.delete();\n  assert.equal(result2.toArray()[0], 3);\n  result2.delete();\n  const result3 = kernel([3]);\n  assert.equal(result3.toArray()[0], 4);\n  const result4 = kernel(result3);\n  result3.delete();\n  assert.equal(result4.toArray()[0], 5);\n  result4.delete();\n  gpu.destroy();\n}\n\ntest('output texture is cloned when recompiling auto', () => {\n  testOutputTextureIsClonedWhenRecompiling();\n});\n\ntest('output texture is cloned when recompiling gpu', () => {\n  testOutputTextureIsClonedWhenRecompiling('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('output texture is cloned when recompiling webgl', () => {\n  testOutputTextureIsClonedWhenRecompiling('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('output texture is cloned when recompiling webgl2', () => {\n  testOutputTextureIsClonedWhenRecompiling('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('output texture is cloned when recompiling headlessgl', () => {\n  testOutputTextureIsClonedWhenRecompiling('headlessgl');\n});\n\nfunction testMappedOutputTextureIsClonedWhenRecompiling(mode) {\n  const gpu = new GPU({ mode });\n  function setValue(value) {\n    return value * 10;\n  }\n  const kernel = gpu.createKernelMap({\n    value: setValue,\n  },function(value1, value2) {\n    setValue(value2[this.thread.x]);\n    return value1[this.thread.x] + 1;\n  }, { output: [1], immutable: true, pipeline: true });\n  const map1 = kernel([1], [1]);\n  assert.equal(map1.result.toArray()[0], 2);\n  assert.equal(map1.value.toArray()[0], 10);\n  const map2 = kernel(map1.result, map1.value);\n  map1.result.delete();\n  map1.value.delete();\n  assert.equal(map2.result.toArray()[0], 3);\n  assert.equal(map2.value.toArray()[0], 100);\n  map2.value.delete();\n  map2.result.delete();\n  const map3 = kernel([3], [3]);\n  assert.equal(map3.result.toArray()[0], 4);\n  assert.equal(map3.value.toArray()[0], 30);\n  const map4 = kernel(map3.result, map3.value);\n  map3.result.delete();\n  map3.value.delete();\n  assert.equal(map4.result.toArray()[0], 5);\n  assert.equal(map4.value.toArray()[0], 300);\n  map4.result.delete();\n  map4.value.delete();\n  gpu.destroy();\n}\n\ntest('mapped output texture is cloned when recompiling auto', () => {\n  testMappedOutputTextureIsClonedWhenRecompiling();\n});\n\ntest('mapped output texture is cloned when recompiling gpu', () => {\n  testMappedOutputTextureIsClonedWhenRecompiling('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('mapped output texture is cloned when recompiling webgl', () => {\n  testMappedOutputTextureIsClonedWhenRecompiling('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('mapped output texture is cloned when recompiling webgl2', () => {\n  testMappedOutputTextureIsClonedWhenRecompiling('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('mapped output texture is cloned when recompiling headlessgl', () => {\n  testMappedOutputTextureIsClonedWhenRecompiling('headlessgl');\n});"
  },
  {
    "path": "test/internal/texture-index.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: texture index');\nfunction createKernelWithNumberConstants(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.constants.v1 + this.constants.v2;\n  }, { output: [1], constants: { v1: 1, v2: 1 } });\n\n  kernel();\n  assert.equal(kernel.kernelConstants.length, 2);\n  assert.equal(kernel.kernelConstants[0].contextHandle, null);\n  assert.equal(kernel.kernelConstants[1].contextHandle, null);\n\n  assert.equal(kernel.kernelArguments.length, 0);\n\n  gpu.destroy();\n}\n\ntest('createKernel with number constants auto', () => {\n  createKernelWithNumberConstants();\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernel with number constants gpu', () => {\n  createKernelWithNumberConstants('gpu');\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernel with number constants webgl', () => {\n  createKernelWithNumberConstants('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernel with number constants webgl2', () => {\n  createKernelWithNumberConstants('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('createKernel with number constants headlessgl', () => {\n  createKernelWithNumberConstants('headlessgl');\n});\n\n\nfunction createKernelWithArrayConstants(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.constants.v1[this.thread.x] + this.constants.v2[this.thread.x];\n  }, { output: [1], constants: { v1: [1], v2: [1] } });\n\n  kernel();\n  const gl = kernel.context;\n  assert.equal(kernel.kernelConstants.length, 2);\n  assert.equal(kernel.kernelConstants[0].contextHandle, gl.TEXTURE0);\n  assert.equal(kernel.kernelConstants[1].contextHandle, gl.TEXTURE0 + 1);\n\n  assert.equal(kernel.kernelArguments.length, 0);\n\n  gpu.destroy();\n}\ntest('createKernel with array constants auto', () => {\n  createKernelWithArrayConstants();\n});\n(GPU.isGPUSupported ? test : skip)('createKernel with array constants gpu', () => {\n  createKernelWithArrayConstants('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('createKernel with array constants webgl', () => {\n  createKernelWithArrayConstants('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernel with array constants webgl2', () => {\n  createKernelWithArrayConstants('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('createKernel with array constants headlessgl', () => {\n  createKernelWithArrayConstants('headlessgl');\n});\n\nfunction creatKernelWithNumberConstantsAndArrayArguments(mode) {\n  const gpu = new GPU({ mode });\n  const textureGetter = gpu.createKernel(function() {\n    return 1;\n  }, { output: [1], pipeline: true });\n  const texture1 = textureGetter();\n  const texture2 = textureGetter();\n  const kernel = gpu.createKernel(function(value1, value2) {\n    return value1[this.thread.x] + value2[this.thread.x] + this.constants.v1 + this.constants.v2;\n  }, { output: [1], constants: { v1: 1, v2: 1 } });\n\n  const output = kernel(texture1, texture2);\n\n  const gl = kernel.context;\n  assert.equal(kernel.kernelConstants.length, 2);\n  assert.equal(kernel.kernelConstants[0].contextHandle, null);\n  assert.equal(kernel.kernelConstants[1].contextHandle, null);\n\n  assert.equal(kernel.kernelArguments.length, 2);\n  assert.equal(kernel.kernelArguments[0].contextHandle, gl.TEXTURE0);\n  assert.equal(kernel.kernelArguments[1].contextHandle, gl.TEXTURE0 + 1);\n\n  gpu.destroy();\n}\ntest('createKernel with number constants & array arguments auto', () => {\n  creatKernelWithNumberConstantsAndArrayArguments();\n});\n(GPU.isGPUSupported ? test : skip)('createKernel with number constants & array arguments gpu', () => {\n  creatKernelWithNumberConstantsAndArrayArguments('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('createKernel with number constants & array arguments webgl', () => {\n  creatKernelWithNumberConstantsAndArrayArguments('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernel with number constants & array arguments webgl2', () => {\n  creatKernelWithNumberConstantsAndArrayArguments('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('createKernel with number constants & array arguments headlessgl', () => {\n  creatKernelWithNumberConstantsAndArrayArguments('headlessgl');\n});\n\nfunction createKernelMapWithArrayConstantsAndTextureArguments(mode) {\n  const gpu = new GPU({ mode });\n  function calcValue1(v) {\n    return v;\n  }\n  function calcValue2(v) {\n    return v;\n  }\n  const textureGetter = gpu.createKernel(function() {\n    return 1;\n  }, { output: [1], pipeline: true });\n  const texture1 = textureGetter();\n  const texture2 = textureGetter();\n  const kernel = gpu.createKernelMap({\n    mappedValue1: calcValue1,\n    mappedValue2: calcValue2,\n  }, function(value1, value2) {\n    return calcValue1(value1[this.thread.x] + value2[this.thread.x]) + calcValue2(this.constants.v1[this.thread.x] + this.constants.v2[this.thread.x]);\n  }, { output: [1], constants: { v1: [1], v2: [1] } });\n\n  kernel(texture1, texture2);\n  const gl = kernel.context;\n  assert.equal(kernel.kernelConstants.length, 2);\n  assert.equal(kernel.kernelConstants[0].contextHandle, gl.TEXTURE0);\n  assert.equal(kernel.kernelConstants[1].contextHandle, gl.TEXTURE0 + 1);\n\n  assert.equal(kernel.kernelArguments.length, 2);\n  assert.equal(kernel.kernelArguments[0].contextHandle, gl.TEXTURE0 + 2);\n  assert.equal(kernel.kernelArguments[1].contextHandle, gl.TEXTURE0 + 3);\n\n  gpu.destroy();\n}\ntest('createKernelMap with array constants & texture arguments auto', () => {\n  createKernelMapWithArrayConstantsAndTextureArguments();\n});\n(GPU.isGPUSupported ? test : skip)('createKernelMap with array constants & texture arguments gpu', () => {\n  createKernelMapWithArrayConstantsAndTextureArguments('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('createKernelMap with array constants & texture arguments webgl', () => {\n  createKernelMapWithArrayConstantsAndTextureArguments('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('createKernelMap with array constants & texture arguments webgl2', () => {\n  createKernelMapWithArrayConstantsAndTextureArguments('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('createKernelMap with array constants & texture arguments headlessgl', () => {\n  createKernelMapWithArrayConstantsAndTextureArguments('headlessgl');\n});\n"
  },
  {
    "path": "test/internal/underscores.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('internal: underscores');\n\nfunction testNumberArgument(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value_1) {\n    return value_1;\n  }, { output: [1], });\n  assert.equal(kernel(1)[0], 1);\n  gpu.destroy();\n}\n\ntest('number argument auto', () => {\n  testNumberArgument();\n});\ntest('number argument gpu', () => {\n  testNumberArgument('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('number argument webgl', () => {\n  testNumberArgument('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('number argument webgl2', () => {\n  testNumberArgument('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('number argument headlessgl', () => {\n  testNumberArgument('headlessgl');\n});\ntest('number argument cpu', () => {\n  testNumberArgument('cpu');\n});\n\nfunction testArrayArgument(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value_1) {\n    return value_1[this.thread.x];\n  }, { output: [1], });\n  assert.equal(kernel([1])[0], 1);\n  gpu.destroy();\n}\n\ntest('array argument auto', () => {\n  testArrayArgument();\n});\ntest('array argument gpu', () => {\n  testArrayArgument('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('array argument webgl', () => {\n  testArrayArgument('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('array argument webgl2', () => {\n  testArrayArgument('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('array argument headlessgl', () => {\n  testArrayArgument('headlessgl');\n});\ntest('array argument cpu', () => {\n  testArrayArgument('cpu');\n});\n\nfunction testTextureArgument(mode) {\n  const gpu = new GPU({ mode });\n  const texture = gpu.createKernel(function() { return 1; }, { output: [1], pipeline: true })();\n  const kernel = gpu.createKernel(function(value_1) {\n    return value_1[this.thread.x];\n  }, { output: [1], });\n  assert.equal(kernel(texture)[0], 1);\n  gpu.destroy();\n}\n\ntest('texture argument auto', () => {\n  testTextureArgument();\n});\ntest('texture argument gpu', () => {\n  testTextureArgument('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('texture argument webgl', () => {\n  testTextureArgument('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('texture argument webgl2', () => {\n  testTextureArgument('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('texture argument headlessgl', () => {\n  testTextureArgument('headlessgl');\n});\ntest('texture argument cpu', () => {\n  testTextureArgument('cpu');\n});\n\n\nfunction testArray2TextureArgument(mode) {\n  const gpu = new GPU({ mode });\n  const texture = gpu.createKernel(function() { return [1, 1]; }, { output: [1], pipeline: true })();\n  const kernel = gpu.createKernel(function(value_1) {\n    debugger;\n    return value_1[this.thread.x];\n  }, { output: [1], });\n  assert.deepEqual(kernel(texture)[0], new Float32Array([1, 1]));\n  gpu.destroy();\n}\n\ntest('array2 texture argument auto', () => {\n  testArray2TextureArgument();\n});\ntest('array2 texture argument gpu', () => {\n  testArray2TextureArgument('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('array2 texture argument webgl', () => {\n  testArray2TextureArgument('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('array2 texture argument webgl2', () => {\n  testArray2TextureArgument('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('array2 texture argument headlessgl', () => {\n  testArray2TextureArgument('headlessgl');\n});\n\n\nfunction testNumberConstant(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value_1;\n  }, {\n    output: [1],\n    constants: {\n      value_1: 1\n    },\n  });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('number constant auto', () => {\n  testNumberConstant();\n});\ntest('number constant gpu', () => {\n  testNumberConstant('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('number constant webgl', () => {\n  testNumberConstant('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('number constant webgl2', () => {\n  testNumberConstant('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('number constant headlessgl', () => {\n  testNumberConstant('headlessgl');\n});\ntest('number constant cpu', () => {\n  testNumberConstant('cpu');\n});\n\nfunction testArrayConstant(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value_1[0];\n  }, {\n    output: [1],\n    constants: {\n      value_1: [1]\n    },\n  });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('array constant auto', () => {\n  testArrayConstant();\n});\ntest('array constant gpu', () => {\n  testArrayConstant('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('array constant webgl', () => {\n  testArrayConstant('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('array constant webgl2', () => {\n  testArrayConstant('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('array constant headlessgl', () => {\n  testArrayConstant('headlessgl');\n});\ntest('array constant cpu', () => {\n  testArrayConstant('cpu');\n});\n\n\nfunction testTextureConstant(mode) {\n  const gpu = new GPU({ mode });\n  const texture = gpu.createKernel(function() { return 1; }, { output: [1], pipeline: true })();\n  const kernel = gpu.createKernel(function() {\n    return this.constants.value_1[0];\n  }, {\n    output: [1],\n    constants: {\n      value_1: texture\n    },\n  });\n  assert.equal(kernel()[0], 1);\n  gpu.destroy();\n}\n\ntest('texture constant auto', () => {\n  testTextureConstant();\n});\ntest('texture constant gpu', () => {\n  testTextureConstant('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('texture constant webgl', () => {\n  testTextureConstant('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('texture constant webgl2', () => {\n  testTextureConstant('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('texture constant headlessgl', () => {\n  testTextureConstant('headlessgl');\n});\n\n"
  },
  {
    "path": "test/internal/utils.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { utils } = require('../../src');\n\ndescribe('internal: utils');\n\ntest('systemEndianness not null', () => {\n  assert.ok(utils.systemEndianness() !== null, 'not null check');\n  assert.ok(utils.systemEndianness() === 'LE' ||  utils.systemEndianness() === 'BE', 'value = ' + utils.systemEndianness());\n});\n\ntest('isFunction', () => {\n  assert.ok(utils.isFunction(function() { }));\n  assert.notOk(utils.isFunction({}));\n});\n\ntest('isFunctionString', () => {\n  assert.ok(utils.isFunctionString('function() { }'));\n  assert.notOk(utils.isFunctionString({}));\n});\n\ntest('getFunctionName_fromString', () => {\n  assert.equal('test', utils.getFunctionNameFromString('function test() { }'));\n});\n\ntest('getParamNames_fromString', () => {\n  assert.deepEqual(['a','b','c'], utils.getArgumentNamesFromString('function test(a,b,c) { }'));\n});\n\ntest('closestSquareDimensions 2', () => {\n  assert.deepEqual(Array.from(utils.closestSquareDimensions(2)), [1,2]);\n});\n\ntest('closestSquareDimensions 5', () => {\n  assert.deepEqual(Array.from(utils.closestSquareDimensions(5)), [2,3]);\n});\n\ntest('closestSquareDimensions 6', () => {\n  assert.deepEqual(Array.from(utils.closestSquareDimensions(6)), [2,3]);\n});\n\ntest('closestSquareDimensions 7', () => {\n  assert.deepEqual(Array.from(utils.closestSquareDimensions(7)), [4,2]);\n});\n\ntest('getDimensions Array of 6, padded', () => {\n  assert.deepEqual(Array.from(utils.getDimensions(new Array(6).fill(1), true)), [6,1,1]);\n});\n\ntest('getDimensions Array of 6,1,1, padded', () => {\n  assert.deepEqual(Array.from(utils.getDimensions([[[1,1,1,1,1,1]]], true)), [6,1,1]);\n});\n\ntest('getDimensions Array of 1,6,1, padded', () => {\n  assert.deepEqual(Array.from(utils.getDimensions([[[1],[1],[1],[1],[1],[1]]], true)), [1,6,1]);\n});\n\ntest('getDimensions Array of 1,1,6, padded', () => {\n  assert.deepEqual(Array.from(utils.getDimensions([[[1]],[[1]],[[1]],[[1]],[[1]],[[1]]], true)), [1,1,6]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [6,1,1], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([6, 1, 1], 4)), [1, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,6,1], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 6, 1], 4)), [1, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,1,6], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 1, 6], 4)), [1, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [6,1,1], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([6, 1, 1], 2)), [2, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,6,1], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 6, 1], 2)), [2, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,1,6], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 1, 6], 2)), [2, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [6,1,1], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([6, 1, 1], 1)), [4, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,6,1], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 6, 1], 1)), [4, 2]);\n});\n\ntest('getMemoryOptimizedFloatTextureSize [1,1,6], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedFloatTextureSize([1, 1, 6], 1)), [4, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [6,1,1], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([6, 1, 1], 4)), [4, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,6,1], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 6, 1], 4)), [4, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,1,6], bitRatio 4', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 1, 6], 4)), [4, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [6,1,1], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([6, 1, 1], 2)), [2, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,6,1], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 6, 1], 2)), [2, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,1,6], bitRatio 2', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 1, 6], 2)), [2, 2]);\n});\ntest('getMemoryOptimizedPackedTextureSize [6,1,1], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([6, 1, 1], 1)), [1, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,6,1], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 6, 1], 1)), [1, 2]);\n});\n\ntest('getMemoryOptimizedPackedTextureSize [1,1,6], bitRatio 1', () => {\n  assert.deepEqual(Array.from(utils.getMemoryOptimizedPackedTextureSize([1, 1, 6], 1)), [1, 2]);\n});\n\ntest('getKernelTextureSize for [1,2] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [1,2]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('getKernelTextureSize for [2,3] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [2,3]);\n  assert.deepEqual(textureSize, new Int32Array([2,3]));\n});\n\ntest('getKernelTextureSize for [4,2] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [4,2]);\n  assert.deepEqual(textureSize, new Int32Array([4,2]));\n});\n\ntest('getKernelTextureSize for [6,1,1] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [6,1,1]);\n  assert.deepEqual(textureSize, new Int32Array([2,3]));\n});\n\ntest('getKernelTextureSize for [1,6,1] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [1,6,1]);\n  assert.deepEqual(textureSize, new Int32Array([1,6]));\n});\n\ntest('getKernelTextureSize for [1,1,6] output, optimizeFloatMemory = true, and precision = \"unsigned\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'unsigned',\n  }, [1,1,6]);\n  assert.deepEqual(textureSize, new Int32Array([2,3]));\n});\n\ntest('getKernelTextureSize for [1,2] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [1,2]);\n  assert.deepEqual(textureSize, new Int32Array([1,1]));\n});\n\ntest('getKernelTextureSize for [2,3] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [2,3]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('getKernelTextureSize for [4,2] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [4,2]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('getKernelTextureSize for [6,1,1] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [6,1,1]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('getKernelTextureSize for [1,6,1] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [1,6,1]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('getKernelTextureSize for [1,1,6] output, optimizeFloatMemory = true, and precision = \"single\"', () => {\n  const textureSize = utils.getKernelTextureSize({\n    optimizeFloatMemory: true,\n    precision: 'single',\n  }, [1,1,6]);\n  assert.deepEqual(textureSize, new Int32Array([1,2]));\n});\n\ntest('erectPackedFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,0,0]);\n  const result = utils.erectPackedFloat(array, 6);\n  assert.deepEqual(result, new Float32Array([0,1,2,3,4,5]));\n});\ntest('erect2DPackedFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,6,7,8,0,0,0,0]);\n  const result = utils.erect2DPackedFloat(array, 3, 3);\n  assert.deepEqual(result, [\n    new Float32Array([0,1,2]),\n    new Float32Array([3,4,5]),\n    new Float32Array([6,7,8])\n  ]);\n});\ntest('erect3DPackedFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,6,7,0,0,0,0,0]);\n  const result = utils.erect3DPackedFloat(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1]),\n      new Float32Array([2,3]),\n    ],[\n      new Float32Array([4,5]),\n      new Float32Array([6,7]),\n    ]\n  ]);\n});\ntest('erectMemoryOptimizedFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,0,0]);\n  const result = utils.erectMemoryOptimizedFloat(array, 6);\n  assert.deepEqual(result, new Float32Array([0,1,2,3,4,5]));\n});\ntest('erectMemoryOptimized2DFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,6,7,8,0,0,0,0]);\n  const result = utils.erectMemoryOptimized2DFloat(array, 3, 3);\n  assert.deepEqual(result, [\n    new Float32Array([0,1,2]),\n    new Float32Array([3,4,5]),\n    new Float32Array([6,7,8])\n  ]);\n});\ntest('erectMemoryOptimized3DFloat', () => {\n  const array = new Float32Array([0,1,2,3,4,5,6,7,0,0,0,0,0]);\n  const result = utils.erectMemoryOptimized3DFloat(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1]),\n      new Float32Array([2,3]),\n    ],[\n      new Float32Array([4,5]),\n      new Float32Array([6,7]),\n    ]\n  ]);\n});\ntest('erectFloat', () => {\n  const array = new Float32Array([\n    0,0,0,0,\n    1,0,0,0,\n    2,0,0,0,\n    3,0,0,0,\n    4,0,0,0,\n    5,0,0,0\n  ]);\n  const result = utils.erectFloat(array, 6);\n  assert.deepEqual(result, new Float32Array([0,1,2,3,4,5]));\n});\ntest('erect2DFloat', () => {\n  const array = new Float32Array([\n    0,0,0,0,\n    1,0,0,0,\n    2,0,0,0,\n    3,0,0,0,\n    4,0,0,0,\n    5,0,0,0,\n    6,0,0,0,\n    7,0,0,0,\n    8,0,0,0,\n    0,0,0,0\n  ]);\n  const result = utils.erect2DFloat(array, 3, 3);\n  assert.deepEqual(result, [\n    new Float32Array([0,1,2]),\n    new Float32Array([3,4,5]),\n    new Float32Array([6,7,8])\n  ]);\n});\ntest('erect3DFloat', () => {\n  const array = new Float32Array([\n    0,0,0,0,\n    1,0,0,0,\n    2,0,0,0,\n    3,0,0,0,\n    4,0,0,0,\n    5,0,0,0,\n    6,0,0,0,\n    7,0,0,0,\n    0,0,0,0\n  ]);\n  const result = utils.erect3DFloat(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1]),\n      new Float32Array([2,3]),\n    ],[\n      new Float32Array([4,5]),\n      new Float32Array([6,7]),\n    ]\n  ]);\n});\ntest('erectArray2', () => {\n  const array = new Float32Array([\n    0,1,0,0,\n    2,3,0,0,\n    4,5,0,0,\n    6,7,0,0\n  ]);\n  const result = utils.erectArray2(array, 4);\n  assert.deepEqual(result, [\n    new Float32Array([0,1]),\n    new Float32Array([2,3]),\n    new Float32Array([4,5]),\n    new Float32Array([6,7]),\n  ]);\n});\ntest('erect2DArray2', () => {\n  const array = new Float32Array([\n    0,1,0,0,\n    2,3,0,0,\n    4,5,0,0,\n    6,7,0,0\n  ]);\n  const result = utils.erect2DArray2(array, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1]),\n      new Float32Array([2,3]),\n    ],\n    [\n      new Float32Array([4,5]),\n      new Float32Array([6,7]),\n    ]\n  ]);\n});\ntest('erect3DArray2', () => {\n  const array = new Float32Array([\n    0,1,0,0,\n    2,3,0,0,\n    4,5,0,0,\n    6,7,0,0,\n    8,9,0,0,\n    10,11,0,0,\n    12,13,0,0,\n    14,15,0,0,\n  ]);\n  const result = utils.erect3DArray2(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      [\n        new Float32Array([0,1]),\n        new Float32Array([2,3]),\n      ],\n      [\n        new Float32Array([4,5]),\n        new Float32Array([6,7]),\n      ]\n    ],\n    [\n      [\n        new Float32Array([8,9]),\n        new Float32Array([10,11]),\n      ],\n      [\n        new Float32Array([12,13]),\n        new Float32Array([14,15]),\n      ]\n    ]\n  ]);\n});\ntest('erectArray3', () => {\n  const array = new Float32Array([\n    0,1,2,0,\n    3,4,5,0,\n    6,7,8,0,\n    9,10,11,0\n  ]);\n  const result = utils.erectArray3(array, 4);\n  assert.deepEqual(result, [\n    new Float32Array([0,1,2]),\n    new Float32Array([3,4,5]),\n    new Float32Array([6,7,8]),\n    new Float32Array([9,10,11]),\n  ]);\n});\ntest('erect2DArray3', () => {\n  const array = new Float32Array([\n    0,1,2,0,\n    3,4,5,0,\n    6,7,8,0,\n    9,10,11,0,\n  ]);\n  const result = utils.erect2DArray3(array, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1,2]),\n      new Float32Array([3,4,5]),\n    ],\n    [\n      new Float32Array([6,7,8]),\n      new Float32Array([9,10,11]),\n    ]\n  ]);\n});\ntest('erect3DArray3', () => {\n  const array = new Float32Array([\n    0,1,2,0,\n    3,4,5,0,\n    6,7,8,0,\n    9,10,11,0,\n    12,13,14,0,\n    15,16,17,0,\n    18,19,20,0,\n    21,22,23,0,\n  ]);\n  const result = utils.erect3DArray3(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      [\n        new Float32Array([0,1,2]),\n        new Float32Array([3,4,5]),\n      ],\n      [\n        new Float32Array([6,7,8]),\n        new Float32Array([9,10,11]),\n      ]\n    ],\n    [\n      [\n        new Float32Array([12,13,14]),\n        new Float32Array([15,16,17]),\n      ],\n      [\n        new Float32Array([18,19,20]),\n        new Float32Array([21,22,23]),\n      ]\n    ]\n  ]);\n});\ntest('erectArray4', () => {\n  const array = new Float32Array([\n    0,1,2,3,\n    4,5,6,7,\n    8,9,10,11,\n    12,13,14,15,\n  ]);\n  const result = utils.erectArray4(array, 4);\n  assert.deepEqual(result, [\n    new Float32Array([0,1,2,3]),\n    new Float32Array([4,5,6,7]),\n    new Float32Array([8,9,10,11]),\n    new Float32Array([12,13,14,15]),\n  ]);\n\n});\ntest('erect2DArray4', () => {\n  const array = new Float32Array([\n    0,1,2,3,\n    4,5,6,7,\n    8,9,10,11,\n    12,13,14,15,\n  ]);\n  const result = utils.erect2DArray4(array, 2, 2);\n  assert.deepEqual(result, [\n    [\n      new Float32Array([0,1,2,3]),\n      new Float32Array([4,5,6,7]),\n    ],\n    [\n      new Float32Array([8,9,10,11]),\n      new Float32Array([12,13,14,15]),\n    ]\n  ]);\n});\ntest('erect3DArray4', () => {\n  const array = new Float32Array([\n    0,1,2,3,\n    4,5,6,7,\n    8,9,10,11,\n    12,13,14,15,\n    16,17,18,19,\n    20,21,22,23,\n    24,25,26,27,\n    28,29,30,31,\n  ]);\n  const result = utils.erect3DArray4(array, 2, 2, 2);\n  assert.deepEqual(result, [\n    [\n      [\n        new Float32Array([0,1,2,3]),\n        new Float32Array([4,5,6,7]),\n      ],\n      [\n        new Float32Array([8,9,10,11]),\n        new Float32Array([12,13,14,15]),\n      ]\n    ],\n    [\n      [\n        new Float32Array([16,17,18,19]),\n        new Float32Array([20,21,22,23]),\n      ],\n      [\n        new Float32Array([24,25,26,27]),\n        new Float32Array([28,29,30,31]),\n      ]\n    ]\n  ]);\n});\n\ntest('flattenFunctionToString', () => {\n  // since we use this internally, currently just testing if parsing simply works\n  [\n    utils.erectPackedFloat,\n    utils.erect2DPackedFloat,\n    utils.erect3DPackedFloat,\n    utils.erectMemoryOptimizedFloat,\n    utils.erectMemoryOptimized2DFloat,\n    utils.erectMemoryOptimized3DFloat,\n    utils.erectFloat,\n    utils.erect2DFloat,\n    utils.erect3DFloat,\n    utils.erectArray2,\n    utils.erect2DArray2,\n    utils.erect3DArray2,\n    utils.erectArray3,\n    utils.erect2DArray3,\n    utils.erect3DArray3,\n    utils.erectArray4,\n    utils.erect2DArray4,\n    utils.erect3DArray4\n  ].forEach(fn => eval(utils.flattenFunctionToString(fn, {\n    findDependency: () => {},\n    thisLookup: () => {},\n  })));\n  assert.ok(true);\n});\n\ntest('improper getMinifySafeName usage with arrow function', () => {\n  assert.throws(() => {\n    utils.getMinifySafeName(() => {});\n  }, 'Unrecognized function type.');\n});\n\ntest('improper getMinifySafeName usage with regular function', () => {\n  assert.throws(() => {\n    utils.getMinifySafeName(function() {});\n  }, 'Unrecognized function type.');\n});\n\ntest('proper getMinifySafeName usage with arrow function', () => {\n  function n() {}\n  const safeName = utils.getMinifySafeName(() => n);\n  assert.equal(safeName, 'n');\n});\n\ntest('proper getMinifySafeName usage with regular function', () => {\n  function n() {}\n  const safeName = utils.getMinifySafeName(function () {\n    return n;\n  });\n  assert.equal(safeName, 'n');\n});"
  },
  {
    "path": "test/issues/114-create-kernel-map-run-second-time.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue # 114');\nfunction secondKernelMap(mode) {\n  const gpu = new GPU({ mode });\n  const A = [1, 2, 3, 4, 5];\n  const B = [1, 2, 3, 4, 5];\n  function add(a,b){\n    return a + b;\n  }\n  const kernels = gpu.createKernelMap([add], function(a, b) {\n    return add(a[this.thread.x], b[this.thread.x]);\n  })\n    .setOutput([5]);\n\n  const E = kernels(A, B).result;\n  const F = kernels(A, B).result;\n  const G = kernels(A, B).result;\n\n  assert.deepEqual(Array.from(E), [2, 4, 6, 8, 10]);\n  assert.deepEqual(Array.from(F), [2, 4, 6, 8, 10]);\n  assert.deepEqual(Array.from(G), [2, 4, 6, 8, 10]);\n  gpu.destroy();\n}\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #114 - run createKernelMap the second time auto\", () => {\n  secondKernelMap();\n});\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #114 - run createKernelMap the second time gpu\", () => {\n  secondKernelMap('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)(\"Issue #114 - run createKernelMap the second time webgl\", () => {\n  secondKernelMap('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #114 - run createKernelMap the second time webgl2\", () => {\n  secondKernelMap('webgl2');\n});\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)(\"Issue #114 - run createKernelMap the second time headlessgl\", () => {\n  secondKernelMap('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/116-multiple-kernels-run-again.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #116');\n\nfunction multipleKernels(mode) {\n  const gpu = new GPU({ mode });\n  const A = [1, 2, 3, 4, 5];\n  const B = [1, 2, 3, 4, 5];\n\n  const sizes = [2, 5, 1];\n\n  function add(a, b, x){\n    return a[x] + b[x];\n  }\n\n  const layerForward = [];\n\n  for (let i = 0;  i < 2; i++) {\n    const kernels = gpu.createKernelMap([add],function(a, b){\n      return add(a,b, this.thread.x);\n    })\n      .setOutput([sizes[i + 1]]); // First: 5. Second: 1.\n\n    layerForward.push(kernels);\n  }\n\n  const E = layerForward[0](A, B).result;\n  const F = layerForward[1](A, B).result;\n  const G = layerForward[0](A, B).result;\n\n  assert.deepEqual(Array.from(E), [2, 4, 6, 8, 10]);\n  assert.deepEqual(Array.from(F), [2]);\n  assert.deepEqual(Array.from(G), [2, 4, 6, 8, 10]);\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #116 - multiple kernels run again auto\", () => {\n  multipleKernels();\n});\n\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #116 - multiple kernels run again gpu\", () => {\n  multipleKernels('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"Issue #116 - multiple kernels run again webgl\", () => {\n  multipleKernels('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #116 - multiple kernels run again webgl2\", () => {\n  multipleKernels('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)(\"Issue #116 - multiple kernels run again headlessgl\", () => {\n  multipleKernels('headlessgl');\n});\n\ntest(\"Issue #116 - multiple kernels run again cpu\", () => {\n  multipleKernels('cpu');\n});\n"
  },
  {
    "path": "test/issues/130-typed-array.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #130');\nfunction typedArrays(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(changes) {\n    return changes[this.thread.y][this.thread.x];\n  })\n    .setOutput([2, 1]);\n\n  const values = [new Float32Array(2)];\n  values[0][0] = 0;\n  values[0][1] = 0;\n  const result = kernel(values);\n  assert.equal(result[0][0], 0);\n  assert.equal(result[0][1], 0);\n  gpu.destroy();\n}\n\ntest(\"Issue #130 - typed array auto\", () => {\n  typedArrays(null);\n});\n\ntest(\"Issue #130 - typed array gpu\", () => {\n  typedArrays('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"Issue #130 - typed array webgl\", () => {\n  typedArrays('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #130 - typed array webgl2\", () => {\n  typedArrays('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"Issue #130 - typed array headlessgl\", () => {\n  typedArrays('headlessgl');\n});\n\ntest(\"Issue #130 - typed array cpu\", () => {\n  typedArrays('cpu');\n});\n"
  },
  {
    "path": "test/issues/147-missing-constant.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #147');\n\nfunction missingConstant(mode) {\n  const gpu = new GPU({ mode });\n  function getPi() {\n    return this.constants.pi;\n  }\n  gpu.addFunction(getPi);\n  const kernel = gpu.createKernel(function() {\n    return getPi();\n  })\n    .setOutput([1])\n    .setConstants({ pi: Math.PI });\n\n  const result = kernel();\n  assert.equal(result[0].toFixed(7), Math.PI.toFixed(7));\n  gpu.destroy();\n}\n\ntest(\"Issue #147 - missing constant auto\", () => {\n  missingConstant(null);\n});\n\ntest(\"Issue #147 - missing constant gpu\", () => {\n  missingConstant('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"Issue #147 - missing constant webgl\", () => {\n  missingConstant('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #147 - missing constant webgl2\", () => {\n  missingConstant('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"Issue #147 - missing constant headlessgl\", () => {\n  missingConstant('headlessgl');\n});\n\ntest(\"Issue #147 - missing constant cpu\", () => {\n  missingConstant('cpu');\n});\n"
  },
  {
    "path": "test/issues/152-for-vars.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #152');\n\nfunction forVars(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel = gpu.createKernel(function() {\n    let sum = 0;\n    for (let i = 0; i < 2; i++) {\n      sum += i;\n    }\n    return sum;\n  })\n    .setOutput([1, 1]);\n\n  const result = kernel();\n  assert.equal(result.length, 1);\n  assert.equal(result[0], 1);\n  gpu.destroy();\n}\n\ntest('Issue #152 - for vars cpu', () => {\n  forVars('cpu');\n});\n\ntest('Issue #152 - for vars auto', () => {\n  forVars('gpu');\n});\n\ntest('Issue #152 - for vars gpu', () => {\n  forVars('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #152 - for vars webgl', () => {\n  forVars('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #152 - for vars webgl2', () => {\n  forVars('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #152 - for vars headlessgl', () => {\n  forVars('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/159-3d.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\n\ndescribe('issue # 159');\n\n(function() {\n  const { GPU } = require('../../src');\n  function threeD(mode) {\n    const gpu = new GPU({ mode });\n\n    const kernel = gpu.createKernel(function(grid) {\n      return grid[this.thread.y][this.thread.x];\n    })\n      .setOutput([5, 5]);\n\n    //This would cause the above to fail\n    gpu.createKernel(function() { return 0; })\n      .setOutput([5, 5, 5])\n      .build();\n\n    const result = kernel([\n      [0,1,2,3,4],\n      [1,2,3,4,5],\n      [2,3,4,5,6],\n      [3,4,5,6,7],\n      [4,5,6,7,8]\n    ]);\n    assert.equal(result.length, 5);\n    assert.deepEqual(result.map(function(v) { return Array.from(v); }), [\n      [0,1,2,3,4],\n      [1,2,3,4,5],\n      [2,3,4,5,6],\n      [3,4,5,6,7],\n      [4,5,6,7,8]\n    ]);\n    gpu.destroy();\n  }\n\n  test('Issue #159 - for vars auto', () => {\n    threeD(null);\n  });\n\n  test('Issue #159 - for vars gpu', () => {\n    threeD('gpu');\n  });\n\n  (GPU.isWebGLSupported ? test : skip)('Issue #159 - for vars webgl', () => {\n    threeD('webgl');\n  });\n\n  (GPU.isWebGL2Supported ? test : skip)('Issue #159 - for vars webgl2', () => {\n    threeD('webgl2');\n  });\n\n  (GPU.isHeadlessGLSupported ? test : skip)('Issue #159 - for vars headlessgl', () => {\n    threeD('headlessgl');\n  });\n\n  test('Issue #159 - for vars cpu', () => {\n    threeD('cpu');\n  });\n})();\n"
  },
  {
    "path": "test/issues/174-webgl-context-warning.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue # 174');\n\nconst input = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];\n\n// recursive!\nfunction manyKernels(mode, kernelCount, t) {\n  if (kernelCount < 1) return;\n  const done = t.async();\n  kernelCount--;\n\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(inp) {\n    return inp[this.thread.y][this.thread.x];\n  }, {\n    output: [3, 3]\n  });\n  const kernel2 = gpu.createKernel(function() {\n    return this.thread.y * this.thread.x;\n  }, {\n    output: [1024, 1024],\n    pipeline: true\n  });\n  kernel(input);\n  kernel2();\n  assert.strictEqual(kernel.context, kernel2.context, \"contexts should be the same object\");\n  manyKernels(mode, kernelCount, t);\n  const canvas = kernel.canvas;\n  const eventListener = canvas.addEventListener('webglcontextlost', (e) => {\n    canvas.removeEventListener('webglcontextlost', eventListener);\n    done();\n  });\n\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #174 - webgl context leak webgl', t => {\n  manyKernels('webgl', 10, t);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #174 - webgl context leak webgl2', t => {\n  manyKernels('webgl2', 10, t);\n});\n"
  },
  {
    "path": "test/issues/195-read-from-texture2d.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #195');\nfunction makeKernel(gpu) {\n  return gpu.createKernel(function(a){\n    return a[this.thread.y][this.thread.x];\n  })\n    .setOutput([matrixSize, matrixSize]);\n}\n\nfunction splitArray(array, part) {\n  const result = [];\n  for(let i = 0; i < array.length; i += part) {\n    result.push(array.slice(i, i + part));\n  }\n  return result;\n}\n\nconst matrixSize =  4;\nconst A = splitArray(Array.apply(null, Array(matrixSize * matrixSize)).map((_, i) => i), matrixSize);\n\nfunction readFromTexture(mode) {\n  const gpu = new GPU({ mode });\n  const noTexture = makeKernel(gpu);\n  const texture = makeKernel(gpu)\n    .setPipeline(true);\n\n  const result = noTexture(A);\n  const textureResult = texture(A).toArray(gpu);\n\n  assert.deepEqual(result.map((v) => Array.from(v)), A);\n  assert.deepEqual(textureResult.map((v) => Array.from(v)), A);\n  assert.deepEqual(textureResult, result);\n  gpu.destroy();\n}\n\ntest(\"Issue #195 Read from Texture 2D (GPU only) auto\", () => {\n  readFromTexture();\n});\n\ntest(\"Issue #195 Read from Texture 2D (GPU only) gpu\", () => {\n  readFromTexture('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)(\"Issue #195 Read from Texture 2D (GPU only) webgl\", () => {\n  readFromTexture('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #195 Read from Texture 2D (GPU Only) webgl2\", () => {\n  readFromTexture('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)(\"Issue #195 Read from Texture 2D (GPU Only) headlessgl\", () => {\n  readFromTexture('headlessgl');\n});\n\n"
  },
  {
    "path": "test/issues/207-same-function-reuse.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #207');\n\nfunction sameFunctionReuse(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel = gpu.createKernel(function(kernelArg1, kernelArg2) {\n    function someFun1(someFun1Arg1, someFun1Arg2) {\n      return customAdder(someFun1Arg1, someFun1Arg2);\n    }\n    function someFun2(someFun2Arg1, someFun2Arg2) {\n      return customAdder(someFun2Arg1, someFun2Arg2);\n    }\n    function customAdder(customAdderArg1, customAdderArg2) {\n      return customAdderArg1 + customAdderArg2;\n    }\n    return someFun1(1, 2) + someFun2(kernelArg1[this.thread.x], kernelArg2[this.thread.x]);\n  })\n    .setOutput([6]);\n\n  const a = [1, 2, 3, 5, 6, 7];\n  const b = [4, 5, 6, 1, 2, 3];\n  const result = kernel(a,b);\n  assert.deepEqual(Array.from(result), [8, 10, 12, 9, 11, 13]);\n  gpu.destroy();\n}\n\ntest('Issue #207 - same function reuse auto', () => {\n  sameFunctionReuse(null);\n});\n\ntest('Issue #207 - same function reuse gpu', () => {\n  sameFunctionReuse('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #207 - same function reuse webgl', () => {\n  sameFunctionReuse('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #207 - same function reuse webgl2', () => {\n  sameFunctionReuse('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #207 - same function reuse headlessgl', () => {\n  sameFunctionReuse('headlessgl');\n});\n\ntest('Issue #207 - same function reuse cpu', () => {\n  sameFunctionReuse('cpu');\n});\n"
  },
  {
    "path": "test/issues/212-funky-function-support.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #212');\n\nfunction funky(mode) {\n  const gpu = new GPU({ mode });\n  gpu.addFunction(function add(value1, value2) {\n    return value1 + value2;\n  });\n  const kernel = gpu.createKernel(`function(v1, v2) {\n    return (0, _add.add)(v1[this.thread.y][this.thread.x], v2[this.thread.y][this.thread.x]);\n  }`)\n    .setOutput([2, 2]);\n\n  const result = kernel([\n    [0,1],\n    [1,2]\n  ], [\n    [0,1],\n    [1,2]\n  ]);\n  assert.deepEqual(result.map((v) => Array.from(v)), [\n    [0,2],\n    [2,4]\n  ]);\n  gpu.destroy();\n}\n\ntest('Issue #212 - funky function support auto', () => {\n  funky('gpu');\n});\n\ntest('Issue #212 - funky function support gpu', () => {\n  funky('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #212 - funky function support webgl', () => {\n  funky('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #212 - funky function support webgl2', () => {\n  funky('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #212 - funky function support headlessgl', () => {\n  funky('headlessgl');\n});\n\ntest('Issue #212 - funky function support cpu', () => {\n  funky('cpu');\n});\n"
  },
  {
    "path": "test/issues/233-kernel-map-single-precision.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue # 233');\n\n//TODO: Write for 2D and 3D and textures\n//TODO: Write for pipeline as well\nfunction kernelMapSinglePrecision(mode) {\n  const lst = [1, 2, 3, 4, 5, 6, 7];\n  const gpu = new GPU({ mode });\n  const kernels = gpu.createKernelMap({\n    stepA: function (x) {\n      return x * x;\n    },\n    stepB: function (x) {\n      return x + 1;\n    }\n  }, function (lst) {\n    const val = lst[this.thread.x];\n\n    stepA(val);\n    stepB(val);\n\n    return val;\n  }, {\n    precision: 'single',\n    output: [lst.length]\n  });\n\n  const result = kernels(lst);\n  const unwrap = gpu.createKernel(function(x) {\n    return x[this.thread.x];\n  }, {\n    output: [lst.length],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const stepAResult = unwrap(result.stepA);\n  const stepBResult = unwrap(result.stepB);\n\n  assert.deepEqual(Array.from(stepAResult), lst.map((x) => x * x));\n  assert.deepEqual(Array.from(stepBResult), lst.map((x) => x + 1));\n  assert.deepEqual(Array.from(result.result), lst);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision auto', () => {\n  kernelMapSinglePrecision();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision gpu', () => {\n  kernelMapSinglePrecision('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Issue #233 - kernel map with single precision webgl', () => {\n  kernelMapSinglePrecision('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #233 - kernel map with single precision webgl2', () => {\n  kernelMapSinglePrecision('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision headlessgl', () => {\n  kernelMapSinglePrecision('headlessgl');\n});\n\ntest('Issue #233 - kernel map with single precision cpu', () => {\n  kernelMapSinglePrecision('cpu');\n});\n\n\nfunction kernelMapSinglePrecision2D(mode) {\n  const lst = [\n    [1,2,3],\n    [4,5,6],\n    [7,8,9]\n  ];\n  const stepAExpected = [\n    [1,4,9],\n    [16,25,36],\n    [49,64,81],\n  ];\n  const stepBExpected = [\n    [2,3,4],\n    [5,6,7],\n    [8,9,10]\n  ];\n  const gpu = new GPU({ mode });\n  const kernels = gpu.createKernelMap({\n    stepA: function (x) {\n      return x * x;\n    },\n    stepB: function (x) {\n      return x + 1;\n    }\n  }, function (lst) {\n    const val = lst[this.thread.y][this.thread.x];\n\n    stepA(val);\n    stepB(val);\n\n    return val;\n  }, {\n    precision: 'single',\n    output: [3, 3]\n  });\n\n  const result = kernels(lst);\n  assert.deepEqual(result.stepA.map(v => Array.from(v)), stepAExpected);\n  assert.deepEqual(result.stepB.map(v => Array.from(v)), stepBExpected);\n  assert.deepEqual(result.result.map(v => Array.from(v)), lst);\n  const memoryOptimize = gpu.createKernel(function(x) {\n    return x[this.thread.y][this.thread.x];\n  }, {\n    output: [3, 3],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const stepAOptimized = memoryOptimize(result.stepA);\n  const stepBOptimized = memoryOptimize(result.stepB);\n  const resultOptimized = memoryOptimize(result.result);\n\n  assert.deepEqual(stepAOptimized.map(v => Array.from(v)), stepAExpected);\n  assert.deepEqual(stepBOptimized.map(v => Array.from(v)), stepBExpected);\n  assert.deepEqual(resultOptimized.map(v => Array.from(v)), lst);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 2d auto', () => {\n  kernelMapSinglePrecision2D();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 2d gpu', () => {\n  kernelMapSinglePrecision2D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Issue #233 - kernel map with single precision 2d webgl', () => {\n  kernelMapSinglePrecision2D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #233 - kernel map with single precision 2d webgl2', () => {\n  kernelMapSinglePrecision2D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 2d headlessgl', () => {\n  kernelMapSinglePrecision2D('headlessgl');\n});\n\ntest('Issue #233 - kernel map with single precision 2d cpu', () => {\n  kernelMapSinglePrecision2D('cpu');\n});\n\nfunction kernelMapSinglePrecision3D(mode) {\n  const lst = [\n    [\n      [1,2,3],\n      [4,5,6],\n      [7,8,9]\n    ],\n    [\n      [10,11,12],\n      [13,14,15],\n      [16,17,18]\n    ]\n  ];\n  const stepAExpected = [\n    [\n      [1,4,9],\n      [16,25,36],\n      [49,64,81],\n    ],\n    [\n      [100,121,144],\n      [169,196,225],\n      [256,289,324],\n    ]\n  ];\n  const stepBExpected = [\n    [\n      [2,3,4],\n      [5,6,7],\n      [8,9,10]\n    ],\n    [\n      [11,12,13],\n      [14,15,16],\n      [17,18,19]\n    ]\n  ];\n  const gpu = new GPU({ mode });\n  const kernels = gpu.createKernelMap({\n    stepA: function (x) {\n      return x * x;\n    },\n    stepB: function (x) {\n      return x + 1;\n    }\n  }, function (lst) {\n    const val = lst[this.thread.z][this.thread.y][this.thread.x];\n\n    stepA(val);\n    stepB(val);\n\n    return val;\n  }, {\n    precision: 'single',\n    output: [3, 3, 2]\n  });\n\n  const result = kernels(lst);\n  assert.deepEqual(arrayFromCube(result.stepA), stepAExpected);\n  assert.deepEqual(arrayFromCube(result.stepB), stepBExpected);\n  assert.deepEqual(arrayFromCube(result.result), lst);\n  const memoryOptimize = gpu.createKernel(function(x) {\n    return x[this.thread.z][this.thread.y][this.thread.x];\n  }, {\n    output: [3, 3, 2],\n    precision: 'single',\n    optimizeFloatMemory: true,\n  });\n  const stepAOptimized = memoryOptimize(result.stepA);\n  const stepBOptimized = memoryOptimize(result.stepB);\n  const resultOptimized = memoryOptimize(result.result);\n\n  assert.deepEqual(arrayFromCube(stepAOptimized), stepAExpected);\n  assert.deepEqual(arrayFromCube(stepBOptimized), stepBExpected);\n  assert.deepEqual(arrayFromCube(resultOptimized), lst);\n\n  function arrayFromCube(cube) {\n    return cube.map(matrix => matrix.map(row => Array.from(row)));\n  }\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 3d auto', () => {\n  kernelMapSinglePrecision3D();\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 3d gpu', () => {\n  kernelMapSinglePrecision3D('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Issue #233 - kernel map with single precision 3d webgl', () => {\n  kernelMapSinglePrecision3D('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #233 - kernel map with single precision 3d webgl2', () => {\n  kernelMapSinglePrecision3D('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('Issue #233 - kernel map with single precision 3d headlessgl', () => {\n  kernelMapSinglePrecision3D('headlessgl');\n});\n\ntest('Issue #233 - kernel map with single precision 3d cpu', () => {\n  kernelMapSinglePrecision3D('cpu');\n});\n"
  },
  {
    "path": "test/issues/241-CPU-vs-GPU-maps-output-differently.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #241');\n\n// this is actually equiv to\n// return this.thread.y * 3 + this.thread.x;\nconst input = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];\nfunction buildIndexTestKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(inp) {\n    return inp[this.thread.y][this.thread.x];\n  }, {\n    output: [3, 3]\n  });\n  const result = kernel(input).map((v) => Array.from(v));\n  assert.deepEqual(result, input);\n  gpu.destroy();\n}\n\ntest('Issue #241 small 2d array input output test auto', () => {\n  buildIndexTestKernel();\n});\n\ntest('Issue #241 small 2d array input output test gpu', () => {\n  buildIndexTestKernel('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #241 small 2d array input output test webgl', () => {\n  buildIndexTestKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #241 small 2d array input output test webgl2', () => {\n  buildIndexTestKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #241 small 2d array input output test headlessgl', () => {\n  buildIndexTestKernel('headlessgl');\n});\n\ntest('Issue #241 small 2d array input output test cpu', () => {\n  buildIndexTestKernel('cpu');\n});\n"
  },
  {
    "path": "test/issues/259-atan2.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #259');\n\nfunction buildAtan2KernelResult(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function() {\n    return Math.atan2(1, 2);\n  }, {\n    output: [1],\n  });\n  assert.equal(kernel()[0].toFixed(7), 0.4636476);\n  gpu.destroy();\n}\n\ntest('Issue #259 atan2 - auto', () => {\n  buildAtan2KernelResult();\n});\n\ntest('Issue #259 atan2 - gpu', () => {\n  buildAtan2KernelResult('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #259 atan2 - webgl', () => {\n  buildAtan2KernelResult('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #259 atan2 - webgl2', () => {\n  buildAtan2KernelResult('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #259 atan2 - headlessgl', () => {\n  buildAtan2KernelResult('headlessgl');\n});\n\ntest('Issue #259 atan2 - cpu', () => {\n  buildAtan2KernelResult('cpu');\n});\n"
  },
  {
    "path": "test/issues/263-to-string.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #263');\n\nfunction toString(mode, context, canvas) {\n  const gpu = new GPU({ mode, context, canvas });\n  const kernel = gpu.createKernel(function() {\n    return 1;\n  }, {\n    output: [1]\n  });\n  kernel.build();\n  const string = kernel.toString();\n  const kernel2 = new Function('return ' + string)()({ context, canvas });\n  const result = kernel2();\n  assert.equal(result[0], 1);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #263 toString single function - webgl', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl');\n  toString('webgl', context, canvas);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #263 toString single function - webgl2', () => {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('webgl2');\n  toString('webgl2', context, canvas);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #263 toString single function - headlessgl', () => {\n  toString('headlessgl', require('gl')(1, 1), null);\n});\n\ntest('Issue #263 toString single function - cpu', () => {\n  toString('cpu');\n});\n"
  },
  {
    "path": "test/issues/267-immutable-sub-kernels.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #267 kernel');\n\nfunction immutableKernelWithoutFloats(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (v) {\n    return v[this.thread.x] + 1;\n  }, {\n    output: [1],\n    immutable: true,\n    pipeline: true,\n    precision: 'unsigned',\n  });\n\n  // start with a value on CPU\n  const output1 = kernel([1]);\n  const result1 = output1.toArray()[0];\n\n  // reuse that output, simulating that this value will be monitored, and updated via the same kernel\n  // this is often used in neural networks\n  const output2 = kernel(output1);\n  const result2 = output2.toArray()[0];\n\n  const output3 = kernel(output2);\n  const result3 = output3.toArray()[0];\n\n  assert.equal(result1, 2);\n  assert.equal(result2, 3);\n  assert.equal(result3, 4);\n  gpu.destroy();\n}\n\ntest('Issue #267 immutable kernel output without floats - auto', () => {\n  immutableKernelWithoutFloats();\n});\n\ntest('Issue #267 immutable kernel output without floats - gpu', () => {\n  immutableKernelWithoutFloats('gpu');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable kernel output without floats - webgl', () => {\n  immutableKernelWithoutFloats('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable kernel output without floats - webgl2', () => {\n  immutableKernelWithoutFloats('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #267 immutable kernel output without floats - headlessgl', () => {\n  immutableKernelWithoutFloats('headlessgl');\n});\n\nfunction immutableKernelWithFloats(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function (v) {\n    return v[this.thread.x] + 1;\n  }, {\n    output: [1],\n    immutable: true,\n    pipeline: true,\n    precision: 'single',\n  });\n\n  // start with a value on CPU\n  // reuse that output, simulating that this value will be monitored, and updated via the same kernel\n  // this is often used in neural networks\n  const output1 = kernel([1]);\n  const output2 = kernel(output1);\n  const output3 = kernel(output2);\n\n  assert.equal(output1.toArray()[0], 2);\n  assert.equal(output2.toArray()[0], 3);\n  assert.equal(output3.toArray()[0], 4);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #267 immutable kernel output with floats - auto', () => {\n  immutableKernelWithFloats();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #267 immutable kernel output with floats - gpu', () => {\n  immutableKernelWithFloats('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable kernel output with floats - webgl', () => {\n  immutableKernelWithFloats('webgl');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable kernel output with floats - webgl2', () => {\n  immutableKernelWithFloats('webgl2');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isHeadlessGLSupported ? test : skip)('Issue #267 immutable kernel output with floats - headlessgl', () => {\n  immutableKernelWithFloats('headlessgl');\n});\n\n\ndescribe('issue #267 sub kernel');\n\nfunction immutableSubKernelsWithoutFloats(mode) {\n  function value1(value) {\n    return value + 1;\n  }\n\n  function value2(value) {\n    return value + 1;\n  }\n\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap(\n    {\n      valueOutput1: value1,\n      valueOutput2: value2\n    },\n    function (a, b) {\n      value1(a[this.thread.x]);\n      return value2(b[this.thread.x]);\n    },\n    {\n      output: [1],\n      immutable: true,\n      pipeline: true,\n      precision: 'unsigned',\n    }\n  );\n\n  // start with a value on CPU\n  const output1 = kernel([1], [2]);\n  const result1 = output1.valueOutput1.toArray()[0];\n\n  // reuse that output, simulating that this value will be monitored, and updated via the same kernel\n  // this is often used in neural networks\n  const output2 = kernel(output1.valueOutput1, output1.valueOutput2);\n  const result2 = output2.valueOutput1.toArray()[0];\n\n  const output3 = kernel(output2.valueOutput1, output2.valueOutput2);\n  const result3 = output3.valueOutput1.toArray()[0];\n\n  assert.equal(result1, 2);\n  assert.equal(result2, 3);\n  assert.equal(result3, 4);\n  gpu.destroy();\n}\n(GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable sub-kernel output - auto', () => {\n  immutableSubKernelsWithoutFloats();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable sub-kernel output - gpu', () => {\n  immutableSubKernelsWithoutFloats('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #267 immutable sub-kernel output - webgl', () => {\n  immutableSubKernelsWithoutFloats('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable sub-kernel output - webgl2', () => {\n  immutableSubKernelsWithoutFloats('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable sub-kernel output - headlessgl', () => {\n  immutableSubKernelsWithoutFloats('headlessgl');\n});\n\n\n\ndescribe('issue #267 sub kernels mixed');\nfunction immutableKernelsMixedWithoutFloats(mode) {\n  function value1(value) {\n    return value + 10;\n  }\n\n  function value2(value) {\n    return value + 50;\n  }\n\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernelMap(\n    {\n      valueOutput1: value1,\n      valueOutput2: value2,\n    },\n    function (a, b) {\n      value1(a[this.thread.x]);\n      return value2(b[this.thread.x]) + 100;\n    },\n    {\n      output: [1],\n      immutable: true,\n      pipeline: true,\n      precision: 'unsigned',\n    }\n  );\n\n  // start with a value on CPU\n  const output1 = kernel([10], [20]);\n\n  // reuse that output, simulating that this value will be monitored, and updated via the same kernel\n  // this is often used in neural networks\n  const output2 = kernel(output1.result, output1.valueOutput2);\n  const output3 = kernel(output2.result, output2.valueOutput2);\n\n  function toArray(value) {\n    return value.toArray ? value.toArray() : value;\n  }\n\n  assert.equal(toArray(output1.valueOutput1)[0], 20); // 10 + 10\n  assert.equal(toArray(output1.valueOutput2)[0], 70); // 20 + 50\n  assert.equal(toArray(output1.result)[0], 170); // (20 + 50) + 100\n\n  assert.equal(toArray(output2.valueOutput1)[0], 180); // 170 + 10\n  assert.equal(toArray(output2.valueOutput2)[0], 120); // 70 + 50\n  assert.equal(toArray(output2.result)[0], 220); // (70 + 50) + 100\n\n  assert.equal(toArray(output3.valueOutput1)[0], 230); // 220 + 10\n  assert.equal(toArray(output3.valueOutput2)[0], 170); // 120 + 50\n  assert.equal(toArray(output3.result)[0], 270); // (120 + 50) + 100\n\n  gpu.destroy();\n}\n\n(GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable kernel & sub-kernel output without floats - auto', () => {\n  immutableKernelsMixedWithoutFloats();\n});\n\n(GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable kernel & sub-kernel output without floats - gpu', () => {\n  immutableKernelsMixedWithoutFloats('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #267 immutable kernel & sub-kernel output without floats - webgl', () => {\n  immutableKernelsMixedWithoutFloats('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #267 immutable kernel & sub-kernel output without floats - webgl2', () => {\n  immutableKernelsMixedWithoutFloats('webgl2');\n});\n\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)('Issue #267 immutable kernel & sub-kernel output without floats - headlessgl', () => {\n  immutableKernelsMixedWithoutFloats('headlessgl');\n});\n\ntest('Issue #267 immutable kernel & sub-kernel output without floats - cpu', () => {\n  immutableKernelsMixedWithoutFloats('cpu');\n});\n"
  },
  {
    "path": "test/issues/270-cache.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { WebGLKernel } = require('../../src');\n\ndescribe('issue # 270');\n\ntest('Issue #270 WebGlKernel getUniformLocation caches falsey - gpu', () => {\n  const canvas = {};\n  const context = {\n    getUniformLocation() {\n      throw new Error('tried to get getUniformLocation when falsey');\n    }\n  };\n  const kernel = new WebGLKernel('function() {}', { canvas, context });\n  kernel.programUniformLocationCache.test = false;\n  assert.equal(kernel.getUniformLocation('test'), false);\n});\n"
  },
  {
    "path": "test/issues/279-wrong-canvas-size.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #279');\n\nconst WIDTH = 600;\nconst HEIGHT =   400;\nfunction wrongCanvasSizeOptimized(mode) {\n  const gpu = new GPU({ mode });\n\n  const initMatrix = gpu.createKernel(function(value) {\n    return value;\n  })\n    .setOptimizeFloatMemory(true)\n    .setOutput([WIDTH, HEIGHT]);\n\n  const render = gpu.createKernel(function(matrix) {\n    const i = matrix[this.thread.y][this.thread.x];\n    this.color(i, i, i, 1);\n  })\n    .setOutput([WIDTH, HEIGHT])\n    .setGraphical(true);\n\n  const matrix = initMatrix(0.5);\n  render(matrix);\n  const canvas = render.canvas;\n  assert.equal(canvas.width, WIDTH);\n  assert.equal(canvas.height, HEIGHT);\n  gpu.destroy();\n}\n\n(GPU.isCanvasSupported ? test : skip)('Issue #279 wrong canvas size optimized - cpu', () => {\n  wrongCanvasSizeOptimized('cpu');\n});\n\ntest('Issue #279 wrong canvas size optimized - gpu', () => {\n  wrongCanvasSizeOptimized('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #279 wrong canvas size optimized - webgl', () => {\n  wrongCanvasSizeOptimized('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #279 wrong canvas size optimized - webgl2', () => {\n  wrongCanvasSizeOptimized('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #279 wrong canvas size optimized - headlessgl', () => {\n  wrongCanvasSizeOptimized('headlessgl');\n});\n\n\nfunction wrongCanvasSizeUnoptimized(mode) {\n  const gpu = new GPU({ mode });\n\n  const initMatrix = gpu.createKernel(function(value) {\n    return value;\n  })\n    .setOptimizeFloatMemory(false)\n    .setOutput([WIDTH, HEIGHT]);\n\n  const render = gpu.createKernel(function(matrix) {\n    const i = matrix[this.thread.y][this.thread.x];\n    this.color(i, i, i, 1);\n  })\n    .setOutput([WIDTH, HEIGHT])\n    .setGraphical(true);\n\n  const matrix = initMatrix(0.5);\n  render(matrix);\n  const canvas = render.canvas;\n  assert.equal(canvas.width, WIDTH);\n  assert.equal(canvas.height, HEIGHT);\n  gpu.destroy();\n}\n\n(GPU.isCanvasSupported ? test : skip)('Issue #279 wrong canvas size unoptimized - cpu', () => {\n  wrongCanvasSizeUnoptimized('cpu');\n});\n\ntest('Issue #279 wrong canvas size unoptimized - gpu', () => {\n  wrongCanvasSizeUnoptimized('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #279 wrong canvas size unoptimized - webgl', () => {\n  wrongCanvasSizeUnoptimized('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #279 wrong canvas size unoptimized - webgl2', () => {\n  wrongCanvasSizeUnoptimized('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #279 wrong canvas size unoptimized - headlessgl', () => {\n  wrongCanvasSizeUnoptimized('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/300-nested-array-index.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #300');\n\nfunction nestedArrayIndex(mode) {\n  const gpu1 = new GPU({ mode });\n  const gpu2 = new GPU({ mode });\n\n  // these 2 should be equivalent\n  const broken = gpu1.createKernel(function(input, lookup) {\n    return lookup[input[this.thread.x]];\n  })\n    .setOutput([1]);\n\n  const working = gpu2.createKernel(function(input, lookup) {\n    const idx = input[this.thread.x];\n    return lookup[idx];\n  })\n    .setOutput([1]);\n\n  assert.equal(broken([2], [7, 13, 19, 23])[0], 19);\n  assert.equal(working([2], [7, 13, 19, 23])[0], 19);\n\n  gpu1.destroy();\n  gpu2.destroy();\n}\n\ntest('Issue #300 nested array index - auto', () => {\n  nestedArrayIndex();\n});\n\ntest('Issue #300 nested array index - gpu', () => {\n  nestedArrayIndex('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #300 nested array index - webgl', () => {\n  nestedArrayIndex('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #300 nested array index - webgl2', () => {\n  nestedArrayIndex('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #300 nested array index - headlessgl', () => {\n  nestedArrayIndex('headlessgl');\n});\n\ntest('Issue #300 nested array index - cpu', () => {\n  nestedArrayIndex('cpu');\n});\n"
  },
  {
    "path": "test/issues/31-nested-var-declare-test.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, FunctionBuilder, WebGLFunctionNode, WebGL2FunctionNode, CPUFunctionNode } = require('../../src');\n\ndescribe('issue #31 redeclare');\n\n// nested redeclare\nfunction nestedVarRedeclareFunction() {\n  let result = 0;\n\n  // outer loop limit is effectively skipped in CPU\n  for(let i=0; i<10; ++i) {\n    // inner loop limit should be higher, to avoid infinite loops\n    for(i=0; i<20; ++i) {\n      result += 1;\n    }\n  }\n\n  return result;\n}\n\nfunction nestedVarRedeclareTest(mode) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(nestedVarRedeclareFunction, {\n    output: [1],\n  });\n  assert.throws(() => {\n    f();\n  });\n  gpu.destroy();\n}\n\ntest('Issue #31 - nestedVarRedeclare auto', () => {\n  nestedVarRedeclareTest(null);\n});\n\ntest('Issue #31 - nestedVarRedeclare gpu', () => {\n  nestedVarRedeclareTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #31 - nestedVarRedeclare webgl', () => {\n  nestedVarRedeclareTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #31 - nestedVarRedeclare webgl2', () => {\n  nestedVarRedeclareTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #31 - nestedVarRedeclare headlessgl', () => {\n  nestedVarRedeclareTest('headlessgl');\n});\n\ntest('Issue #31 - nestedVarRedeclare cpu', () => {\n  nestedVarRedeclareTest('cpu');\n});\n\ntest('Issue #31 - nestedVarRedeclare : AST handling webgl', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new WebGLFunctionNode(nestedVarRedeclareFunction.toString(), { output: [1] })],\n    output: [1]\n  });\n  assert.throws(() => {\n    builder.getStringFromFunctionNames(['nestedVarRedeclareFunction']);\n  });\n});\n\ntest('Issue #31 - nestedVarRedeclare : AST handling webgl2', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new WebGL2FunctionNode(nestedVarRedeclareFunction.toString(), { output: [1] })],\n    output: [1]\n  });\n  assert.throws(() => {\n    builder.getStringFromFunctionNames(['nestedVarRedeclareFunction']);\n  });\n});\n\ntest('Issue #31 - nestedVarRedeclare : AST handling cpu', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new CPUFunctionNode(nestedVarRedeclareFunction.toString(), { output: [1] })],\n    output: [1]\n  });\n  assert.throws(() => {\n    builder.getStringFromFunctionNames(['nestedVarRedeclareFunction']);\n  });\n});\n\n\ndescribe('issue #31 nested declare');\n// nested declare\nfunction nestedVarDeclareFunction() {\n  let result = 0.0;\n\n  // outer loop limit is effectively skipped in CPU\n  for(let i=0; i<10; ++i) {\n    // inner loop limit should be higher, to avoid infinite loops\n    for(let i=0; i<20; ++i) {\n      result += 1;\n    }\n  }\n\n  return result;\n}\n\nfunction nestedVarDeclareTest(mode ) {\n  const gpu = new GPU({ mode });\n  const f = gpu.createKernel(nestedVarDeclareFunction, {\n    output : [1]\n  });\n\n  assert.equal(f(), 200, 'basic return function test');\n  gpu.destroy();\n}\n\ntest('Issue #31 - nestedVarDeclare auto', () => {\n  nestedVarDeclareTest(null);\n});\n\ntest('Issue #31 - nestedVarDeclare gpu', () => {\n  nestedVarDeclareTest('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #31 - nestedVarDeclare webgl', () => {\n  nestedVarDeclareTest('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #31 - nestedVarDeclare webgl2', () => {\n  nestedVarDeclareTest('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #31 - nestedVarDeclare headlessgl', () => {\n  nestedVarDeclareTest('headlessgl');\n});\n\ntest('Issue #31 - nestedVarDeclare cpu', () => {\n  nestedVarDeclareTest('cpu');\n});\n\ntest('Issue #31 - nestedVarDeclare : AST handling webgl', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new WebGLFunctionNode(nestedVarDeclareFunction.toString(), { output: [1] })]\n  });\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['nestedVarDeclareFunction']),\n    'float nestedVarDeclareFunction() {'\n    + '\\nfloat user_result=0.0;'\n    + '\\nfor (int user_i=0;(user_i<10);++user_i){'\n    + '\\nfor (int user_i=0;(user_i<20);++user_i){' //<-- Note: don't do this in real life!\n    + '\\nuser_result+=1.0;}'\n    + '\\n}'\n    + '\\n'\n    + '\\nreturn user_result;'\n    + '\\n}'\n  );\n});\n\ntest('Issue #31 - nestedVarDeclare : AST handling webgl2', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new WebGL2FunctionNode(nestedVarDeclareFunction.toString(), { output: [1] })]\n  });\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['nestedVarDeclareFunction']),\n    'float nestedVarDeclareFunction() {'\n    + '\\nfloat user_result=0.0;'\n    + '\\nfor (int user_i=0;(user_i<10);++user_i){'\n    + '\\nfor (int user_i=0;(user_i<20);++user_i){' //<-- Note: don't do this in real life!\n    + '\\nuser_result+=1.0;}'\n    + '\\n}'\n    + '\\n'\n    + '\\nreturn user_result;'\n    + '\\n}'\n  );\n});\n\ntest('Issue #31 - nestedVarDeclare : AST handling cpu', () => {\n  const builder = new FunctionBuilder({\n    functionNodes: [new CPUFunctionNode(nestedVarDeclareFunction.toString(), { output: [1] })]\n  });\n\n  assert.equal(\n    builder.getStringFromFunctionNames(['nestedVarDeclareFunction']),\n    'function nestedVarDeclareFunction() {'\n    + '\\nlet user_result=0;'\n    + '\\nfor (let user_i=0;(user_i<10);++user_i){'\n    + '\\nfor (let user_i=0;(user_i<20);++user_i){'\n    + '\\nuser_result+=1;}'\n    + '\\n}'\n    + '\\n'\n    + '\\nreturn user_result;'\n    + '\\n}'\n  );\n});\n"
  },
  {
    "path": "test/issues/313-variable-lookup.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #313');\n\nfunction variableLookup(mode) {\n  function mult2(scale) {\n    return 2*scale;\n  }\n\n  const gpu = new GPU({\n    mode,\n    functions: [mult2]\n  });\n\n  const render1 = gpu.createKernel(function(input) {\n    return (mult2(input) + mult2(input*2) + mult2(input*1))  // RIGHT\n  })\n    .setOutput([1]);\n\n  const render2 = gpu.createKernel(function(input) {\n    return (mult2(input) + mult2(input*2) + mult2(input)); // WRONG\n  })\n    .setOutput([1]);\n\n  assert.equal(render1(1)[0], 8, 'render1 equals 8');\n  assert.equal(render2(1)[0], 8, 'render2 equals 8');\n  gpu.destroy();\n}\ntest('Issue #313 Mismatch argument lookup - auto', () => {\n  variableLookup();\n});\ntest('Issue #313 Mismatch argument lookup - gpu', () => {\n  variableLookup('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)('Issue #313 Mismatch argument lookup - webgl', () => {\n  variableLookup('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('Issue #313 Mismatch argument lookup - webgl2', () => {\n  variableLookup('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #313 Mismatch argument lookup - headlessgl', () => {\n  variableLookup('headlessgl');\n});\ntest('Issue #313 Mismatch argument lookup - cpu', () => {\n  variableLookup('cpu');\n});\n"
  },
  {
    "path": "test/issues/314-large-input-array-addressing.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, WebGLKernel, HeadlessGLKernel } = require('../../src');\n\ndescribe('issue #314');\n\n// max size of ok addressing was 8388608, 8388609 is shifted by 1 so index seems to be 8388610\n// after this fix max addressing is 2^31 which is the max a int32 can handle\n// run out of heap before being able to create a butter that big!\n// wanted to use uints but caused more problems than it solved\nconst DATA_MAX = (GPU.isHeadlessGLSupported ? HeadlessGLKernel : WebGLKernel).features.maxTextureSize*8;\nconst divisor = 100;\nconst data = new Uint16Array(DATA_MAX);\nlet v = 0;\nfor (let i = 0; i < DATA_MAX/divisor; i++) {\n  for (let j = 0; j < divisor; j++) {\n    data[i*divisor + j] = v++;\n  }\n}\nfunction buildLargeArrayAddressKernel(mode) {\n  const gpu = new GPU({ mode });\n  const largeArrayAddressKernel = gpu.createKernel(function(data) {\n    return data[this.thread.x];\n  }, {\n    precision: 'unsigned',\n  })\n    .setOutput([DATA_MAX]);\n\n  const result = largeArrayAddressKernel(data);\n\n  let same = true;\n  let i = 0;\n  for (; i < DATA_MAX; i++) {\n    if (result[i] !== data[i]) {\n      same = false;\n      break;\n    }\n  }\n  assert.ok(same, \"not all elements are the same, failed on index:\" + i);\n  gpu.destroy();\n}\n\ntest('Issue #314 Large array addressing - auto', () => {\n  buildLargeArrayAddressKernel(null);\n});\n\ntest('Issue #314 Large array addressing - gpu', () => {\n  buildLargeArrayAddressKernel('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #314 Large array addressing - webgl', () => {\n  buildLargeArrayAddressKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #314 Large array addressing - webgl2', () => {\n  buildLargeArrayAddressKernel('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #314 Large array addressing - headlessgl', () => {\n  buildLargeArrayAddressKernel('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/335-missing-z-index-issue.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #335');\n\nfunction missingZIndexIssue(mode) {\n  const gpu = new GPU({ mode });\n\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.z][this.thread.y][this.thread.x];\n  })\n    .setOutput([1, 1, undefined]);\n\n  kernel([[[1]]]);\n  gpu.destroy();\n}\n\ntest('Issue #335 Missing z index issue auto', () => {\n  assert.throws(() => {\n    missingZIndexIssue('auto');\n  });\n});\n\ntest('Issue #335 Missing z index issue gpu', () => {\n  assert.throws(() => {\n    missingZIndexIssue('gpu');\n  });\n});\n\ntest('Issue #335 Missing z index issue webgl', () => {\n  assert.throws(() => {\n    missingZIndexIssue('webgl');\n  });\n});\n\ntest('Issue #335 Missing z index issue webgl2', () => {\n  assert.throws(() => {\n    missingZIndexIssue('webgl2');\n  });\n});\n\ntest('Issue #335 Missing z index issue cpu', () => {\n  assert.throws(() => {\n    missingZIndexIssue('cpu');\n  });\n});\n"
  },
  {
    "path": "test/issues/346-uint8array-converted.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #346');\n\nconst DATA_MAX = 1024;\nconst uint8data = new Uint8Array(DATA_MAX);\nconst uint16data = new Uint16Array(DATA_MAX);\n\nfor (let i = 0; i < DATA_MAX; i++) {\n  uint8data[i] = Math.random() * 255;\n  uint16data[i] = Math.random() * 255 * 255;\n}\nfunction buildUintArrayInputKernel(mode, data) {\n  const gpu = new GPU({ mode });\n  const largeArrayAddressKernel = gpu.createKernel(function(data) {\n    return data[this.thread.x];\n  }, { precision: 'unsigned' })\n    .setOutput([DATA_MAX]);\n\n  const result = largeArrayAddressKernel(data);\n  let same = true;\n  let i = 0;\n  for (; i < DATA_MAX; i++) {\n    if (result[i] !== data[i]) {\n      same = false;\n      break;\n    }\n  }\n  assert.ok(same, \"not all elements are the same, failed on index:\" + i);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #346 uint8 input array - webgl', () => {\n  buildUintArrayInputKernel('webgl', uint8data);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #346 uint8 input array - webgl2', () => {\n  buildUintArrayInputKernel('webgl2', uint8data);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #346 uint8 input array - headlessgl', () => {\n  buildUintArrayInputKernel('headlessgl', uint8data);\n});\n\n(GPU.isWebGLSupported ? test : skip)('Issue #346 uint16 input array - webgl', () => {\n  buildUintArrayInputKernel('webgl', uint16data);\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #346 uint16 input array - webgl2', () => {\n  buildUintArrayInputKernel('webgl2', uint16data);\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #346 uint16 input array - headlessgl', () => {\n  buildUintArrayInputKernel('headlessgl', uint16data);\n});\n"
  },
  {
    "path": "test/issues/349-division-by-factors-of-3.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #349 divide by 3');\n\nfunction testDivideByThree(mode) {\n  const gpu = new GPU({mode});\n  const k = gpu.createKernel(function (v1, v2) {\n    return v1 / v2;\n  }, {\n    output: [1],\n    precision: 'single'\n  });\n  assert.equal(k(6, 3)[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - divide by three auto', () => {\n  testDivideByThree();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - divide by three gpu', () => {\n  testDivideByThree('gpu');\n});\n\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Issue #349 - divide by three webgl', () => {\n  testDivideByThree('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #349 - divide by three webgl2', () => {\n  testDivideByThree('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #349 - divide by three headlessgl', () => {\n  testDivideByThree('headlessgl');\n});\n\ntest('Issue #349 - divide by three cpu', () => {\n  testDivideByThree('cpu');\n});\n\n\ndescribe('issue #349 divide by random numbers');\nfunction someRandomWholeNumberDivisions(mode) {\n  const DATA_MAX = 1024 * 1024;\n  const dividendData = new Float32Array(DATA_MAX);\n  const divisorData = new Float32Array(DATA_MAX);\n  const expectedResults = new Float32Array(DATA_MAX);\n  const maxWholeNumberRepresentation = Math.sqrt(16777217);\n  for (let i = 0; i < DATA_MAX; i++) {\n    divisorData[i] = parseInt(Math.random() * maxWholeNumberRepresentation + 1, 10);\n    expectedResults[i] = parseInt(Math.random() * maxWholeNumberRepresentation + 1, 10);\n    dividendData[i] = divisorData[i] * expectedResults[i];\n  }\n  const gpu = new GPU({mode});\n  const k = gpu.createKernel(function (v1, v2) {\n    return v1[this.thread.x] / v2[this.thread.x];\n  }, {\n    output: [DATA_MAX],\n    precision: 'single'\n  });\n  const result = k(dividendData, divisorData);\n  let same = true;\n  let i = 0;\n  for (; i < DATA_MAX; i++) {\n    if (result[i] !== expectedResults[i]) {\n      same = false;\n      break;\n    }\n  }\n  assert.ok(same, same ? \"\" : \"not all elements are the same, failed on index:\" + i + \" \" + dividendData[i] + \"/\" + divisorData[i]);\n  gpu.destroy();\n}\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - some random whole number divisions auto', () => {\n  someRandomWholeNumberDivisions();\n});\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - some random whole number divisions gpu', () => {\n  someRandomWholeNumberDivisions('gpu');\n});\n(GPU.isSinglePrecisionSupported && GPU.isWebGLSupported ? test : skip)('Issue #349 - some random whole number divisions webgl', () => {\n  someRandomWholeNumberDivisions('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)('Issue #349 - some random whole number divisions webgl2', () => {\n  someRandomWholeNumberDivisions('webgl2');\n});\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #349 - some random whole number divisions headlessgl', () => {\n  someRandomWholeNumberDivisions('headlessgl');\n});\ntest('Issue #349 - some random whole number divisions cpu', () => {\n  someRandomWholeNumberDivisions('cpu');\n});\n\n\ndescribe('issue #349 disable integer division bug');\nfunction testDisableFixIntegerDivisionBug(mode) {\n  const gpu = new GPU({mode});\n  const idFix = gpu.createKernel(function(v1, v2) {\n    return v1 / v2;\n  }, { precision: 'single', output: [1] });\n\n  const idDixOff = gpu.createKernel(function(v1, v2) {\n    return v1 / v2;\n  }, {\n    output: [1],\n    precision: 'single',\n    fixIntegerDivisionAccuracy: false\n  });\n\n  if (!gpu.Kernel.features.isIntegerDivisionAccurate) {\n    assert.ok(\n      (\n        idFix(6, 3)[0] === 2\n        && idFix(6030401, 3991)[0] === 1511\n      ) && (\n        idDixOff(6, 3)[0] !== 2\n        || idDixOff(6030401, 3991)[0] !== 1511\n      ), \"when bug is present should show bug!\");\n  } else {\n    assert.ok(idFix(6, 3)[0] === 2 && idDixOff(6, 3)[0] === 2, \"when bug isn't present should not show bug!\");\n  }\n  gpu.destroy();\n}\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - test disable fix integer division bug auto', () => {\n  testDisableFixIntegerDivisionBug();\n});\n\n(GPU.isSinglePrecisionSupported ? test : skip)('Issue #349 - test disable fix integer division bug gpu', () => {\n  testDisableFixIntegerDivisionBug('gpu');\n});\n\n(GPU.isSinglePrecisionSupported  && GPU.isWebGLSupported ? test : skip)('Issue #349 - test disable fix integer division bug webgl', () => {\n  testDisableFixIntegerDivisionBug('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #349 - test disable fix integer division bug webgl2', () => {\n  testDisableFixIntegerDivisionBug('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #349 - test disable fix integer division bug headlessgl', () => {\n  testDisableFixIntegerDivisionBug('headlessgl');\n});\n\ntest('Issue #349 - test disable fix integer division bug cpu', () => {\n  testDisableFixIntegerDivisionBug('cpu');\n});\n"
  },
  {
    "path": "test/issues/357-modulus-issue.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #357');\n\n// complimentary tests in features/arithmetic-operators.js & features/assignment-operators.js\nfunction testModKernel(mode) {\n  const gpu = new GPU({mode});\n  const nValues = 100;\n\n  const myFunc3 = gpu.createKernel(function(x) {\n    return x[this.thread.x % 3];\n  }).setOutput([nValues]);\n\n  const input = [1, 2, 3];\n  myFunc3(input);\n\n  const expected = new Float32Array(nValues);\n  for (let i = 0; i < nValues; i++) {\n    expected[i] = input[i % 3];\n  }\n  assert.deepEqual(myFunc3([1, 2, 3]), expected);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #357 - modulus issue webgl', () => {\n  testModKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #357 - modulus issue webgl2', () => {\n  testModKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #357 - modulus issue headlessgl', () => {\n  testModKernel('headlessgl');\n});\n\n"
  },
  {
    "path": "test/issues/359-addfunction-params-wrong.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #359');\n\nfunction testAddFunctionKernel(mode) {\n  const gpu = new GPU({mode});\n  function clcC(xx) {\n    return Math.abs(xx);\n  }\n  function intermediate(c1) {\n    return clcC(c1);\n  }\n\n  gpu.addFunction(clcC);\n  gpu.addFunction(intermediate);\n\n  const nestFunctionsKernel = gpu.createKernel(function() {\n    return intermediate(-1);\n  }, {\n    output: [1]\n  });\n\n  assert.equal(nestFunctionsKernel()[0], 1);\n\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #359 - addFunction calls addFunction issue webgl', () => {\n  testAddFunctionKernel('webgl')\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #359 - addFunction calls addFunction issue webgl2', () => {\n  testAddFunctionKernel('webgl2')\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #359 - addFunction calls addFunction issue headlessgl', () => {\n  testAddFunctionKernel('headlessgl')\n});\n"
  },
  {
    "path": "test/issues/378-only-first-iteration.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #378');\n\nfunction testOnlyFirstIterationSafari(mode) {\n  const gpu = new GPU({ mode: mode });\n  const conflictingName = 0.4;\n  const kernel = gpu.createKernel(function(iter) {\n    let sum = 0;\n    for(let i=2; i<iter; i++) {\n      sum = sum + i;\n    }\n    return 2*sum ; //+ this.thread.x;\n  })\n    .setOutput([10])\n    .setConstants({\n      conflictingName: conflictingName\n    });\n\n  const result = kernel(5);\n\n  assert.deepEqual(Array.from(result), [18,18,18,18,18,18,18,18,18,18]);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #378 - only first iteration safari webgl', () => {\n  testOnlyFirstIterationSafari('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #378 - only first iteration safari webgl2', () => {\n  testOnlyFirstIterationSafari('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #378 - only first iteration safari headlessgl', () => {\n  testOnlyFirstIterationSafari('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/382-bad-constant.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #382');\n\nfunction testModKernel(mode) {\n  const gpu = new GPU({ mode: mode });\n  const conflictingName = 0.4;\n  const kernel = gpu.createKernel(function(a, conflictingName) {\n    return a[this.thread.x] + this.constants.conflictingName + conflictingName;\n  })\n    .setOutput([1])\n    .setConstants({\n      conflictingName: conflictingName\n    });\n\n  const result = kernel([1], 0.6);\n\n  assert.equal(result[0], 2);\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #382 - bad constant webgl', () => {\n  testModKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #382 - bad constant webgl2', () => {\n  testModKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #382 - bad constant headlessgl', () => {\n  testModKernel('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/390-thread-assignment.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { WebGLFunctionNode, WebGL2FunctionNode, CPUFunctionNode } = require('../../src');\n\ndescribe('issue #390');\n\ntest('Issue #390 - thread assignment webgl', function(assert) {\n  const node = new WebGLFunctionNode(function assignThreadToVar() {\n    const x = this.thread.x;\n    const y = this.thread.y;\n    const sum = x + y;\n    return sum;\n  }.toString(), { output: [1], returnType: 'Number' });\n  assert.equal(node.toString(), 'float assignThreadToVar() {'\n    + '\\nfloat user_x=float(threadId.x);'\n    + '\\nfloat user_y=float(threadId.y);'\n    + '\\nfloat user_sum=(user_x+user_y);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const { x, y, sum } = node.contexts[1];\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Number');\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Number');\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\ntest('Issue #390 - thread assignment webgl2', function(assert) {\n  const node = new WebGL2FunctionNode(function assignThreadToVar() {\n    const x = this.thread.x;\n    const y = this.thread.y;\n    const sum = x + y;\n    return sum;\n  }.toString(), { output: [1], returnType: 'Number' });\n  assert.equal(node.toString(), 'float assignThreadToVar() {'\n    + '\\nfloat user_x=float(threadId.x);'\n    + '\\nfloat user_y=float(threadId.y);'\n    + '\\nfloat user_sum=(user_x+user_y);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const { x, y, sum } = node.contexts[1];\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Number');\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Number');\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\ntest('Issue #390 - thread assignment cpu', function(assert) {\n  const node = new CPUFunctionNode(function assignThreadToVar() {\n    const x = this.thread.x;\n    const y = this.thread.y;\n    const sum = x + y;\n    return sum;\n  }.toString(), { output: [1] });\n  assert.equal(node.toString(), 'function assignThreadToVar() {'\n    + '\\nconst user_x=_this.thread.x;'\n    + '\\nconst user_y=_this.thread.y;'\n    + '\\nconst user_sum=(user_x+user_y);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const { x, y, z, sum } = node.contexts[1];\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Integer');\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Integer');\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\n\ntest('Issue #390 (related) - output assignment webgl', function(assert) {\n  const node = new WebGLFunctionNode(function assignThreadToVar() {\n    const x = this.output.x;\n    const y = this.output.y;\n    const z = this.output.z;\n    const sum = x + y + z;\n    return sum;\n  }.toString(), {\n    output: [1,2,3]\n  });\n  assert.equal(node.toString(), 'float assignThreadToVar() {'\n    + '\\nfloat user_x=1.0;'\n    + '\\nfloat user_y=2.0;'\n    + '\\nfloat user_z=3.0;'\n    + '\\nfloat user_sum=((user_x+user_y)+user_z);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const { x, y, z, sum } = node.contexts[1];\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Number');\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Number');\n  assert.equal(z.name, 'z');\n  assert.equal(z.valueType, 'Number');\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\ntest('Issue #390 (related) - output assignment webgl2', function(assert) {\n  const node = new WebGL2FunctionNode(function assignThreadToVar() {\n    const x = this.output.x;\n    const y = this.output.y;\n    const z = this.output.z;\n    const sum = x + y + z;\n    return sum;\n  }.toString(), {\n    output: [1,2,3]\n  });\n  assert.equal(node.toString(), 'float assignThreadToVar() {'\n    + '\\nfloat user_x=1.0;'\n    + '\\nfloat user_y=2.0;'\n    + '\\nfloat user_z=3.0;'\n    + '\\nfloat user_sum=((user_x+user_y)+user_z);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const context = node.contexts[1];\n  const { x, y, z, sum } = context;\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Number');\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Number');\n  assert.equal(z.name, 'z');\n  assert.equal(z.valueType, 'Number');\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\ntest('Issue #390 (related) - output assignment cpu', function(assert) {\n  const node = new CPUFunctionNode(`function assignThreadToVar() {\n    const x = this.output.x;\n    const y = this.output.y;\n    const z = this.output.z;\n    const sum = x + y + z;\n    return sum;\n  }`, {\n    output: [1,2,3]\n  });\n  assert.equal(node.toString(), 'function assignThreadToVar() {'\n    + '\\nconst user_x=outputX;'\n    + '\\nconst user_y=outputY;'\n    + '\\nconst user_z=outputZ;'\n    + '\\nconst user_sum=((user_x+user_y)+user_z);'\n    + '\\nreturn user_sum;'\n    + '\\n}');\n  const context = node.contexts[1];\n  const { x, y, z, sum } = context;\n  assert.equal(context['@contextType'], 'const/let');\n\n  assert.equal(x.name, 'x');\n  assert.equal(x.valueType, 'Number');\n\n  assert.equal(y.name, 'y');\n  assert.equal(y.valueType, 'Number');\n\n  assert.equal(z.name, 'z');\n  assert.equal(z.valueType, 'Number');\n\n  assert.equal(sum.name, 'sum');\n  assert.equal(sum.valueType, 'Number');\n});\n\n"
  },
  {
    "path": "test/issues/396-combine-kernels-example.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #396 - combine kernels example');\n\nfunction combineKernelsExample(mode) {\n  const gpu = new GPU({ mode });\n  const add = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.x];\n  }).setOutput([5]);\n\n  const multiply = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] * b[this.thread.x];\n  }).setOutput([5]);\n\n  const superKernel = gpu.combineKernels(add, multiply, function(a, b, c) {\n    return multiply(add(a, b), c);\n  });\n\n  const result = superKernel([1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]);\n  assert.deepEqual(Array.from(result), [2,\n    8,\n    18,\n    32,\n    50\n  ]);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  combineKernelsExample();\n});\n\ntest('gpu', () => {\n  combineKernelsExample('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  combineKernelsExample('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  combineKernelsExample('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  combineKernelsExample('headlessgl');\n});\n\ntest('cpu', () => {\n  combineKernelsExample('cpu');\n});\n"
  },
  {
    "path": "test/issues/399-double-definition.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #399');\n\nfunction doubleDefinitionUnsignedPrecision(mode) {\n  const gpu = new GPU({ mode });\n  const toTexture = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    precision: 'unsigned',\n    output: [2],\n    pipeline: true,\n    hardcodeConstants: true,\n    immutable: true\n  });\n  // basically it doesn't die, but builds all the way through to webGL\n  assert.equal(toTexture([0, 1]).constructor.name, 'GLTextureUnsigned');\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported ? test : skip)('Issue #399 - double definition unsigned precision webgl', () => {\n  doubleDefinitionUnsignedPrecision('webgl')\n});\n\n(GPU.isWebGL2Supported ? test : skip)('Issue #399 - double definition unsigned precision webgl2', () => {\n  doubleDefinitionUnsignedPrecision('webgl2')\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('Issue #399 - double definition unsigned precision headlessgl', () => {\n  doubleDefinitionUnsignedPrecision('headlessgl')\n});\n\nfunction doubleDefinitionSinglePrecision(mode) {\n  const gpu = new GPU({ mode });\n  const toTexture = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    precision: 'single',\n    output: [2],\n    pipeline: true,\n    hardcodeConstants: true,\n    immutable: true\n  });\n  // basically it doesn't die, but builds all the way through to webGL\n  assert.equal(toTexture([0, 1]).constructor.name, 'GLTextureFloat');\n  gpu.destroy();\n}\n\n(GPU.isWebGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Issue #399 - double definition single precision webgl', () => {\n  doubleDefinitionSinglePrecision('webgl')\n});\n\n(GPU.isWebGL2Supported && GPU.isSinglePrecisionSupported ? test : skip)('Issue #399 - double definition single precision webgl2', () => {\n  doubleDefinitionSinglePrecision('webgl2')\n});\n\n(GPU.isHeadlessGLSupported && GPU.isSinglePrecisionSupported ? test : skip)('Issue #399 - double definition single precision headlessgl', () => {\n  doubleDefinitionSinglePrecision('headlessgl')\n});\n"
  },
  {
    "path": "test/issues/401-cpu-canvas-check.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, CPUKernel } = require('../../src');\n\ndescribe('issue #401');\n\ntest('Issue #401 - cpu no canvas graphical', function(assert) {\n  assert.throws(function() {\n    CPUKernel.prototype.build.apply({\n      setupConstants: function() {},\n      setupArguments: function() {},\n      validateSettings: function() {},\n      getKernelString: function() {},\n      translateSource: function() {},\n      buildSignature: function() {},\n      graphical: true,\n      output: [1],\n      canvas: null\n    }, []);\n  },\n    new Error('no canvas available for using graphical output'),\n    'throws when canvas is not available and using graphical output');\n});\n\ntest('Issue #401 - cpu no canvas', function(assert) {\n  CPUKernel.prototype.build.apply({\n    setupConstants: function() {},\n    setupArguments: function() {},\n    validateSettings: function() {},\n    getKernelString: function() {},\n    translateSource: function() {},\n    buildSignature: function() {},\n    graphical: false,\n    output: [1],\n    canvas: null\n  }, []);\n  assert.equal(true, true, 'ok when canvas is not available and not using graphical output');\n});\n"
  },
  {
    "path": "test/issues/410-if-statement.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #410 - if statement when unsigned on NVidia');\n\nfunction ifStatement(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a) {\n    const paramDenom = a[this.thread.x][1] - a[this.thread.x][0];\n    if(paramDenom === 0) {\n      return 100;\n    }\n    return 200;\n  })\n    .setPrecision('unsigned')\n    .setOutput([2]);\n\n  const result =\n    kernel(\n      [\n        [0, 0],\n        [0, 2]\n      ]\n    );\n\n  assert.deepEqual(Array.from(result), [100,200]);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  ifStatement();\n});\n\ntest('gpu', () => {\n  ifStatement('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  ifStatement('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  ifStatement('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  ifStatement('headlessgl');\n});\n\ntest('cpu', () => {\n  ifStatement('cpu');\n});\n"
  },
  {
    "path": "test/issues/422-warnings.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #422 - warnings');\n\nfunction warnings(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a, b) {\n    return a[this.thread.x] + b[this.thread.x];\n  }).setOutput([10]);\n  assert.deepEqual(Array.from(kernel([0,1,2,3,4,5,6,7,8,9], [0,1,2,3,4,5,6,7,8,9])), [0,2,4,6,8,10,12,14,16,18]);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  warnings();\n});\n\ntest('gpu', () => {\n  warnings('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  warnings('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  warnings('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  warnings('headlessgl');\n});\n\ntest('cpu', () => {\n  warnings('cpu');\n});\n"
  },
  {
    "path": "test/issues/470-modulus-wrong.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #470 - modulus wrong');\n\nfunction testModulusWrong(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(mod) {\n    return this.thread.x % mod;\n  }, {\n    output: [10],\n    argumentTypes: {\n      mod: 'Integer',\n    },\n  });\n\n  const result = kernel(6);\n  assert.equal(kernel.argumentTypes[0], 'Integer');\n  assert.equal(result[0], 0 % 6);\n  assert.equal(result[1], 1 % 6);\n  assert.equal(result[2], 2 % 6);\n  assert.equal(result[3], 3 % 6);\n  assert.equal(result[4], 4 % 6);\n  assert.equal(result[5], 5 % 6);\n  assert.equal(result[6], 6 % 6);\n  assert.equal(result[7], 7 % 6);\n  assert.equal(result[8], 8 % 6);\n  assert.equal(result[9], 9 % 6);\n}\n\ntest('auto', () => {\n  testModulusWrong();\n});\n\ntest('gpu', () => {\n  testModulusWrong('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testModulusWrong('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testModulusWrong('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testModulusWrong('headlessgl');\n});\n\ntest('cpu', () => {\n  testModulusWrong('cpu');\n});\n"
  },
  {
    "path": "test/issues/471-canvas-issue.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #471 - canvas issue');\n\nfunction testCanvasIssue(mode) {\n  const gpu = new GPU({mode});\n  const render = gpu\n    .createKernel(function () {\n      this.color(0, 0, 0, 1);\n    })\n    .setOutput([200, 200])\n    .setGraphical(true);\n\n  render();\n\n  assert.equal(render.canvas.constructor.name, 'HTMLCanvasElement');\n  gpu.destroy();\n}\n\n(GPU.isCanvasSupported ? test : skip)('auto', () => {\n  testCanvasIssue();\n});\n\n(GPU.isCanvasSupported ? test : skip)('gpu', () => {\n  testCanvasIssue('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testCanvasIssue('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testCanvasIssue('webgl2');\n});\n\n(GPU.isCanvasSupported ? test : skip)('cpu', () => {\n  testCanvasIssue('cpu');\n});\n"
  },
  {
    "path": "test/issues/472-compilation-issue.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #472 - compilation issue');\n\nfunction testCompilationIssue(mode) {\n  const gpu = new GPU({ mode });\n  const kernelFunction = function(data, wobble) {\n    let x = this.thread.x,\n      y = this.thread.y;\n\n    x = Math.floor(x + wobble * Math.sin(y / 10));\n    y = Math.floor(y + wobble * Math.cos(x / 10));\n\n    const n = 4 * (x + this.constants.w * (this.constants.h - y));\n    this.color(data[n] / 256, data[n + 1] / 256, data[n + 2] / 256, 1);\n  };\n  const render = gpu.createKernel(kernelFunction, {\n    constants: { w: 4, h: 4 },\n    output: [2, 2],\n    graphical: true,\n  });\n  render(new Uint8ClampedArray([\n    230,233,240,255,\n    231,234,241,255,\n    232,235,242,255,\n    233,236,243,255\n  ]), 14 * Math.sin(Date.now() / 400));\n  assert.equal(render.getPixels().length, 2 * 2 * 4);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testCompilationIssue();\n});\n\ntest('gpu', () => {\n  testCompilationIssue('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testCompilationIssue('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testCompilationIssue('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testCompilationIssue('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('cpu', () => {\n  testCompilationIssue('cpu');\n});\n"
  },
  {
    "path": "test/issues/473-4-pixels.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #473 - only 4 pixels are shown');\n\nfunction testOnly4PixelsAreShownRGBStaticOutput(mode) {\n  const gpu = new GPU({ mode });\n  const render = gpu.createKernel(\n    function() {\n      this.color(1, 1, 1);\n    },\n    {\n      output: [20, 20],\n      graphical: true,\n    }\n  );\n\n  render();\n\n  const pixels = render.getPixels();\n  assert.equal(pixels.length, 20 * 20 * 4);\n  assert.equal(pixels.filter(v => v === 255).length, 20 * 20 * 4);\n  gpu.destroy();\n}\n\ntest('RGB static output auto', () => {\n  testOnly4PixelsAreShownRGBStaticOutput();\n});\n\ntest('RGB static output gpu', () => {\n  testOnly4PixelsAreShownRGBStaticOutput('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('RGB static output webgl', () => {\n  testOnly4PixelsAreShownRGBStaticOutput('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('RGB static output webgl2', () => {\n  testOnly4PixelsAreShownRGBStaticOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('RGB static output headlessgl', () => {\n  testOnly4PixelsAreShownRGBStaticOutput('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('RGB static output cpu', () => {\n  testOnly4PixelsAreShownRGBStaticOutput('cpu');\n});\n\nfunction testOnly4PixelsAreShownRGBAStaticOutput(mode) {\n  const gpu = new GPU({ mode });\n  const render = gpu.createKernel(\n    function() {\n      this.color(1, 1, 1, 1);\n    },\n    {\n      output: [20, 20],\n      graphical: true,\n    }\n  );\n\n  render();\n\n  const pixels = render.getPixels();\n  assert.equal(pixels.length, 20 * 20 * 4);\n  assert.equal(pixels.filter(v => v === 255).length, 20 * 20 * 4);\n  gpu.destroy();\n}\n\ntest('RGBA static output auto', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput();\n});\n\ntest('RGBA static output gpu', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('RGBA static output webgl', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('RGBA static output webgl2', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('RGBA static output headlessgl', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('RGBA static output cpu', () => {\n  testOnly4PixelsAreShownRGBAStaticOutput('cpu');\n});\n\nfunction testOnly4PixelsAreShownRGBDynamicOutput(mode) {\n  const gpu = new GPU({ mode });\n  const render = gpu.createKernel(\n    function() {\n      this.color(1, 1, 1);\n    },\n    {\n      output: [20, 20],\n      graphical: true,\n      dynamicOutput: true,\n    }\n  );\n\n  render();\n\n  const pixels = render.getPixels();\n  assert.equal(pixels.length, 20 * 20 * 4);\n  assert.equal(pixels.filter(v => v === 255).length, 20 * 20 * 4);\n\n  render.setOutput([10, 10]);\n  render();\n\n  const pixels2 = render.getPixels();\n  assert.equal(pixels2.length, 10 * 10 * 4);\n  assert.equal(pixels2.filter(v => v === 255).length, 10 * 10 * 4);\n  gpu.destroy();\n}\n\ntest('rgb dynamic output auto', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput();\n});\n\ntest('rgb dynamic output gpu', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('rgb dynamic output webgl', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('rgb dynamic output webgl2', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('rgb dynamic output headlessgl', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('rgb dynamic output cpu', () => {\n  testOnly4PixelsAreShownRGBDynamicOutput('cpu');\n});\n\nfunction testOnly4PixelsAreShownRGBADynamicOutput(mode) {\n  const gpu = new GPU({ mode });\n  const render = gpu.createKernel(\n    function() {\n      this.color(1, 1, 1, 1);\n    },\n    {\n      output: [20, 20],\n      graphical: true,\n      dynamicOutput: true,\n    }\n  );\n\n  render();\n\n  const pixels = render.getPixels();\n  assert.equal(pixels.length, 20 * 20 * 4);\n  assert.equal(pixels.filter(v => v === 255).length, 20 * 20 * 4);\n\n  render.setOutput([10, 10]);\n  render();\n\n  const pixels2 = render.getPixels();\n  assert.equal(pixels2.length, 10 * 10 * 4);\n  assert.equal(pixels2.filter(v => v === 255).length, 10 * 10 * 4);\n  gpu.destroy();\n}\n\ntest('rgba dynamic output auto', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput();\n});\n\ntest('rgba dynamic output gpu', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('rgba dynamic output webgl', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('rgba dynamic output webgl2', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('rgba dynamic output headlessgl', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput('headlessgl');\n});\n\n(GPU.isCanvasSupported ? test : skip)('rgba dynamic output cpu', () => {\n  testOnly4PixelsAreShownRGBADynamicOutput('cpu');\n});\n"
  },
  {
    "path": "test/issues/487-dynamic-arguments.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #487 - pipeline dynamic arguments');\n\nfunction testPipelineDynamicArguments(mode) {\n  const gpu = new GPU({mode: mode});\n\n  const kernel = gpu.createKernel(function (w) {\n    return this.thread.x + this.thread.y * w;\n  })\n    .setPipeline(true)\n    .setDynamicOutput(true);\n\n  const sumRow = gpu.createKernel(function (texture, w) {\n    let sum = 0;\n    for (let i = 0; i < w; i++)\n      sum = sum + texture[this.thread.x][i];\n    return sum;\n  })\n    .setDynamicArguments(true)\n    .setDynamicOutput(true);\n\n  function doAThing(w, h) {\n    kernel.setOutput([w, h]);\n    let intermediate = kernel(w);\n    const array = intermediate.toArray();\n    assert.equal(array.length, h);\n    assert.equal(array[0].length, w);\n    sumRow.setOutput([h]);\n    const result = sumRow(intermediate, w);\n    assert.equal(result.length, h);\n    assert.equal(result[0].length, undefined);\n  }\n\n  doAThing(10, 5);\n  doAThing(3, 2);\n  gpu.destroy();\n}\n\ntest('(GPU only) auto', () => {\n  testPipelineDynamicArguments();\n});\n\ntest('(GPU only) gpu', () => {\n  testPipelineDynamicArguments('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('(GPU only) webgl', () => {\n  testPipelineDynamicArguments('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('(GPU only) webgl2', () => {\n  testPipelineDynamicArguments('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('(GPU only) headlessgl', () => {\n  testPipelineDynamicArguments('headlessgl');\n});\n"
  },
  {
    "path": "test/issues/493-strange-literal.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #493 - strange literal');\n\nfunction testStrangeLiteral(mode) {\n  const gpu = new GPU({ mode });\n  function kernelFunction(array) {\n    const xFactor = (1 - 0) * this.constants.x + this.thread.x * this.constants.y;\n    const yFactor = (1 - .5) * this.constants.x + this.thread.x * this.constants.y;\n    const value = array[this.thread.x];\n    return [\n      value[0] / xFactor,\n      value[1] / yFactor,\n    ];\n  }\n  const kernel1 = gpu.createKernel(kernelFunction)\n    .setArgumentTypes({ array: 'Array1D(2)' })\n    .setConstants({ x: 1, y: 1})\n    .setOutput([1]);\n  assert.deepEqual(kernel1([[1,2]]), [new Float32Array([1,4])]);\n  const kernel2 = gpu.createKernel(kernelFunction)\n    .setStrictIntegers(true)\n    .setArgumentTypes({ array: 'Array1D(2)' })\n    .setConstants({ x: 1, y: 1})\n    .setOutput([1]);\n  assert.deepEqual(kernel2([[1,2]]), [new Float32Array([1,4])]);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testStrangeLiteral();\n});\n\ntest('gpu', () => {\n  testStrangeLiteral('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testStrangeLiteral('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testStrangeLiteral('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testStrangeLiteral('headlessgl');\n});\n\ntest('cpu', () => {\n  testStrangeLiteral('cpu');\n});\n"
  },
  {
    "path": "test/issues/500-sticky-arrays.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #500 - strange literal');\n\nfunction testStickyArrays(mode) {\n  const gpu = new GPU({ mode });\n  function processImage(image) {\n    return image[0];\n  }\n  gpu.addFunction(processImage);\n  const kernel = gpu.createKernel(function(image1, image2, image3) {\n    return [processImage(image1), processImage(image2), processImage(image3)];\n  }, { output: [1] });\n\n  assert.deepEqual(kernel([1], [2], [3]), [new Float32Array([1,2,3])]);\n}\n\ntest('auto', () => {\n  testStickyArrays();\n});\n\ntest('gpu', () => {\n  testStickyArrays('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testStickyArrays('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testStickyArrays('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testStickyArrays('headlessgl');\n});\n\ntest('cpu', () => {\n  testStickyArrays('cpu');\n});\n"
  },
  {
    "path": "test/issues/519-sanitize-names.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #519 - sanitize names');\n\nfunction testSanitizeNames(mode) {\n  const gpu = new GPU({ mode });\n  const kernel1 = gpu.createKernel(function (value__$, value__, value$, _) {\n    return value__$ + value__ + value$ + _ + 1;\n  }, {\n    output: [1]\n  });\n  assert.equal(kernel1(1, 2, 3, 4)[0], 11);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testSanitizeNames();\n});\n\ntest('gpu', () => {\n  testSanitizeNames('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testSanitizeNames('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testSanitizeNames('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testSanitizeNames('headlessgl');\n});\n\ntest('cpu', () => {\n  testSanitizeNames('cpu');\n});"
  },
  {
    "path": "test/issues/553-permanent-flip.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #553 - permanent flip');\n\nfunction testFixPermanentFlip(precision, mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(a1, a2, a3, a4) {\n    return a2[this.thread.x];\n  }, {\n    precision,\n    output: [4]\n  });\n  const arr = [1, 2, 3, 4];\n  for (let i = 0; i < 4; i++) {\n    assert.deepEqual(kernel(\n      999,\n      arr,\n      new Image(2, 2),\n      999,\n    ), new Float32Array(arr));\n  }\n\n  gpu.destroy();\n}\n\n// unsigned\n(typeof Image === 'undefined' ? skip : test)('auto unsigned', () => {\n  testFixPermanentFlip('unsigned');\n});\n\n(typeof Image === 'undefined' ? skip : test)('gpu unsigned', () => {\n  testFixPermanentFlip('unsigned', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl unsigned', () => {\n  testFixPermanentFlip('unsigned', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2 unsigned', () => {\n  testFixPermanentFlip('unsigned', 'webgl2');\n});\n\n(typeof Image === 'undefined' ? skip : test)('cpu unsigned', () => {\n  testFixPermanentFlip('unsigned', 'cpu');\n});\n\n// single\n(typeof Image === 'undefined' ? skip : test)('auto single', () => {\n  testFixPermanentFlip('single');\n});\n\n(typeof Image === 'undefined' ? skip : test)('gpu single', () => {\n  testFixPermanentFlip('single', 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl single', () => {\n  testFixPermanentFlip('single', 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2 single', () => {\n  testFixPermanentFlip('single', 'webgl2');\n});\n\n(typeof Image === 'undefined' ? skip : test)('cpu single', () => {\n  testFixPermanentFlip('single', 'cpu');\n});"
  },
  {
    "path": "test/issues/556-minify-for-loop.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU, WebGLFunctionNode } = require('../../src');\n\ndescribe('issue #556 - minify for loop');\n\nconst source = 'function w(t,e){for(var r=0,i=0;i<this.constants.size;i++)r+=t[this.thread.y][i]*e[i][this.thread.x];return r}';\n\nfunction testWebGLFunctionNode() {\n  const node = new WebGLFunctionNode(\n    source,\n    {\n      constantTypes: {\n        size: 'Number',\n      },\n      output: [1],\n      argumentNames: ['t', 'e'],\n      argumentTypes: ['Array', 'Array'],\n      lookupFunctionArgumentBitRatio: () => 4,\n      returnType: 'Number'\n    });\n\n  assert.equal(node.toString(), `float w(sampler2D user_t,ivec2 user_tSize,ivec3 user_tDim, sampler2D user_e,ivec2 user_eSize,ivec3 user_eDim) {\nfloat user_r=0.0;int user_i=0;\nfor (int safeI=0;safeI<LOOP_MAX;safeI++){\nif (!(user_i<int(constants_size))) break;\nuser_r+=(get32(user_t, user_tSize, user_tDim, 0, threadId.y, user_i)*get32(user_e, user_eSize, user_eDim, 0, user_i, threadId.x));\nuser_i++;}\n\nreturn user_r;\n}`);\n}\n\ntest('WebGLFunctionNode', () => {\n  testWebGLFunctionNode();\n});\n\nfunction testKernel(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(source, {\n    output: [1, 1],\n    constants: {\n      size: 1\n    },\n  });\n  const result = kernel([[1]], [[1]]);\n  assert.deepEqual(result, [new Float32Array([1])]);\n\n}\n\ntest('kernel auto', () => {\n  testKernel();\n});\n\ntest('kernel gpu', () => {\n  testKernel('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('kernel webgl', () => {\n  testKernel('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('kernel webgl2', () => {\n  testKernel('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('kernel headlessgl', () => {\n  testKernel('headlessgl');\n});\n\ntest('kernel cpu', () => {\n  testKernel('cpu');\n});"
  },
  {
    "path": "test/issues/560-minification-madness.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #560 - minification madness');\n\nfunction testMinificationMadness(mode, canvas) {\n  const gpu = new GPU({ mode, canvas });\n  const kernel = gpu.createKernel(function (t, e, i, n, r) {\n    for (\n      var o = this.constants.maxIter,\n        a = this.constants.canvasWidth,\n        s = this.constants.canvasHeight,\n        l = i + (n - i) * (this.thread.y / s),\n        c = t + (e - t) * (this.thread.x / a),\n        p = 0,\n        u = 0,\n        h = 0,\n        d = 0;\n      p * p + u * u < 4 && h < o\n      ;)\n      d = p * p - u * u + c,\n        u = 2 * p * u + l,\n        p = d,\n        h++;\n    h === o\n      ? this.color(0, 0, 0, 1)\n      : this.color(r[3 * h] / 255, r[3 * h + 1] / 255, r[3 * h + 2] / 255, 1);\n  }, {\n    output: [1, 1],\n    constants: {\n      maxIter: 1,\n      canvasWidth: 1,\n      canvasHeight: 1,\n    },\n    graphical: true,\n  });\n  kernel(1,2,3,4,[5]);\n  assert.ok(kernel.getPixels());\n  if (kernel.context && kernel.context.getError)\n  assert.ok(kernel.context.getError() === 0);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testMinificationMadness();\n});\n\ntest('gpu', () => {\n  testMinificationMadness('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testMinificationMadness('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testMinificationMadness('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testMinificationMadness('headlessgl');\n});\n\ntest('cpu', () => {\n  const mockData = [];\n  const result = true;\n  mockData.data = { set: () => {}, slice: () => result };\n  const mockPutImageData = () => {};\n  const mockContext = {\n    createImageData: () => mockData,\n    putImageData: mockPutImageData,\n  };\n  const mockCanvas = {\n    getContext: () => mockContext\n  };\n  testMinificationMadness('cpu', typeof HTMLCanvasElement === 'undefined' ? mockCanvas : null);\n});"
  },
  {
    "path": "test/issues/564-boolean.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #564 - boolean handled');\n\nfunction testBooleanHandled(fft, mode) {\n  const gpu = new GPU({ mode });\n  gpu.addNativeFunction('fft', fft, { returnType: 'Array(4)' });\n  const kernel = gpu.createKernel(\n    function(){\n      let s = true;\n      return fft(s);\n    },{\n      output:[1],\n    }\n  );\n  assert.deepEqual(Array.from(kernel()[0]), [1,1,1,1]);\n\n  gpu.destroy();\n}\n\nconst fft = `vec4 fft (bool horizontal){\n  return vec4(1,1,horizontal?1:0,1);\n}`;\ntest('auto', () => {\n  testBooleanHandled(fft);\n});\n\ntest('gpu', () => {\n  testBooleanHandled(fft, 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testBooleanHandled(fft, 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testBooleanHandled(fft, 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testBooleanHandled(fft, 'headlessgl');\n});\n\ntest('cpu', () => {\n  testBooleanHandled(`function fft(horizontal){\n  return [1,1,horizontal?1:0,1];\n}`, 'cpu');\n});"
  },
  {
    "path": "test/issues/567-wrong-modulus.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #567 - wrong modulus');\n\nfunction testWrongModulus(mode) {\n  const gpu = new GPU({ mode });\n  const kernel1 = gpu.createKernel(function () {\n    return 91 % 7;\n  }, {\n    output: [1]\n  });\n  assert.equal(kernel1()[0], 91 % 7);\n\n  const kernel2 = gpu.createKernel(function (value1, value2) {\n    return value1 % value2;\n  }, {\n    output: [1],\n  });\n  assert.equal(kernel2(91, 7)[0], 91 % 7);\n\n  const kernel3 = gpu.createKernel(function (value1, value2) {\n    return value1 % value2;\n  }, {\n    output: [1],\n  });\n  assert.equal(kernel3(91, 7)[0], 91 % 7);\n\n  const kernel4 = gpu.createKernel(function () {\n    return this.constants.value1 % this.constants.value2;\n  }, {\n    output: [1],\n    constants: {\n      value1: 91,\n      value2: 7,\n    }\n  });\n  assert.equal(kernel4()[0].toFixed(2), 91 % 7);\n\n  const kernel5 = gpu.createKernel(function () {\n    return 91 % this.constants.value;\n  }, {\n    output: [1],\n    constants: {\n      value: 7\n    },\n    strictIntegers: true\n  });\n  assert.equal(kernel5()[0], 91 % 7);\n\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testWrongModulus();\n});\n\ntest('gpu', () => {\n  testWrongModulus('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testWrongModulus('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testWrongModulus('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testWrongModulus('headlessgl');\n});\n\ntest('cpu', () => {\n  testWrongModulus('cpu');\n});"
  },
  {
    "path": "test/issues/585-inaccurate-lookups.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #585 - inaccurate lookups');\n\nfunction testResize(mode) {\n  const gpu = new GPU({ mode });\n  const kernel = gpu.createKernel(function(value) {\n    return value[this.thread.x];\n  }, {\n    output: [4],\n  });\n\n  const result = kernel([0,1,2,3]);\n  assert.equal(Math.round(result[0]), 0);\n  assert.equal(Math.round(result[1]), 1);\n  assert.equal(Math.round(result[2]), 2);\n  assert.equal(Math.round(result[3]), 3);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testResize();\n});\n\ntest('gpu', () => {\n  testResize('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testResize('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testResize('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testResize('headlessgl');\n});\n\ntest('cpu', () => {\n  testResize('cpu');\n});"
  },
  {
    "path": "test/issues/586-unable-to-resize.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #586 - unable to resize');\n\nfunction testResize(convert, mode) {\n  const gpu = new GPU({ mode });\n  const createTexture1 = gpu.createKernel(function() {\n    return 1;\n  }, { output: [2, 2], pipeline: false});\n\n  const createTexture2 = gpu.createKernel(function() {\n    return 1;\n  }, { output: [4, 4], pipeline: true});\n\n  var t1 = createTexture1();\n  var t2 = createTexture2();\n\n  assert.deepEqual(convert(t2), [\n    new Float32Array([1,1,1,1]),\n    new Float32Array([1,1,1,1]),\n    new Float32Array([1,1,1,1]),\n    new Float32Array([1,1,1,1]),\n  ]);\n\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testResize(t => t.toArray());\n});\n\ntest('gpu', () => {\n  testResize(t => t.toArray(), 'gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testResize(t => t.toArray(), 'webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testResize(t => t.toArray(), 'webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testResize(t => t.toArray(), 'headlessgl');\n});\n\ntest('cpu', () => {\n  testResize(a => a, 'cpu');\n});"
  },
  {
    "path": "test/issues/608-rewritten-arrays.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #608 - rewritten arrays');\n\nfunction testRewrittenArrays(mode) {\n  const gpu = new GPU({ mode });\n  const kernel1 = gpu.createKernel(function (a, b) {\n    return a[this.thread.y][this.thread.x];\n  }, {\n    constants: {\n      c: [21, 23]\n    },\n    output: [2, 2]\n  });\n  const kernel2 = gpu.createKernel(function (a, b) {\n    return b[this.thread.y][this.thread.x];\n  }, {\n    constants: {\n      c: [21, 23]\n    },\n    output: [2, 2]\n  });\n  const kernel3 = gpu.createKernel(function (a, b) {\n    return this.constants.c[this.thread.x];\n  }, {\n    constants: {\n      c: [21, 23]\n    },\n    output: [2, 2]\n  });\n  const a = [\n    [2, 3],\n    [5, 7]\n  ];\n  const b = [\n    [11, 13],\n    [17, 19]\n  ];\n  const cExpected = [\n    [21, 23],\n    [21, 23]\n  ];\n  // testing twice to ensure constants are reset\n  assert.deepEqual(kernel1(a, b).map(v => Array.from(v)), a);\n  assert.deepEqual(kernel2(a, b).map(v => Array.from(v)), b);\n  assert.deepEqual(kernel3(a, b).map(v => Array.from(v)), cExpected);\n\n  assert.deepEqual(kernel1(a, b).map(v => Array.from(v)), a);\n  assert.deepEqual(kernel2(a, b).map(v => Array.from(v)), b);\n  assert.deepEqual(kernel3(a, b).map(v => Array.from(v)), cExpected);\n  gpu.destroy();\n}\n\ntest('auto', () => {\n  testRewrittenArrays();\n});\n\ntest('gpu', () => {\n  testRewrittenArrays('gpu');\n});\n\n(GPU.isWebGLSupported ? test : skip)('webgl', () => {\n  testRewrittenArrays('webgl');\n});\n\n(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {\n  testRewrittenArrays('webgl2');\n});\n\n(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {\n  testRewrittenArrays('headlessgl');\n});\n\ntest('cpu', () => {\n  testRewrittenArrays('cpu');\n});\n"
  },
  {
    "path": "test/issues/91-create-kernel-map-array.js",
    "content": "const { assert, skip, test, module: describe } = require('qunit');\nconst { GPU, HeadlessGLKernel, WebGLKernel, WebGL2Kernel, CPUKernel } = require('../../src');\n\ndescribe('issue #91');\nfunction getResult(mode) {\n  const A = [\n    [1, 2],\n    [3, 4],\n    [5, 6]\n  ];\n\n  const B = [\n    [6, 5, 4],\n    [3, 2, 1]\n  ];\n\n  const gpu = new GPU({ mode });\n\n  function multiply(b, a, y, x) {\n    let sum = 0;\n    for (let i = 0; i < 2; i++) {\n      sum += b[y][i] * a[i][x];\n    }\n    return sum;\n  }\n\n  const kernels = gpu.createKernelMap({\n    multiplyResult: multiply\n  }, function (a, b) {\n    return multiply(b, a, this.thread.y, this.thread.x);\n  })\n    .setOutput([2, 2]);\n  const result = kernels(A, B).result;\n  assert.deepEqual(Array.from(result[0]), [21,32]);\n  assert.deepEqual(Array.from(result[1]), [9,14]);\n  gpu.destroy();\n  return kernels;\n}\n(GPU.isWebGL2Supported || (GPU.isHeadlessGLSupported && HeadlessGLKernel.features.kernelMap) ? test : skip)(\"Issue #91 - type detection auto\", () => {\n  getResult();\n});\n(GPU.isWebGL2Supported || (GPU.isHeadlessGLSupported && HeadlessGLKernel.features.kernelMap) ? test : skip)(\"Issue #91 - type detection gpu\", () => {\n  getResult('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)(\"Issue #91 - type detection webgl\", () => {\n  const kernel = getResult('webgl');\n  assert.equal(kernel.kernel.constructor, WebGLKernel, 'kernel type is wrong');\n});\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #91 - type detection webgl2\", () => {\n  const kernel = getResult('webgl2');\n  assert.equal(kernel.kernel.constructor, WebGL2Kernel, 'kernel type is wrong');\n});\n(GPU.isHeadlessGLSupported ? test : skip)(\"Issue #91 - type detection headlessgl\", () => {\n  const kernel = getResult('headlessgl');\n  assert.equal(kernel.kernel.constructor, HeadlessGLKernel, 'kernel type is wrong');\n});\ntest(\"Issue #91 - type detection cpu\", () => {\n  const kernel = getResult('cpu');\n  assert.equal(kernel.kernel.constructor, CPUKernel, 'kernel type is wrong');\n});\n"
  },
  {
    "path": "test/issues/96-param-names.js",
    "content": "const { assert, skip, test, module: describe, only } = require('qunit');\nconst { GPU } = require('../../src');\n\ndescribe('issue #96');\n\nfunction getResult(mode) {\n  const A = [\n    [1, 1, 1],\n    [1, 1, 1]\n  ];\n\n  const B = [\n    [1, 1],\n    [1, 1],\n    [1, 1]\n  ];\n\n  const gpu = new GPU({ mode });\n\n  function multiply(m, n, y, x) {\n    let sum = 0;\n    for (let i = 0; i < 2; i++) {\n      sum += m[y][i] * n[i][x];\n    }\n    return sum;\n  }\n\n  const kernels = gpu.createKernelMap({\n    multiplyResult: multiply\n  }, function (a, b) {\n    return multiply(b, a, this.thread.y, this.thread.x);\n  })\n    .setOutput([B.length, A.length]);\n\n  const result = kernels(A, B).result;\n  assert.deepEqual(Array.from(result[0]), [2,2,2]);\n  assert.deepEqual(Array.from(result[1]), [2,2,2]);\n  assert.deepEqual(result.length, 2);\n  gpu.destroy();\n  return result;\n}\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #96 - param names auto\", () => {\n  getResult();\n});\n(GPU.isKernelMapSupported ? test : skip)(\"Issue #96 - param names gpu\", () => {\n  getResult('gpu');\n});\n(GPU.isWebGLSupported ? test : skip)(\"Issue #96 - param names webgl\", () => {\n  getResult('webgl');\n});\n(GPU.isWebGL2Supported ? test : skip)(\"Issue #96 - param names webgl2\", () => {\n  getResult('webgl2');\n});\n(GPU.isHeadlessGLSupported && GPU.isKernelMapSupported ? test : skip)(\"Issue #96 - param names headlessgl\", () => {\n  getResult('headlessgl');\n});\ntest(\"Issue #96 - param names cpu\", () => {\n  getResult('cpu');\n});\n"
  },
  {
    "path": "test/test-utils.js",
    "content": "\nconst testUtils = {\n  /**\n   * A visual debug utility\n   * @param {GPU} gpu\n   * @param rgba\n   * @param width\n   * @param height\n   * @return {Object[]}\n   */\n  splitRGBAToCanvases: (gpu, rgba, width, height) => {\n    const visualKernelR = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(pixel.r / 255, 0, 0, 255);\n    }, { output: [width, height], graphical: true, argumentTypes: { v: 'Array2D(4)' } });\n    visualKernelR(rgba);\n\n    const visualKernelG = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, pixel.g / 255, 0, 255);\n    }, { output: [width, height], graphical: true, argumentTypes: { v: 'Array2D(4)' } });\n    visualKernelG(rgba);\n\n    const visualKernelB = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(0, 0, pixel.b / 255, 255);\n    }, { output: [width, height], graphical: true, argumentTypes: { v: 'Array2D(4)' } });\n    visualKernelB(rgba);\n\n    const visualKernelA = gpu.createKernel(function(v) {\n      const pixel = v[this.thread.y][this.thread.x];\n      this.color(255, 255, 255, pixel.a / 255);\n    }, { output: [width, height], graphical: true, argumentTypes: { v: 'Array2D(4)' } });\n    visualKernelA(rgba);\n\n    return [\n      visualKernelR.getPixels(),\n      visualKernelG.getPixels(),\n      visualKernelB.getPixels(),\n      visualKernelA.getPixels(),\n    ];\n  },\n};\n\nmodule.exports = testUtils;\n"
  }
]