/** * @license * Copyright 2023 Google LLC. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ import * as tf from '../index'; import { BROWSER_ENVS, describeWithFlags } from '../jasmine_util'; import { expectArraysClose, expectArraysEqual } from '../test_util'; function readPixelsFromCanvas(canvas, contextType, width, height) { let actualData; if (contextType === '2d') { const ctx = canvas.getContext(contextType); actualData = ctx.getImageData(0, 0, width, height).data; } else { const tmpCanvas = document.createElement('canvas'); tmpCanvas.width = width; tmpCanvas.height = height; const ctx = tmpCanvas.getContext('2d'); ctx.drawImage(canvas, 0, 0); actualData = new Uint8ClampedArray(ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data); } return actualData; } function convertToRGBA(data, shape, dtype, alpha = 1) { const [height, width] = shape.slice(0, 2); const depth = shape.length === 2 ? 1 : shape[2]; const multiplier = dtype === 'float32' ? 255 : 1; const bytes = new Uint8ClampedArray(width * height * 4); for (let i = 0; i < height * width; ++i) { const rgba = [0, 0, 0, 255 * alpha]; for (let d = 0; d < depth; d++) { const value = data[i * depth + d]; if (dtype === 'float32') { if (value < 0 || value > 1) { throw new Error(`Tensor values for a float32 Tensor must be in the ` + `range [0 - 1] but encountered ${value}.`); } } else if (dtype === 'int32') { if (value < 0 || value > 255) { throw new Error(`Tensor values for a int32 Tensor must be in the ` + `range [0 - 255] but encountered ${value}.`); } } if (depth === 1) { rgba[0] = value * multiplier; rgba[1] = value * multiplier; rgba[2] = value * multiplier; } else { rgba[d] = value * multiplier; } } const j = i * 4; bytes[j + 0] = Math.round(rgba[0]); bytes[j + 1] = Math.round(rgba[1]); bytes[j + 2] = Math.round(rgba[2]); bytes[j + 3] = Math.round(rgba[3]); } return bytes; } function drawAndReadback(contextType, data, shape, dtype, alpha = 1, canvasUsedAs2d = false) { const [height, width] = shape.slice(0, 2); let img; if (shape.length === 3) { img = tf.tensor3d(data, shape, dtype); } else { img = tf.tensor2d(data, shape, dtype); } const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; if (canvasUsedAs2d) { canvas.getContext('2d'); } const drawOptions = { contextOptions: { contextType }, imageOptions: { alpha } }; // tslint:disable-next-line:no-any tf.browser.draw(img, canvas, drawOptions); const actualData = readPixelsFromCanvas(canvas, contextType, width, height); const expectedData = convertToRGBA(data, shape, dtype, alpha); img.dispose(); return [actualData, expectedData]; } // CPU and GPU handle pixel value differently. The epsilon may possibly grow // after each draw and read back. const DRAW_EPSILON = 6.0; describeWithFlags('draw on canvas context', BROWSER_ENVS, (env) => { let contextType; beforeAll(async () => { await tf.setBackend(env.name); contextType = env.name === 'cpu' ? '2d' : env.name; }); it('draw image with 4 channels and int values', async () => { const data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]; const shape = [2, 2, 4]; const dtype = 'int32'; const startNumTensors = tf.memory().numTensors; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype); expect(tf.memory().numTensors).toEqual(startNumTensors); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw image with 4 channels and int values, alpha=0.5', async () => { const data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]; const shape = [2, 2, 4]; const dtype = 'int32'; const startNumTensors = tf.memory().numTensors; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype, 0.5); expect(tf.memory().numTensors).toEqual(startNumTensors); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw image with 4 channels and float values', async () => { const data = [.1, .2, .3, .4, .5, .6, .7, .8, .09, .1, .11, .12, .13, .14, .15, .16]; const shape = [2, 2, 4]; const dtype = 'float32'; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw image with 3 channels and int values', async () => { const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; const shape = [2, 2, 3]; const dtype = 'int32'; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype); expectArraysEqual(actualData, expectedData); }); it('draw image with 3 channels and int values, alpha=0.5', async () => { const data = [101, 32, 113, 14, 35, 76, 17, 38, 59, 70, 81, 92]; const shape = [2, 2, 3]; const dtype = 'int32'; const alpha = 0.5; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype, alpha); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw image with 3 channels and float values', async () => { const data = [.1, .2, .3, .4, .5, .6, .7, .8, .9, .1, .11, .12]; const shape = [2, 2, 3]; const dtype = 'float32'; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw 2D image in grayscale', async () => { const data = [100, 12, 90, 64]; const shape = [2, 2]; const dtype = 'int32'; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype); expectArraysEqual(actualData, expectedData); }); it('draw image with alpha=0.5', async () => { const data = [101, 212, 113, 14, 35, 76, 17, 38, 59, 70, 81, 92]; const shape = [6, 2, 1]; const dtype = 'int32'; const alpha = 0.5; const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype, alpha); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); it('draw image works when canvas has been used for 2d', async () => { const data = [101, 212, 113, 14, 35, 76, 17, 38, 59, 70, 81, 92]; const shape = [6, 2, 1]; const dtype = 'int32'; // Set canvasUsedAs2d to true so the canvas will be first used for 2d. const [actualData, expectedData] = drawAndReadback(contextType, data, shape, dtype, 1, true); expectArraysClose(actualData, expectedData, DRAW_EPSILON); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJhd190ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vdGZqcy1jb3JlL3NyYy9vcHMvZHJhd190ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBQyxZQUFZLEVBQUUsaUJBQWlCLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUNoRSxPQUFPLEVBQUMsaUJBQWlCLEVBQUUsaUJBQWlCLEVBQUMsTUFBTSxjQUFjLENBQUM7QUFFbEUsU0FBUyxvQkFBb0IsQ0FDekIsTUFBeUIsRUFBRSxXQUFtQixFQUFFLEtBQWEsRUFDN0QsTUFBYztJQUNoQixJQUFJLFVBQVUsQ0FBQztJQUNmLElBQUksV0FBVyxLQUFLLElBQUksRUFBRTtRQUN4QixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzNDLFVBQVUsR0FBRyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQztLQUN6RDtTQUFNO1FBQ0wsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNuRCxTQUFTLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUN4QixTQUFTLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUMxQixNQUFNLEdBQUcsR0FBRyxTQUFTLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXZDLEdBQUcsQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM1QixVQUFVLEdBQUcsSUFBSSxpQkFBaUIsQ0FDOUIsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEdBQUcs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RUFBRSxLQUFLLElBQUksRUFBRTtRQUMzRCxNQUFNLElBQUksR0FDTixDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzVFLE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN4QixNQUFNLEtBQUssR0FBRyxTQUFTLENBQUM7UUFDeEIsTUFBTSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsR0FDNUIsZUFBZSxDQUFDLFdBQVcsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3JELGlCQUFpQixDQUFDLFVBQVUsRUFBRSxZQUFZLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDNUQsQ0FBQyxDQUFDLENBQUM7SUFFSCxFQUFFLENBQUMsMkNBQTJDLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDekQsTUFBTSxJQUFJLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3JELE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN4QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUM7UUFDdEIsTUFBTSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsR0FDNUIsZUFBZSxDQUFDLFdBQVcsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3JELGlCQUFpQixDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQztJ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Q0FBQztJQUM1RCxDQUFDLENBQUMsQ0FBQztJQUVILEVBQUUsQ0FBQyxtREFBbUQsRUFBRSxLQUFLLElBQUksRUFBRTtRQUNqRSxNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDakUsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3hCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQztRQUN0QixzRUFBc0U7UUFDdEUsTUFBTSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsR0FDNUIsZUFBZSxDQUFDLFdBQVcsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDOUQsaUJBQWlCLENBQUMsVUFBVSxFQUFFLFlBQVksRUFBRSxZQUFZLENBQUMsQ0FBQztJQUM1RCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjMgR29vZ2xlIExMQy5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuLi9pbmRleCc7XG5pbXBvcnQge0JST1dTRVJfRU5WUywgZGVzY3JpYmVXaXRoRmxhZ3N9IGZyb20gJy4uL2phc21pbmVfdXRpbCc7XG5pbXBvcnQge2V4cGVjdEFycmF5c0Nsb3NlLCBleHBlY3RBcnJheXNFcXVhbH0gZnJvbSAnLi4vdGVzdF91dGlsJztcblxuZnVuY3Rpb24gcmVhZFBpeGVsc0Zyb21DYW52YXMoXG4gICAgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCwgY29udGV4dFR5cGU6IHN0cmluZywgd2lkdGg6IG51bWJlcixcbiAgICBoZWlnaHQ6IG51bWJlcikge1xuICBsZXQgYWN0dWFsRGF0YTtcbiAgaWYgKGNvbnRleHRUeXBlID09PSAnMmQnKSB7XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoY29udGV4dFR5cGUpO1xuICAgIGFjdHVhbERhdGEgPSBjdHguZ2V0SW1hZ2VEYXRhKDAsIDAsIHdpZHRoLCBoZWlnaHQpLmRhdGE7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgdG1wQ2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgdG1wQ2FudmFzLndpZHRoID0gd2lkdGg7XG4gICAgdG1wQ2FudmFzLmhlaWdodCA9IGhlaWdodDtcbiAgICBjb25zdCBjdHggPSB0bXBDYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcblxuICAgIGN0eC5kcmF3SW1hZ2UoY2FudmFzLCAwLCAwKTtcbiAgICBhY3R1YWxEYXRhID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KFxuICAgICAgICBjdHguZ2V0SW1hZ2VEYXRhKDAsIDAsIGN0eC5jYW52YXMud2lkdGgsIGN0eC5jYW52YXMuaGVpZ2h0KS5kYXRhKTtcbiAgfVxuICByZXR1cm4gYWN0dWFsRGF0YTtcbn1cblxuZnVuY3Rpb24gY29udmVydFRvUkdCQShcbiAgICBkYXRhOiBudW1iZXJbXSwgc2hhcGU6IG51bWJlcltdLCBkdHlwZTogc3RyaW5nLCBhbHBoYSA9IDEpIHtcbiAgY29uc3QgW2hlaWdodCwgd2lkdGhdID0gc2hhcGUuc2xpY2UoMCwgMik7XG4gIGNvbnN0IGRlcHRoID0gc2hhcGUubGVuZ3RoID09PSAyID8gMSA6IHNoYXBlWzJdO1xuICBjb25zdCBtdWx0aXBsaWVyID0gZHR5cGUgPT09ICdmbG9hdDMyJyA/IDI1NSA6IDE7XG4gIGNvbnN0IGJ5dGVzID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KHdpZHRoICogaGVpZ2h0ICogNCk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBoZWlnaHQgKiB3aWR0aDsgKytpKSB7XG4gICAgY29uc3QgcmdiYSA9IFswLCAwLCAwLCAyNTUgKiBhbHBoYV07XG5cbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyBkKyspIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gZGF0YVtpICogZGVwdGggKyBkXTtcblxuICAgICAgaWYgKGR0eXBlID09PSAnZmxvYXQzMicpIHtcbiAgICAgICAgaWYgKHZhbHVlIDwgMCB8fCB2YWx1ZSA+IDEpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgIGBUZW5zb3IgdmFsdWVzIGZvciBhIGZsb2F0MzIgVGVuc29yIG11c3QgYmUgaW4gdGhlIGAgK1xuICAgICAgICAgICAgICBgcmFuZ2UgWzAgLSAxXSBidXQgZW5jb3VudGVyZWQgJHt2YWx1ZX0uYCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZHR5cGUgPT09ICdpbnQzMicpIHtcbiAgICAgICAgaWYgKHZhbHVlIDwgMCB8fCB2YWx1ZSA+IDI1NSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgYFRlbnNvciB2YWx1ZXMgZm9yIGEgaW50MzIgVGVuc29yIG11c3QgYmUgaW4gdGhlIGAgK1xuICAgICAgICAgICAgICBgcmFuZ2UgWzAgLSAyNTVdIGJ1dCBlbmNvdW50ZXJlZCAke3ZhbHVlfS5gKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZGVwdGggPT09IDEpIHtcbiAgICAgICAgcmdiYVswXSA9IHZhbHVlICogbXVsdGlwbGllcjtcbiAgICAgICAgcmdiYVsxXSA9IHZhbHVlICogbXVsdGlwbGllcjtcbiAgICAgICAgcmdiYVsyXSA9IHZhbHVlICogbXVsdGlwbGllcjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJnYmFbZF0gPSB2YWx1ZSAqIG11bHRpcGxpZXI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgaiA9IGkgKiA0O1xuICAgIGJ5dGVzW2ogKyAwXSA9IE1hdGgucm91bmQocmdiYVswXSk7XG4gICAgYnl0ZXNbaiArIDFdID0gTWF0aC5yb3VuZChyZ2JhWzFdKTtcbiAgICBieXRlc1tqICsgMl0gPSBNYXRoLnJvdW5kKHJnYmFbMl0pO1xuICAgIGJ5dGVzW2ogKyAzXSA9IE1hdGgucm91bmQocmdiYVszXSk7XG4gIH1cbiAgcmV0dXJuIGJ5dGVzO1xufVxuXG5mdW5jdGlvbiBkcmF3QW5kUmVhZGJhY2soXG4gICAgY29udGV4dFR5cGU6IHN0cmluZywgZGF0YTogbnVtYmVyW10sIHNoYXBlOiBudW1iZXJbXSwgZHR5cGU6IHN0cmluZyxcbiAgICBhbHBoYSA9IDEsIGNhbnZhc1VzZWRBczJkID0gZmFsc2UpIHtcbiAgY29uc3QgW2hlaWdodCwgd2lkdGhdID0gc2hhcGUuc2xpY2UoMCwgMik7XG4gIGxldCBpbWc7XG4gIGlmIChzaGFwZS5sZW5ndGggPT09IDMpIHtcbiAgICBpbWcgPSB0Zi50ZW5zb3IzZChcbiAgICAgICAgZGF0YSwgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkdHlwZSBhcyBrZXlvZiB0Zi5EYXRhVHlwZU1hcCk7XG4gIH0gZWxzZSB7XG4gICAgaW1nID0gdGYudGVuc29yMmQoXG4gICAgICAgIGRhdGEsIHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIGR0eXBlIGFzIGtleW9mIHRmLkRhdGFUeXBlTWFwKTtcbiAgfVxuICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgY2FudmFzLndpZHRoID0gd2lkdGg7XG4gIGNhbnZhcy5oZWlnaHQgPSBoZWlnaHQ7XG4gIGlmIChjYW52YXNVc2VkQXMyZCkge1xuICAgIGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuICB9XG4gIGNvbnN0IGRyYXdPcHRpb25zID0ge2NvbnRleHRPcHRpb25zOiB7Y29udGV4dFR5cGV9LCBpbWFnZU9wdGlvbnM6IHthbHBoYX19O1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIHRmLmJyb3dzZXIuZHJhdyhpbWcsIGNhbnZhcyBhcyBhbnksIGRyYXdPcHRpb25zKTtcbiAgY29uc3QgYWN0dWFsRGF0YSA9IHJlYWRQaXhlbHNGcm9tQ2FudmFzKGNhbnZhcywgY29udGV4dFR5cGUsIHdpZHRoLCBoZWlnaHQpO1xuICBjb25zdCBleHBlY3RlZERhdGEgPSBjb252ZXJ0VG9SR0JBKGRhdGEsIHNoYXBlLCBkdHlwZSwgYWxwaGEpO1xuICBpbWcuZGlzcG9zZSgpO1xuICByZXR1cm4gW2FjdHVhbERhdGEsIGV4cGVjdGVkRGF0YV07XG59XG5cbi8vIENQVSBhbmQgR1BVIGhhbmRsZSBwaXhlbCB2YWx1ZSBkaWZmZXJlbnRseS4gVGhlIGVwc2lsb24gbWF5IHBvc3NpYmx5IGdyb3dcbi8vIGFmdGVyIGVhY2ggZHJhdyBhbmQgcmVhZCBiYWNrLlxuY29uc3QgRFJBV19FUFNJTE9OID0gNi4wO1xuXG5kZXNjcmliZVdpdGhGbGFncygnZHJhdyBvbiBjYW52YXMgY29udGV4dCcsIEJST1dTRVJfRU5WUywgKGVudikgPT4ge1xuICBsZXQgY29udGV4dFR5cGU6IHN0cmluZztcbiAgYmVmb3JlQWxsKGFzeW5jICgpID0+IHtcbiAgICBhd2FpdCB0Zi5zZXRCYWNrZW5kKGVudi5uYW1lKTtcbiAgICBjb250ZXh0VHlwZSA9IGVudi5uYW1lID09PSAnY3B1JyA/ICcyZCcgOiBlbnYubmFtZTtcbiAgfSk7XG5cbiAgaXQoJ2RyYXcgaW1hZ2Ugd2l0aCA0IGNoYW5uZWxzIGFuZCBpbnQgdmFsdWVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPVxuICAgICAgICBbMTAsIDIwLCAzMCwgNDAsIDUwLCA2MCwgNzAsIDgwLCA5MCwgMTAwLCAxMTAsIDEyMCwgMTMwLCAxNDAsIDE1MCwgMTYwXTtcbiAgICBjb25zdCBzaGFwZSA9IFsyLCAyLCA0XTtcbiAgICBjb25zdCBkdHlwZSA9ICdpbnQzMic7XG4gICAgY29uc3Qgc3RhcnROdW1UZW5zb3JzID0gdGYubWVtb3J5KCkubnVtVGVuc29ycztcbiAgICBjb25zdCBbYWN0dWFsRGF0YSwgZXhwZWN0ZWREYXRhXSA9XG4gICAgICAgIGRyYXdBbmRSZWFkYmFjayhjb250ZXh0VHlwZSwgZGF0YSwgc2hhcGUsIGR0eXBlKTtcbiAgICBleHBlY3QodGYubWVtb3J5KCkubnVtVGVuc29ycykudG9FcXVhbChzdGFydE51bVRlbnNvcnMpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKGFjdHVhbERhdGEsIGV4cGVjdGVkRGF0YSwgRFJBV19FUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ2RyYXcgaW1hZ2Ugd2l0aCA0IGNoYW5uZWxzIGFuZCBpbnQgdmFsdWVzLCBhbHBoYT0wLjUnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZGF0YSA9XG4gICAgICAgIFsxMCwgMjAsIDMwLCA0MCwgNTAsIDYwLCA3MCwgODAsIDkwLCAxMDAsIDExMCwgMTIwLCAxMzAsIDE0MCwgMTUwLCAxNjBdO1xuICAgIGNvbnN0IHNoYXBlID0gWzIsIDIsIDRdO1xuICAgIGNvbnN0IGR0eXBlID0gJ2ludDMyJztcbiAgICBjb25zdCBzdGFydE51bVRlbnNvcnMgPSB0Zi5tZW1vcnkoKS5udW1UZW5zb3JzO1xuICAgIGNvbnN0IFthY3R1YWxEYXRhLCBleHBlY3RlZERhdGFdID1cbiAgICAgICAgZHJhd0FuZFJlYWRiYWNrKGNvbnRleHRUeXBlLCBkYXRhLCBzaGFwZSwgZHR5cGUsIDAuNSk7XG4gICAgZXhwZWN0KHRmLm1lbW9yeSgpLm51bVRlbnNvcnMpLnRvRXF1YWwoc3RhcnROdW1UZW5zb3JzKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IGltYWdlIHdpdGggNCBjaGFubmVscyBhbmQgZmxvYXQgdmFsdWVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPVxuICAgICAgICBbLjEsIC4yLCAuMywgLjQsIC41LCAuNiwgLjcsIC44LCAuMDksIC4xLCAuMTEsIC4xMiwgLjEzLCAuMTQsIC4xNSwgLjE2XTtcbiAgICBjb25zdCBzaGFwZSA9IFsyLCAyLCA0XTtcbiAgICBjb25zdCBkdHlwZSA9ICdmbG9hdDMyJztcbiAgICBjb25zdCBbYWN0dWFsRGF0YSwgZXhwZWN0ZWREYXRhXSA9XG4gICAgICAgIGRyYXdBbmRSZWFkYmFjayhjb250ZXh0VHlwZSwgZGF0YSwgc2hhcGUsIGR0eXBlKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IGltYWdlIHdpdGggMyBjaGFubmVscyBhbmQgaW50IHZhbHVlcycsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkYXRhID0gWzEsIDIsIDMsIDQsIDUsIDYsIDcsIDgsIDksIDEwLCAxMSwgMTJdO1xuICAgIGNvbnN0IHNoYXBlID0gWzIsIDIsIDNdO1xuICAgIGNvbnN0IGR0eXBlID0gJ2ludDMyJztcbiAgICBjb25zdCBbYWN0dWFsRGF0YSwgZXhwZWN0ZWREYXRhXSA9XG4gICAgICAgIGRyYXdBbmRSZWFkYmFjayhjb250ZXh0VHlwZSwgZGF0YSwgc2hhcGUsIGR0eXBlKTtcbiAgICBleHBlY3RBcnJheXNFcXVhbChhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEpO1xuICB9KTtcblxuICBpdCgnZHJhdyBpbWFnZSB3aXRoIDMgY2hhbm5lbHMgYW5kIGludCB2YWx1ZXMsIGFscGhhPTAuNScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkYXRhID0gWzEwMSwgMzIsIDExMywgMTQsIDM1LCA3NiwgMTcsIDM4LCA1OSwgNzAsIDgxLCA5Ml07XG4gICAgY29uc3Qgc2hhcGUgPSBbMiwgMiwgM107XG4gICAgY29uc3QgZHR5cGUgPSAnaW50MzInO1xuICAgIGNvbnN0IGFscGhhID0gMC41O1xuICAgIGNvbnN0IFthY3R1YWxEYXRhLCBleHBlY3RlZERhdGFdID1cbiAgICAgICAgZHJhd0FuZFJlYWRiYWNrKGNvbnRleHRUeXBlLCBkYXRhLCBzaGFwZSwgZHR5cGUsIGFscGhhKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IGltYWdlIHdpdGggMyBjaGFubmVscyBhbmQgZmxvYXQgdmFsdWVzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPSBbLjEsIC4yLCAuMywgLjQsIC41LCAuNiwgLjcsIC44LCAuOSwgLjEsIC4xMSwgLjEyXTtcbiAgICBjb25zdCBzaGFwZSA9IFsyLCAyLCAzXTtcbiAgICBjb25zdCBkdHlwZSA9ICdmbG9hdDMyJztcbiAgICBjb25zdCBbYWN0dWFsRGF0YSwgZXhwZWN0ZWREYXRhXSA9XG4gICAgICAgIGRyYXdBbmRSZWFkYmFjayhjb250ZXh0VHlwZSwgZGF0YSwgc2hhcGUsIGR0eXBlKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IDJEIGltYWdlIGluIGdyYXlzY2FsZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkYXRhID0gWzEwMCwgMTIsIDkwLCA2NF07XG4gICAgY29uc3Qgc2hhcGUgPSBbMiwgMl07XG4gICAgY29uc3QgZHR5cGUgPSAnaW50MzInO1xuICAgIGNvbnN0IFthY3R1YWxEYXRhLCBleHBlY3RlZERhdGFdID1cbiAgICAgICAgZHJhd0FuZFJlYWRiYWNrKGNvbnRleHRUeXBlLCBkYXRhLCBzaGFwZSwgZHR5cGUpO1xuICAgIGV4cGVjdEFycmF5c0VxdWFsKGFjdHVhbERhdGEsIGV4cGVjdGVkRGF0YSk7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IGltYWdlIHdpdGggYWxwaGE9MC41JywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPSBbMTAxLCAyMTIsIDExMywgMTQsIDM1LCA3NiwgMTcsIDM4LCA1OSwgNzAsIDgxLCA5Ml07XG4gICAgY29uc3Qgc2hhcGUgPSBbNiwgMiwgMV07XG4gICAgY29uc3QgZHR5cGUgPSAnaW50MzInO1xuICAgIGNvbnN0IGFscGhhID0gMC41O1xuICAgIGNvbnN0IFthY3R1YWxEYXRhLCBleHBlY3RlZERhdGFdID1cbiAgICAgICAgZHJhd0FuZFJlYWRiYWNrKGNvbnRleHRUeXBlLCBkYXRhLCBzaGFwZSwgZHR5cGUsIGFscGhhKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xuXG4gIGl0KCdkcmF3IGltYWdlIHdvcmtzIHdoZW4gY2FudmFzIGhhcyBiZWVuIHVzZWQgZm9yIDJkJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPSBbMTAxLCAyMTIsIDExMywgMTQsIDM1LCA3NiwgMTcsIDM4LCA1OSwgNzAsIDgxLCA5Ml07XG4gICAgY29uc3Qgc2hhcGUgPSBbNiwgMiwgMV07XG4gICAgY29uc3QgZHR5cGUgPSAnaW50MzInO1xuICAgIC8vIFNldCBjYW52YXNVc2VkQXMyZCB0byB0cnVlIHNvIHRoZSBjYW52YXMgd2lsbCBiZSBmaXJzdCB1c2VkIGZvciAyZC5cbiAgICBjb25zdCBbYWN0dWFsRGF0YSwgZXhwZWN0ZWREYXRhXSA9XG4gICAgICAgIGRyYXdBbmRSZWFkYmFjayhjb250ZXh0VHlwZSwgZGF0YSwgc2hhcGUsIGR0eXBlLCAxLCB0cnVlKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShhY3R1YWxEYXRhLCBleHBlY3RlZERhdGEsIERSQVdfRVBTSUxPTik7XG4gIH0pO1xufSk7XG4iXX0=