gx
chenyc
2025-02-12 ea42ff3ebee1eeb3fb29423aa848a249441db81c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
 * @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=