/** * @license * Copyright 2017 Google LLC. All Rights Reserved. * 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 { ALL_ENVS, describeWithFlags } from '../jasmine_util'; import { expectArraysClose } from '../test_util'; describeWithFlags('multinomial', ALL_ENVS, () => { const NUM_SAMPLES = 1000; // Allowed Variance in probability (in %). const EPSILON = 0.05; const SEED = 3.14; it('Flip a fair coin and check bounds', async () => { const probs = tf.tensor1d([1, 1]); const result = tf.multinomial(probs, NUM_SAMPLES, SEED); expect(result.dtype).toBe('int32'); expect(result.shape).toEqual([NUM_SAMPLES]); const outcomeProbs = computeProbs(await result.data(), 2); expectArraysClose(outcomeProbs, [0.5, 0.5], EPSILON); }); it('Flip a two-sided coin with 100% of heads', async () => { const logits = tf.tensor1d([1, -100]); const result = tf.multinomial(logits, NUM_SAMPLES, SEED); expect(result.dtype).toBe('int32'); expect(result.shape).toEqual([NUM_SAMPLES]); const outcomeProbs = computeProbs(await result.data(), 2); expectArraysClose(outcomeProbs, [1, 0], EPSILON); }); it('Flip a two-sided coin with 100% of tails', async () => { const logits = tf.tensor1d([-100, 1]); const result = tf.multinomial(logits, NUM_SAMPLES, SEED); expect(result.dtype).toBe('int32'); expect(result.shape).toEqual([NUM_SAMPLES]); const outcomeProbs = computeProbs(await result.data(), 2); expectArraysClose(outcomeProbs, [0, 1], EPSILON); }); it('Flip a single-sided coin throws error', () => { const probs = tf.tensor1d([1]); expect(() => tf.multinomial(probs, NUM_SAMPLES, SEED)).toThrowError(); }); it('Flip a ten-sided coin and check bounds', async () => { const numOutcomes = 10; const logits = tf.fill([numOutcomes], 1).as1D(); const result = tf.multinomial(logits, NUM_SAMPLES, SEED); expect(result.dtype).toBe('int32'); expect(result.shape).toEqual([NUM_SAMPLES]); const outcomeProbs = computeProbs(await result.data(), numOutcomes); expect(outcomeProbs.length).toBeLessThanOrEqual(numOutcomes); }); it('Flip 3 three-sided coins, each coin is 100% biases', async () => { const numOutcomes = 3; const logits = tf.tensor2d([[-100, -100, 1], [-100, 1, -100], [1, -100, -100]], [3, numOutcomes]); const result = tf.multinomial(logits, NUM_SAMPLES, SEED); expect(result.dtype).toBe('int32'); expect(result.shape).toEqual([3, NUM_SAMPLES]); // First coin always gets last event. let outcomeProbs = computeProbs((await result.data()).slice(0, NUM_SAMPLES), numOutcomes); expectArraysClose(outcomeProbs, [0, 0, 1], EPSILON); // Second coin always gets middle event. outcomeProbs = computeProbs((await result.data()).slice(NUM_SAMPLES, 2 * NUM_SAMPLES), numOutcomes); expectArraysClose(outcomeProbs, [0, 1, 0], EPSILON); // Third coin always gets first event outcomeProbs = computeProbs((await result.data()).slice(2 * NUM_SAMPLES), numOutcomes); expectArraysClose(outcomeProbs, [1, 0, 0], EPSILON); }); it('passing Tensor3D throws error', () => { const probs = tf.zeros([3, 2, 2]); const normalized = true; expect(() => tf.multinomial(probs, 3, SEED, normalized)) .toThrowError(); }); it('throws when passed a non-tensor', () => { // tslint:disable-next-line:no-any expect(() => tf.multinomial({}, NUM_SAMPLES, SEED)) .toThrowError(/Argument 'logits' passed to 'multinomial' must be a Tensor/); }); it('accepts a tensor-like object for logits (biased coin)', async () => { const res = tf.multinomial([-100, 1], NUM_SAMPLES, SEED); expect(res.dtype).toBe('int32'); expect(res.shape).toEqual([NUM_SAMPLES]); const outcomeProbs = computeProbs(await res.data(), 2); expectArraysClose(outcomeProbs, [0, 1], EPSILON); }); it('creates the same data given the same seed', async () => { const res1 = tf.multinomial([1, 2, 3, 4], NUM_SAMPLES, SEED); const res2 = tf.multinomial([1, 2, 3, 4], NUM_SAMPLES, SEED); expectArraysClose(await res1.data(), await res2.data()); }); function computeProbs(events, numOutcomes) { const counts = []; for (let i = 0; i < numOutcomes; ++i) { counts[i] = 0; } const numSamples = events.length; for (let i = 0; i < events.length; ++i) { counts[events[i]]++; } // Normalize counts to be probabilities between [0, 1]. for (let i = 0; i < counts.length; i++) { counts[i] /= numSamples; } return counts; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGlub21pYWxfdGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvb3BzL211bHRpbm9taWFsX3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxFQUFDLFFBQVEsRUFBRSxpQkFBaUIsRUFBQyxNQUFNLGlCQUFpQixDQUFDO0FBRTVELE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUUvQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUM5QyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDekIsMENBQTBDO0lBQzFDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQztJQUNyQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUM7SUFFbEIsRUFBRSxDQUFDLG1DQUFtQyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2pELE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQyxNQUFNLE1BQU0sR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDeEQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbkMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQyxNQUFNLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMxRCxpQkFBaUIsQ0FBQyxZQUFZLEVB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Q0FBQyxHQUFHLFdBQVcsRUFBRSxFQUFFLENBQUMsRUFBRTtZQUNwQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ2Y7UUFDRCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQ2pDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFO1lBQ3RDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ3JCO1FBQ0QsdURBQXVEO1FBQ3ZELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3RDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxVQUFVLENBQUM7U0FDekI7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0FBQ0gsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCAqIGFzIHRmIGZyb20gJy4uL2luZGV4JztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuLi9qYXNtaW5lX3V0aWwnO1xuaW1wb3J0IHtUZW5zb3IxRH0gZnJvbSAnLi4vdGVuc29yJztcbmltcG9ydCB7ZXhwZWN0QXJyYXlzQ2xvc2V9IGZyb20gJy4uL3Rlc3RfdXRpbCc7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdtdWx0aW5vbWlhbCcsIEFMTF9FTlZTLCAoKSA9PiB7XG4gIGNvbnN0IE5VTV9TQU1QTEVTID0gMTAwMDtcbiAgLy8gQWxsb3dlZCBWYXJpYW5jZSBpbiBwcm9iYWJpbGl0eSAoaW4gJSkuXG4gIGNvbnN0IEVQU0lMT04gPSAwLjA1O1xuICBjb25zdCBTRUVEID0gMy4xNDtcblxuICBpdCgnRmxpcCBhIGZhaXIgY29pbiBhbmQgY2hlY2sgYm91bmRzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHByb2JzID0gdGYudGVuc29yMWQoWzEsIDFdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChwcm9icywgTlVNX1NBTVBMRVMsIFNFRUQpO1xuICAgIGV4cGVjdChyZXN1bHQuZHR5cGUpLnRvQmUoJ2ludDMyJyk7XG4gICAgZXhwZWN0KHJlc3VsdC5zaGFwZSkudG9FcXVhbChbTlVNX1NBTVBMRVNdKTtcbiAgICBjb25zdCBvdXRjb21lUHJvYnMgPSBjb21wdXRlUHJvYnMoYXdhaXQgcmVzdWx0LmRhdGEoKSwgMik7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2Uob3V0Y29tZVByb2JzLCBbMC41LCAwLjVdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0d28tc2lkZWQgY29pbiB3aXRoIDEwMCUgb2YgaGVhZHMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbG9naXRzID0gdGYudGVuc29yMWQoWzEsIC0xMDBdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChsb2dpdHMsIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzdWx0LmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXN1bHQuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlc3VsdC5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzEsIDBdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0d28tc2lkZWQgY29pbiB3aXRoIDEwMCUgb2YgdGFpbHMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbG9naXRzID0gdGYudGVuc29yMWQoWy0xMDAsIDFdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChsb2dpdHMsIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzdWx0LmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXN1bHQuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlc3VsdC5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzAsIDFdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSBzaW5nbGUtc2lkZWQgY29pbiB0aHJvd3MgZXJyb3InLCAoKSA9PiB7XG4gICAgY29uc3QgcHJvYnMgPSB0Zi50ZW5zb3IxZChbMV0pO1xuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbChwcm9icywgTlVNX1NBTVBMRVMsIFNFRUQpKS50b1Rocm93RXJyb3IoKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0ZW4tc2lkZWQgY29pbiBhbmQgY2hlY2sgYm91bmRzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IG51bU91dGNvbWVzID0gMTA7XG4gICAgY29uc3QgbG9naXRzID0gdGYuZmlsbChbbnVtT3V0Y29tZXNdLCAxKS5hczFEKCk7XG4gICAgY29uc3QgcmVzdWx0ID0gdGYubXVsdGlub21pYWwobG9naXRzLCBOVU1fU0FNUExFUywgU0VFRCk7XG4gICAgZXhwZWN0KHJlc3VsdC5kdHlwZSkudG9CZSgnaW50MzInKTtcbiAgICBleHBlY3QocmVzdWx0LnNoYXBlKS50b0VxdWFsKFtOVU1fU0FNUExFU10pO1xuICAgIGNvbnN0IG91dGNvbWVQcm9icyA9IGNvbXB1dGVQcm9icyhhd2FpdCByZXN1bHQuZGF0YSgpLCBudW1PdXRjb21lcyk7XG4gICAgZXhwZWN0KG91dGNvbWVQcm9icy5sZW5ndGgpLnRvQmVMZXNzVGhhbk9yRXF1YWwobnVtT3V0Y29tZXMpO1xuICB9KTtcblxuICBpdCgnRmxpcCAzIHRocmVlLXNpZGVkIGNvaW5zLCBlYWNoIGNvaW4gaXMgMTAwJSBiaWFzZXMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbnVtT3V0Y29tZXMgPSAzO1xuICAgIGNvbnN0IGxvZ2l0cyA9IHRmLnRlbnNvcjJkKFxuICAgICAgICBbWy0xMDAsIC0xMDAsIDFdLCBbLTEwMCwgMSwgLTEwMF0sIFsxLCAtMTAwLCAtMTAwXV0sIFszLCBudW1PdXRjb21lc10pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRmLm11bHRpbm9taWFsKGxvZ2l0cywgTlVNX1NBTVBMRVMsIFNFRUQpO1xuICAgIGV4cGVjdChyZXN1bHQuZHR5cGUpLnRvQmUoJ2ludDMyJyk7XG4gICAgZXhwZWN0KHJlc3VsdC5zaGFwZSkudG9FcXVhbChbMywgTlVNX1NBTVBMRVNdKTtcblxuICAgIC8vIEZpcnN0IGNvaW4gYWx3YXlzIGdldHMgbGFzdCBldmVudC5cbiAgICBsZXQgb3V0Y29tZVByb2JzID1cbiAgICAgICAgY29tcHV0ZVByb2JzKChhd2FpdCByZXN1bHQuZGF0YSgpKS5zbGljZSgwLCBOVU1fU0FNUExFUyksIG51bU91dGNvbWVzKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShvdXRjb21lUHJvYnMsIFswLCAwLCAxXSwgRVBTSUxPTik7XG5cbiAgICAvLyBTZWNvbmQgY29pbiBhbHdheXMgZ2V0cyBtaWRkbGUgZXZlbnQuXG4gICAgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKFxuICAgICAgICAoYXdhaXQgcmVzdWx0LmRhdGEoKSkuc2xpY2UoTlVNX1NBTVBMRVMsIDIgKiBOVU1fU0FNUExFUyksIG51bU91dGNvbWVzKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShvdXRjb21lUHJvYnMsIFswLCAxLCAwXSwgRVBTSUxPTik7XG5cbiAgICAvLyBUaGlyZCBjb2luIGFsd2F5cyBnZXRzIGZpcnN0IGV2ZW50XG4gICAgb3V0Y29tZVByb2JzID1cbiAgICAgICAgY29tcHV0ZVByb2JzKChhd2FpdCByZXN1bHQuZGF0YSgpKS5zbGljZSgyICogTlVNX1NBTVBMRVMpLCBudW1PdXRjb21lcyk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2Uob3V0Y29tZVByb2JzLCBbMSwgMCwgMF0sIEVQU0lMT04pO1xuICB9KTtcblxuICBpdCgncGFzc2luZyBUZW5zb3IzRCB0aHJvd3MgZXJyb3InLCAoKSA9PiB7XG4gICAgY29uc3QgcHJvYnMgPSB0Zi56ZXJvcyhbMywgMiwgMl0pO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB0cnVlO1xuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbChwcm9icyBhcyBUZW5zb3IxRCwgMywgU0VFRCwgbm9ybWFsaXplZCkpXG4gICAgICAgIC50b1Rocm93RXJyb3IoKTtcbiAgfSk7XG5cbiAgaXQoJ3Rocm93cyB3aGVuIHBhc3NlZCBhIG5vbi10ZW5zb3InLCAoKSA9PiB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbCh7fSBhcyBhbnksIE5VTV9TQU1QTEVTLCBTRUVEKSlcbiAgICAgICAgLnRvVGhyb3dFcnJvcihcbiAgICAgICAgICAgIC9Bcmd1bWVudCAnbG9naXRzJyBwYXNzZWQgdG8gJ211bHRpbm9taWFsJyBtdXN0IGJlIGEgVGVuc29yLyk7XG4gIH0pO1xuXG4gIGl0KCdhY2NlcHRzIGEgdGVuc29yLWxpa2Ugb2JqZWN0IGZvciBsb2dpdHMgKGJpYXNlZCBjb2luKScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCByZXMgPSB0Zi5tdWx0aW5vbWlhbChbLTEwMCwgMV0sIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzLmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXMuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlcy5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzAsIDFdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ2NyZWF0ZXMgdGhlIHNhbWUgZGF0YSBnaXZlbiB0aGUgc2FtZSBzZWVkJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHJlczEgPSB0Zi5tdWx0aW5vbWlhbChbMSwgMiwgMywgNF0sIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBjb25zdCByZXMyID0gdGYubXVsdGlub21pYWwoWzEsIDIsIDMsIDRdLCBOVU1fU0FNUExFUywgU0VFRCk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2UoYXdhaXQgcmVzMS5kYXRhKCksIGF3YWl0IHJlczIuZGF0YSgpKTtcbiAgfSk7XG5cbiAgZnVuY3Rpb24gY29tcHV0ZVByb2JzKFxuICAgICAgZXZlbnRzOiBGbG9hdDMyQXJyYXl8VWludDhBcnJheXxJbnQzMkFycmF5LCBudW1PdXRjb21lczogbnVtYmVyKSB7XG4gICAgY29uc3QgY291bnRzID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1PdXRjb21lczsgKytpKSB7XG4gICAgICBjb3VudHNbaV0gPSAwO1xuICAgIH1cbiAgICBjb25zdCBudW1TYW1wbGVzID0gZXZlbnRzLmxlbmd0aDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGV2ZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgY291bnRzW2V2ZW50c1tpXV0rKztcbiAgICB9XG4gICAgLy8gTm9ybWFsaXplIGNvdW50cyB0byBiZSBwcm9iYWJpbGl0aWVzIGJldHdlZW4gWzAsIDFdLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY291bnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb3VudHNbaV0gLz0gbnVtU2FtcGxlcztcbiAgICB9XG4gICAgcmV0dXJuIGNvdW50cztcbiAgfVxufSk7XG4iXX0=