UNPKG

19.6 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2017 Google LLC. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * =============================================================================
16 */
17import * as tf from '../index';
18import { ALL_ENVS, describeWithFlags } from '../jasmine_util';
19import { expectArraysClose } from '../test_util';
20describeWithFlags('multinomial', ALL_ENVS, () => {
21 const NUM_SAMPLES = 1000;
22 // Allowed Variance in probability (in %).
23 const EPSILON = 0.05;
24 const SEED = 3.14;
25 it('Flip a fair coin and check bounds', async () => {
26 const probs = tf.tensor1d([1, 1]);
27 const result = tf.multinomial(probs, NUM_SAMPLES, SEED);
28 expect(result.dtype).toBe('int32');
29 expect(result.shape).toEqual([NUM_SAMPLES]);
30 const outcomeProbs = computeProbs(await result.data(), 2);
31 expectArraysClose(outcomeProbs, [0.5, 0.5], EPSILON);
32 });
33 it('Flip a two-sided coin with 100% of heads', async () => {
34 const logits = tf.tensor1d([1, -100]);
35 const result = tf.multinomial(logits, NUM_SAMPLES, SEED);
36 expect(result.dtype).toBe('int32');
37 expect(result.shape).toEqual([NUM_SAMPLES]);
38 const outcomeProbs = computeProbs(await result.data(), 2);
39 expectArraysClose(outcomeProbs, [1, 0], EPSILON);
40 });
41 it('Flip a two-sided coin with 100% of tails', async () => {
42 const logits = tf.tensor1d([-100, 1]);
43 const result = tf.multinomial(logits, NUM_SAMPLES, SEED);
44 expect(result.dtype).toBe('int32');
45 expect(result.shape).toEqual([NUM_SAMPLES]);
46 const outcomeProbs = computeProbs(await result.data(), 2);
47 expectArraysClose(outcomeProbs, [0, 1], EPSILON);
48 });
49 it('Flip a single-sided coin throws error', () => {
50 const probs = tf.tensor1d([1]);
51 expect(() => tf.multinomial(probs, NUM_SAMPLES, SEED)).toThrowError();
52 });
53 it('Flip a ten-sided coin and check bounds', async () => {
54 const numOutcomes = 10;
55 const logits = tf.fill([numOutcomes], 1).as1D();
56 const result = tf.multinomial(logits, NUM_SAMPLES, SEED);
57 expect(result.dtype).toBe('int32');
58 expect(result.shape).toEqual([NUM_SAMPLES]);
59 const outcomeProbs = computeProbs(await result.data(), numOutcomes);
60 expect(outcomeProbs.length).toBeLessThanOrEqual(numOutcomes);
61 });
62 it('Flip 3 three-sided coins, each coin is 100% biases', async () => {
63 const numOutcomes = 3;
64 const logits = tf.tensor2d([[-100, -100, 1], [-100, 1, -100], [1, -100, -100]], [3, numOutcomes]);
65 const result = tf.multinomial(logits, NUM_SAMPLES, SEED);
66 expect(result.dtype).toBe('int32');
67 expect(result.shape).toEqual([3, NUM_SAMPLES]);
68 // First coin always gets last event.
69 let outcomeProbs = computeProbs((await result.data()).slice(0, NUM_SAMPLES), numOutcomes);
70 expectArraysClose(outcomeProbs, [0, 0, 1], EPSILON);
71 // Second coin always gets middle event.
72 outcomeProbs = computeProbs((await result.data()).slice(NUM_SAMPLES, 2 * NUM_SAMPLES), numOutcomes);
73 expectArraysClose(outcomeProbs, [0, 1, 0], EPSILON);
74 // Third coin always gets first event
75 outcomeProbs =
76 computeProbs((await result.data()).slice(2 * NUM_SAMPLES), numOutcomes);
77 expectArraysClose(outcomeProbs, [1, 0, 0], EPSILON);
78 });
79 it('passing Tensor3D throws error', () => {
80 const probs = tf.zeros([3, 2, 2]);
81 const normalized = true;
82 expect(() => tf.multinomial(probs, 3, SEED, normalized))
83 .toThrowError();
84 });
85 it('throws when passed a non-tensor', () => {
86 // tslint:disable-next-line:no-any
87 expect(() => tf.multinomial({}, NUM_SAMPLES, SEED))
88 .toThrowError(/Argument 'logits' passed to 'multinomial' must be a Tensor/);
89 });
90 it('accepts a tensor-like object for logits (biased coin)', async () => {
91 const res = tf.multinomial([-100, 1], NUM_SAMPLES, SEED);
92 expect(res.dtype).toBe('int32');
93 expect(res.shape).toEqual([NUM_SAMPLES]);
94 const outcomeProbs = computeProbs(await res.data(), 2);
95 expectArraysClose(outcomeProbs, [0, 1], EPSILON);
96 });
97 function computeProbs(events, numOutcomes) {
98 const counts = [];
99 for (let i = 0; i < numOutcomes; ++i) {
100 counts[i] = 0;
101 }
102 const numSamples = events.length;
103 for (let i = 0; i < events.length; ++i) {
104 counts[events[i]]++;
105 }
106 // Normalize counts to be probabilities between [0, 1].
107 for (let i = 0; i < counts.length; i++) {
108 counts[i] /= numSamples;
109 }
110 return counts;
111 }
112});
113//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGlub21pYWxfdGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvb3BzL211bHRpbm9taWFsX3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxFQUFDLFFBQVEsRUFBRSxpQkFBaUIsRUFBQyxNQUFNLGlCQUFpQixDQUFDO0FBRTVELE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUUvQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUM5QyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDekIsMENBQTBDO0lBQzFDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQztJQUNyQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUM7SUFFbEIsRUFBRSxDQUFDLG1DQUFtQyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2pELE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQyxNQUFNLE1BQU0sR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDeEQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbkMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQyxNQUFNLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMxRCxpQkFBaUIsQ0FBQyxZQUFZLEVB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SUFBSSxVQUFVLENBQUM7U0FDekI7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0FBQ0gsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICovXG5cbmltcG9ydCAqIGFzIHRmIGZyb20gJy4uL2luZGV4JztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuLi9qYXNtaW5lX3V0aWwnO1xuaW1wb3J0IHtUZW5zb3IxRH0gZnJvbSAnLi4vdGVuc29yJztcbmltcG9ydCB7ZXhwZWN0QXJyYXlzQ2xvc2V9IGZyb20gJy4uL3Rlc3RfdXRpbCc7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdtdWx0aW5vbWlhbCcsIEFMTF9FTlZTLCAoKSA9PiB7XG4gIGNvbnN0IE5VTV9TQU1QTEVTID0gMTAwMDtcbiAgLy8gQWxsb3dlZCBWYXJpYW5jZSBpbiBwcm9iYWJpbGl0eSAoaW4gJSkuXG4gIGNvbnN0IEVQU0lMT04gPSAwLjA1O1xuICBjb25zdCBTRUVEID0gMy4xNDtcblxuICBpdCgnRmxpcCBhIGZhaXIgY29pbiBhbmQgY2hlY2sgYm91bmRzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHByb2JzID0gdGYudGVuc29yMWQoWzEsIDFdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChwcm9icywgTlVNX1NBTVBMRVMsIFNFRUQpO1xuICAgIGV4cGVjdChyZXN1bHQuZHR5cGUpLnRvQmUoJ2ludDMyJyk7XG4gICAgZXhwZWN0KHJlc3VsdC5zaGFwZSkudG9FcXVhbChbTlVNX1NBTVBMRVNdKTtcbiAgICBjb25zdCBvdXRjb21lUHJvYnMgPSBjb21wdXRlUHJvYnMoYXdhaXQgcmVzdWx0LmRhdGEoKSwgMik7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2Uob3V0Y29tZVByb2JzLCBbMC41LCAwLjVdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0d28tc2lkZWQgY29pbiB3aXRoIDEwMCUgb2YgaGVhZHMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbG9naXRzID0gdGYudGVuc29yMWQoWzEsIC0xMDBdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChsb2dpdHMsIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzdWx0LmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXN1bHQuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlc3VsdC5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzEsIDBdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0d28tc2lkZWQgY29pbiB3aXRoIDEwMCUgb2YgdGFpbHMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbG9naXRzID0gdGYudGVuc29yMWQoWy0xMDAsIDFdKTtcbiAgICBjb25zdCByZXN1bHQgPSB0Zi5tdWx0aW5vbWlhbChsb2dpdHMsIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzdWx0LmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXN1bHQuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlc3VsdC5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzAsIDFdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSBzaW5nbGUtc2lkZWQgY29pbiB0aHJvd3MgZXJyb3InLCAoKSA9PiB7XG4gICAgY29uc3QgcHJvYnMgPSB0Zi50ZW5zb3IxZChbMV0pO1xuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbChwcm9icywgTlVNX1NBTVBMRVMsIFNFRUQpKS50b1Rocm93RXJyb3IoKTtcbiAgfSk7XG5cbiAgaXQoJ0ZsaXAgYSB0ZW4tc2lkZWQgY29pbiBhbmQgY2hlY2sgYm91bmRzJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IG51bU91dGNvbWVzID0gMTA7XG4gICAgY29uc3QgbG9naXRzID0gdGYuZmlsbChbbnVtT3V0Y29tZXNdLCAxKS5hczFEKCk7XG4gICAgY29uc3QgcmVzdWx0ID0gdGYubXVsdGlub21pYWwobG9naXRzLCBOVU1fU0FNUExFUywgU0VFRCk7XG4gICAgZXhwZWN0KHJlc3VsdC5kdHlwZSkudG9CZSgnaW50MzInKTtcbiAgICBleHBlY3QocmVzdWx0LnNoYXBlKS50b0VxdWFsKFtOVU1fU0FNUExFU10pO1xuICAgIGNvbnN0IG91dGNvbWVQcm9icyA9IGNvbXB1dGVQcm9icyhhd2FpdCByZXN1bHQuZGF0YSgpLCBudW1PdXRjb21lcyk7XG4gICAgZXhwZWN0KG91dGNvbWVQcm9icy5sZW5ndGgpLnRvQmVMZXNzVGhhbk9yRXF1YWwobnVtT3V0Y29tZXMpO1xuICB9KTtcblxuICBpdCgnRmxpcCAzIHRocmVlLXNpZGVkIGNvaW5zLCBlYWNoIGNvaW4gaXMgMTAwJSBiaWFzZXMnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgbnVtT3V0Y29tZXMgPSAzO1xuICAgIGNvbnN0IGxvZ2l0cyA9IHRmLnRlbnNvcjJkKFxuICAgICAgICBbWy0xMDAsIC0xMDAsIDFdLCBbLTEwMCwgMSwgLTEwMF0sIFsxLCAtMTAwLCAtMTAwXV0sIFszLCBudW1PdXRjb21lc10pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRmLm11bHRpbm9taWFsKGxvZ2l0cywgTlVNX1NBTVBMRVMsIFNFRUQpO1xuICAgIGV4cGVjdChyZXN1bHQuZHR5cGUpLnRvQmUoJ2ludDMyJyk7XG4gICAgZXhwZWN0KHJlc3VsdC5zaGFwZSkudG9FcXVhbChbMywgTlVNX1NBTVBMRVNdKTtcblxuICAgIC8vIEZpcnN0IGNvaW4gYWx3YXlzIGdldHMgbGFzdCBldmVudC5cbiAgICBsZXQgb3V0Y29tZVByb2JzID1cbiAgICAgICAgY29tcHV0ZVByb2JzKChhd2FpdCByZXN1bHQuZGF0YSgpKS5zbGljZSgwLCBOVU1fU0FNUExFUyksIG51bU91dGNvbWVzKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShvdXRjb21lUHJvYnMsIFswLCAwLCAxXSwgRVBTSUxPTik7XG5cbiAgICAvLyBTZWNvbmQgY29pbiBhbHdheXMgZ2V0cyBtaWRkbGUgZXZlbnQuXG4gICAgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKFxuICAgICAgICAoYXdhaXQgcmVzdWx0LmRhdGEoKSkuc2xpY2UoTlVNX1NBTVBMRVMsIDIgKiBOVU1fU0FNUExFUyksIG51bU91dGNvbWVzKTtcbiAgICBleHBlY3RBcnJheXNDbG9zZShvdXRjb21lUHJvYnMsIFswLCAxLCAwXSwgRVBTSUxPTik7XG5cbiAgICAvLyBUaGlyZCBjb2luIGFsd2F5cyBnZXRzIGZpcnN0IGV2ZW50XG4gICAgb3V0Y29tZVByb2JzID1cbiAgICAgICAgY29tcHV0ZVByb2JzKChhd2FpdCByZXN1bHQuZGF0YSgpKS5zbGljZSgyICogTlVNX1NBTVBMRVMpLCBudW1PdXRjb21lcyk7XG4gICAgZXhwZWN0QXJyYXlzQ2xvc2Uob3V0Y29tZVByb2JzLCBbMSwgMCwgMF0sIEVQU0lMT04pO1xuICB9KTtcblxuICBpdCgncGFzc2luZyBUZW5zb3IzRCB0aHJvd3MgZXJyb3InLCAoKSA9PiB7XG4gICAgY29uc3QgcHJvYnMgPSB0Zi56ZXJvcyhbMywgMiwgMl0pO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB0cnVlO1xuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbChwcm9icyBhcyBUZW5zb3IxRCwgMywgU0VFRCwgbm9ybWFsaXplZCkpXG4gICAgICAgIC50b1Rocm93RXJyb3IoKTtcbiAgfSk7XG5cbiAgaXQoJ3Rocm93cyB3aGVuIHBhc3NlZCBhIG5vbi10ZW5zb3InLCAoKSA9PiB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIGV4cGVjdCgoKSA9PiB0Zi5tdWx0aW5vbWlhbCh7fSBhcyBhbnksIE5VTV9TQU1QTEVTLCBTRUVEKSlcbiAgICAgICAgLnRvVGhyb3dFcnJvcihcbiAgICAgICAgICAgIC9Bcmd1bWVudCAnbG9naXRzJyBwYXNzZWQgdG8gJ211bHRpbm9taWFsJyBtdXN0IGJlIGEgVGVuc29yLyk7XG4gIH0pO1xuXG4gIGl0KCdhY2NlcHRzIGEgdGVuc29yLWxpa2Ugb2JqZWN0IGZvciBsb2dpdHMgKGJpYXNlZCBjb2luKScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCByZXMgPSB0Zi5tdWx0aW5vbWlhbChbLTEwMCwgMV0sIE5VTV9TQU1QTEVTLCBTRUVEKTtcbiAgICBleHBlY3QocmVzLmR0eXBlKS50b0JlKCdpbnQzMicpO1xuICAgIGV4cGVjdChyZXMuc2hhcGUpLnRvRXF1YWwoW05VTV9TQU1QTEVTXSk7XG4gICAgY29uc3Qgb3V0Y29tZVByb2JzID0gY29tcHV0ZVByb2JzKGF3YWl0IHJlcy5kYXRhKCksIDIpO1xuICAgIGV4cGVjdEFycmF5c0Nsb3NlKG91dGNvbWVQcm9icywgWzAsIDFdLCBFUFNJTE9OKTtcbiAgfSk7XG5cbiAgZnVuY3Rpb24gY29tcHV0ZVByb2JzKFxuICAgICAgZXZlbnRzOiBGbG9hdDMyQXJyYXl8VWludDhBcnJheXxJbnQzMkFycmF5LCBudW1PdXRjb21lczogbnVtYmVyKSB7XG4gICAgY29uc3QgY291bnRzID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1PdXRjb21lczsgKytpKSB7XG4gICAgICBjb3VudHNbaV0gPSAwO1xuICAgIH1cbiAgICBjb25zdCBudW1TYW1wbGVzID0gZXZlbnRzLmxlbmd0aDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGV2ZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgY291bnRzW2V2ZW50c1tpXV0rKztcbiAgICB9XG4gICAgLy8gTm9ybWFsaXplIGNvdW50cyB0byBiZSBwcm9iYWJpbGl0aWVzIGJldHdlZW4gWzAsIDFdLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY291bnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb3VudHNbaV0gLz0gbnVtU2FtcGxlcztcbiAgICB9XG4gICAgcmV0dXJuIGNvdW50cztcbiAgfVxufSk7XG4iXX0=
\No newline at end of file