/** * @license * Copyright 2021 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 { fingerPrint64, hexToLong } from './hash_util'; import { ALL_ENVS, describeWithFlags } from './jasmine_util'; /** * The ACMRandom generator is for situations where extreme statistical quality * is not important. ACMRandom is useful for testing since it is seeded allowing * for reproducible results as well as low overhead so using it will * not affect test speed. */ class ACMRandom { constructor(seed) { seed = seed & 0x7fffffff; if (seed === 0 || seed === ACMRandom.MAX_INT32) { seed = 1; } this.seed = seed; } next() { const A = hexToLong('41A7'); // bits 14, 8, 7, 5, 2, 1, 0 const MAX_INT32 = ACMRandom.MAX_INT32; // We are computing // seed = (seed * A) % MAX_INT32, where MAX_INT32 = 2^31-1 // // seed must not be zero or MAX_INT32, or else all subsequent computed // values will be zero or MAX_INT32 respectively. For all other values, // seed will end up cycling through every number in [1,MAX_INT32-1] const product = A.mul(this.seed); // Compute (product % MAX_INT32) using the fact that // ((x << 31) % MAX_INT32) == x. this.seed = product.shru(31).add(product.and(MAX_INT32)).getLowBits(); // The first reduction may overflow by 1 bit, so we may need to repeat. // mod == MAX_INT32 is not possible; using > allows for the faster // sign-bit-based test. if (this.seed > MAX_INT32) { this.seed -= MAX_INT32; } return this.seed; } rand8() { return (this.next() >> 1) & 0x000000ff; } } ACMRandom.MAX_INT32 = 2147483647; describeWithFlags('hash_util', ALL_ENVS, () => { it('check incremental hashes', () => { const buf = new Uint8Array(1000); const r = new ACMRandom(10); for (let i = 0; i < buf.length; ++i) { buf[i] = r.rand8(); } const expectIteration = (length, expectedHash) => expect(fingerPrint64(buf, length)).toEqual(hexToLong(expectedHash)); expectIteration(0, '9ae16a3b2f90404f'); expectIteration(1, '49d8a5e3fa93c327'); expectIteration(2, 'fd259abb0ff2bf12'); expectIteration(3, '781f1c6437096ac2'); expectIteration(4, '0f1369d6c0b45716'); expectIteration(5, '02d8cec6394de09a'); expectIteration(7, '1faf6c6d43626c48'); expectIteration(9, 'c93efd6dbe139be8'); expectIteration(12, '65abbc967c87d515'); expectIteration(16, '3f61450a03cff5af'); expectIteration(20, '5d2fe297e45fed1a'); expectIteration(26, 'eb665983aeb9ab94'); expectIteration(33, '3a5f3890b124b4a3'); expectIteration(42, 'f0c1cd66d7d9f246'); expectIteration(53, 'cf7e3e4b1efeba6d'); expectIteration(67, '0ced753b45740875'); expectIteration(84, 'a585e0be01846ff4'); expectIteration(105, 'fb6496deb356cdda'); expectIteration(132, 'f2e4c5b6db6c154a'); expectIteration(166, '4498451c3bca85a0'); expectIteration(208, 'd604355fa4d0b14e'); expectIteration(261, 'bb165e6b84ba9cdf'); expectIteration(327, '9ebfab4519b1348c'); expectIteration(409, '5921974ba2e9a5c2'); expectIteration(512, 'a86a96e7a44282e3'); expectIteration(640, 'dd731dfee500aa3c'); expectIteration(800, 'a69f3400e6c98357'); expectIteration(1000, '5c63b66443990bec'); }); // This is more thorough, but if something is wrong the output will be even // less illuminating, because it just checks one integer at the end. it('check many different strings', () => { const iters = 800; const s = new Uint8Array(4 * iters); let len = 0; let h = hexToLong('0'); // Helper that replaces h with a hash of itself and return a // char that is also a hash of h. Neither hash needs to be particularly // good. const remix = () => { h = h.xor(h.shru(41)); h = h.mul(949921979); return 'a'.charCodeAt(0) + h.and(0xfffff).mod(26).getLowBits(); }; for (let i = 0; i < iters; i++) { h = h.xor(fingerPrint64(s, i)); s[len++] = remix(); h = h.xor(fingerPrint64(s, i * i % len)); s[len++] = remix(); h = h.xor(fingerPrint64(s, i * i * i % len)); s[len++] = remix(); h = h.xor(fingerPrint64(s, len)); s[len++] = remix(); const x0 = s[len - 1]; const x1 = s[len - 2]; const x2 = s[len - 3]; const x3 = s[len >> 1]; s[((x0 << 16) + (x1 << 8) + x2) % len] ^= x3; s[((x1 << 16) + (x2 << 8) + x3) % len] ^= i % 256; } expect(h).toEqual(hexToLong('7a1d67c50ec7e167')); }); it('check string hash', () => { const fingerPrintHash = (hash) => { const mul = hexToLong('9ddfea08eb382d69'); let b = hash.mul(mul); b = b.xor(b.shru(44)); b = b.mul(mul); b = b.xor(b.shru(41)); b = b.mul(mul); return b; }; const getString = (length) => Uint8Array.from('x'.repeat(length).split('').map(char => char.charCodeAt(0))); expect(fingerPrintHash(fingerPrint64(getString(40)))) .toEqual(hexToLong('2117170c4aebaffe')); expect(fingerPrintHash(fingerPrint64(getString(60)))) .toEqual(hexToLong('e252963f3fd7a3af')); expect(fingerPrintHash(fingerPrint64(getString(70)))) .toEqual(hexToLong('b0a8cf4a56c570fa')); expect(fingerPrintHash(fingerPrint64(getString(80)))) .toEqual(hexToLong('d6ddaa49ddef5839')); expect(fingerPrintHash(fingerPrint64(getString(90)))) .toEqual(hexToLong('168f3a694b4dce29')); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFzaF91dGlsX3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi90ZmpzLWNvcmUvc3JjL2hhc2hfdXRpbF90ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUVILE9BQU8sRUFBQyxhQUFhLEVBQUUsU0FBUyxFQUFDLE1BQU0sYUFBYSxDQUFDO0FBQ3JELE9BQU8sRUFBQyxRQUFRLEVBQUUsaUJBQWlCLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUUzRDs7Ozs7R0FLRztBQUNILE1BQU0sU0FBUztJQUliLFlBQVksSUFBWTtRQUN0QixJQUFJLEdBQUcsSUFBSSxHQUFHLFVBQVUsQ0FBQztRQUN6QixJQUFJLElBQUksS0FBSyxDQUFDLElBQUksSUFBSSxLQUFLLFNBQVMsQ0FBQyxTQUFTLEVBQUU7WUFDOUMsSUFBSSxHQUFHLENBQUMsQ0FBQztTQUNWO1FBQ0QsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7SUFDbkIsQ0FBQztJQUVPLElBQUk7UUFDVixNQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBRSw0QkFBNEI7UUFDMUQsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLFNBQVMsQ0FBQztRQUN0QyxtQkFBbUI7UUFDbkIsbUVBQW1FO1FBQ25FLEVBQUU7UUFDRixzRUFBc0U7UUFDdEUsd0VBQXdFO1FBQ3hFLG1FQUFtRTtRQUNuRSxNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVqQyxvREFBb0Q7UUFDcEQsZ0NBQWdDO1FBQ2hDLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3RFLHVFQUF1RTtRQUN2RSxrRUFBa0U7UUFDbEUsdUJBQXVCO1FBQ3ZCLElBQUksSUFBSSxDQUFDLElBQUksR0FBRyxTQUFTLEVBQUU7WUFDekIsSUFBSSxDQUFDLElBQUksSUFBSSxTQUFTLENBQUM7U0FDeEI7UUFDRCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDbkIsQ0FBQztJQUVNLEtBQUs7UUFDVixPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQztJQUN6QyxDQUFDOztBQXBDZSxtQkFBUyxHQUFHLFVBQVUsQ0FBQztBQXVDekMsaUJBQWlCLENBQUMsV0FBVyxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7SUFDNUMsRUFBRSxDQUFDLDBCQUEwQixFQUFFLEdBQUcsRUFBRTtRQUNsQyxNQUFNLEdBQUcsR0FBRyxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNqQyxNQUFNLENBQUMsR0FBRyxJQUFJLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM1QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRTtZQUNuQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDO1NBQ3BCO1FBRUQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxNQUFjLEVBQUUsWUFBb0IsRUFBRSxFQUFFLENBQzdELE1BQU0sQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO1FBRXhFLGVBQWUsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN2QyxlQUFlLENBQUMsQ0FBQyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDdkMsZUFBZSxDQUFDLENBQUMsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3ZDLGVBQWUsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN2QyxlQUFlLENBQUMsQ0FBQyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDdkMsZUFBZSxDQUFDLENBQUMsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3ZDLGVBQWUsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN2QyxlQUFlLENBQUMsQ0FBQyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDdkMsZUFBZSxDQUFDLEVBQUUsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3hDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN4QyxlQUFlLENBQUMsRUFBRSxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDeEMsZUFBZSxDQUFDLEVBQUUsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3hDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN4QyxlQUFlLENBQUMsRUFBRSxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDeEMsZUFBZSxDQUFDLEVBQUUsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3hDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN4QyxlQUFlLENBQUMsRUFBRSxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDeEMsZUFBZSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3pDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN6QyxlQUFlLENBQUMsR0FBRyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDekMsZUFBZSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3pDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN6QyxlQUFlLENBQUMsR0FBRyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDekMsZUFBZSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3pDLGVBQWUsQ0FBQyxHQUFHLEVB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QUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQyxlQUFlLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDaEQsT0FBTyxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7UUFDNUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUNoRCxPQUFPLENBQUMsU0FBUyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQztRQUM1QyxNQUFNLENBQUMsZUFBZSxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ2hELE9BQU8sQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQyxlQUFlLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDaEQsT0FBTyxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7SUFDOUMsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDIxIEdvb2dsZSBMTEMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKi9cblxuaW1wb3J0IHtmaW5nZXJQcmludDY0LCBoZXhUb0xvbmd9IGZyb20gJy4vaGFzaF91dGlsJztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuL2phc21pbmVfdXRpbCc7XG5cbi8qKlxuICogVGhlIEFDTVJhbmRvbSBnZW5lcmF0b3IgaXMgZm9yIHNpdHVhdGlvbnMgd2hlcmUgZXh0cmVtZSBzdGF0aXN0aWNhbCBxdWFsaXR5XG4gKiBpcyBub3QgaW1wb3J0YW50LiBBQ01SYW5kb20gaXMgdXNlZnVsIGZvciB0ZXN0aW5nIHNpbmNlIGl0IGlzIHNlZWRlZCBhbGxvd2luZ1xuICogZm9yIHJlcHJvZHVjaWJsZSByZXN1bHRzIGFzIHdlbGwgYXMgbG93IG92ZXJoZWFkIHNvIHVzaW5nIGl0IHdpbGxcbiAqIG5vdCBhZmZlY3QgdGVzdCBzcGVlZC5cbiAqL1xuY2xhc3MgQUNNUmFuZG9tIHtcbiAgc3RhdGljIHJlYWRvbmx5IE1BWF9JTlQzMiA9IDIxNDc0ODM2NDc7XG4gIHByaXZhdGUgc2VlZDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNlZWQ6IG51bWJlcikge1xuICAgIHNlZWQgPSBzZWVkICYgMHg3ZmZmZmZmZjtcbiAgICBpZiAoc2VlZCA9PT0gMCB8fCBzZWVkID09PSBBQ01SYW5kb20uTUFYX0lOVDMyKSB7XG4gICAgICBzZWVkID0gMTtcbiAgICB9XG4gICAgdGhpcy5zZWVkID0gc2VlZDtcbiAgfVxuXG4gIHByaXZhdGUgbmV4dCgpIHtcbiAgICBjb25zdCBBID0gaGV4VG9Mb25nKCc0MUE3Jyk7ICAvLyBiaXRzIDE0LCA4LCA3LCA1LCAyLCAxLCAwXG4gICAgY29uc3QgTUFYX0lOVDMyID0gQUNNUmFuZG9tLk1BWF9JTlQzMjtcbiAgICAvLyBXZSBhcmUgY29tcHV0aW5nXG4gICAgLy8gICAgICAgc2VlZCA9IChzZWVkICogQSkgJSBNQVhfSU5UMzIsICAgIHdoZXJlIE1BWF9JTlQzMiA9IDJeMzEtMVxuICAgIC8vXG4gICAgLy8gc2VlZCBtdXN0IG5vdCBiZSB6ZXJvIG9yIE1BWF9JTlQzMiwgb3IgZWxzZSBhbGwgc3Vic2VxdWVudCBjb21wdXRlZFxuICAgIC8vIHZhbHVlcyB3aWxsIGJlIHplcm8gb3IgTUFYX0lOVDMyIHJlc3BlY3RpdmVseS4gIEZvciBhbGwgb3RoZXIgdmFsdWVzLFxuICAgIC8vIHNlZWQgd2lsbCBlbmQgdXAgY3ljbGluZyB0aHJvdWdoIGV2ZXJ5IG51bWJlciBpbiBbMSxNQVhfSU5UMzItMV1cbiAgICBjb25zdCBwcm9kdWN0ID0gQS5tdWwodGhpcy5zZWVkKTtcblxuICAgIC8vIENvbXB1dGUgKHByb2R1Y3QgJSBNQVhfSU5UMzIpIHVzaW5nIHRoZSBmYWN0IHRoYXRcbiAgICAvLyAoKHggPDwgMzEpICUgTUFYX0lOVDMyKSA9PSB4LlxuICAgIHRoaXMuc2VlZCA9IHByb2R1Y3Quc2hydSgzMSkuYWRkKHByb2R1Y3QuYW5kKE1BWF9JTlQzMikpLmdldExvd0JpdHMoKTtcbiAgICAvLyBUaGUgZmlyc3QgcmVkdWN0aW9uIG1heSBvdmVyZmxvdyBieSAxIGJpdCwgc28gd2UgbWF5IG5lZWQgdG8gcmVwZWF0LlxuICAgIC8vIG1vZCA9PSBNQVhfSU5UMzIgaXMgbm90IHBvc3NpYmxlOyB1c2luZyA+IGFsbG93cyBmb3IgdGhlIGZhc3RlclxuICAgIC8vIHNpZ24tYml0LWJhc2VkIHRlc3QuXG4gICAgaWYgKHRoaXMuc2VlZCA+IE1BWF9JTlQzMikge1xuICAgICAgdGhpcy5zZWVkIC09IE1BWF9JTlQzMjtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuc2VlZDtcbiAgfVxuXG4gIHB1YmxpYyByYW5kOCgpIHtcbiAgICByZXR1cm4gKHRoaXMubmV4dCgpID4+IDEpICYgMHgwMDAwMDBmZjtcbiAgfVxufVxuXG5kZXNjcmliZVdpdGhGbGFncygnaGFzaF91dGlsJywgQUxMX0VOVlMsICgpID0+IHtcbiAgaXQoJ2NoZWNrIGluY3JlbWVudGFsIGhhc2hlcycsICgpID0+IHtcbiAgICBjb25zdCBidWYgPSBuZXcgVWludDhBcnJheSgxMDAwKTtcbiAgICBjb25zdCByID0gbmV3IEFDTVJhbmRvbSgxMCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBidWYubGVuZ3RoOyArK2kpIHtcbiAgICAgIGJ1ZltpXSA9IHIucmFuZDgoKTtcbiAgICB9XG5cbiAgICBjb25zdCBleHBlY3RJdGVyYXRpb24gPSAobGVuZ3RoOiBudW1iZXIsIGV4cGVjdGVkSGFzaDogc3RyaW5nKSA9PlxuICAgICAgICBleHBlY3QoZmluZ2VyUHJpbnQ2NChidWYsIGxlbmd0aCkpLnRvRXF1YWwoaGV4VG9Mb25nKGV4cGVjdGVkSGFzaCkpO1xuXG4gICAgZXhwZWN0SXRlcmF0aW9uKDAsICc5YWUxNmEzYjJmOTA0MDRmJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDEsICc0OWQ4YTVlM2ZhOTNjMzI3Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDIsICdmZDI1OWFiYjBmZjJiZjEyJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDMsICc3ODFmMWM2NDM3MDk2YWMyJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDQsICcwZjEzNjlkNmMwYjQ1NzE2Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDUsICcwMmQ4Y2VjNjM5NGRlMDlhJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDcsICcxZmFmNmM2ZDQzNjI2YzQ4Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDksICdjOTNlZmQ2ZGJlMTM5YmU4Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDEyLCAnNjVhYmJjOTY3Yzg3ZDUxNScpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbigxNiwgJzNmNjE0NTBhMDNjZmY1YWYnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oMjAsICc1ZDJmZTI5N2U0NWZlZDFhJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDI2LCAnZWI2NjU5ODNhZWI5YWI5NCcpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbigzMywgJzNhNWYzODkwYjEyNGI0YTMnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oNDIsICdmMGMxY2Q2NmQ3ZDlmMjQ2Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDUzLCAnY2Y3ZTNlNGIxZWZlYmE2ZCcpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbig2NywgJzBjZWQ3NTNiNDU3NDA4NzUnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oODQsICdhNTg1ZTBiZTAxODQ2ZmY0Jyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDEwNSwgJ2ZiNjQ5NmRlYjM1NmNkZGEnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oMTMyLCAnZjJlNGM1YjZkYjZjMTU0YScpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbigxNjYsICc0NDk4NDUxYzNiY2E4NWEwJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDIwOCwgJ2Q2MDQzNTVmYTRkMGIxNGUnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oMjYxLCAnYmIxNjVlNmI4NGJhOWNkZicpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbigzMjcsICc5ZWJmYWI0NTE5YjEzNDhjJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDQwOSwgJzU5MjE5NzRiYTJlOWE1YzInKTtcbiAgICBleHBlY3RJdGVyYXRpb24oNTEyLCAnYTg2YTk2ZTdhNDQyODJlMycpO1xuICAgIGV4cGVjdEl0ZXJhdGlvbig2NDAsICdkZDczMWRmZWU1MDBhYTNjJyk7XG4gICAgZXhwZWN0SXRlcmF0aW9uKDgwMCwgJ2E2OWYzNDAwZTZjOTgzNTcnKTtcbiAgICBleHBlY3RJdGVyYXRpb24oMTAwMCwgJzVjNjNiNjY0NDM5OTBiZWMnKTtcbiAgfSk7XG5cbiAgLy8gVGhpcyBpcyBtb3JlIHRob3JvdWdoLCBidXQgaWYgc29tZXRoaW5nIGlzIHdyb25nIHRoZSBvdXRwdXQgd2lsbCBiZSBldmVuXG4gIC8vIGxlc3MgaWxsdW1pbmF0aW5nLCBiZWNhdXNlIGl0IGp1c3QgY2hlY2tzIG9uZSBpbnRlZ2VyIGF0IHRoZSBlbmQuXG4gIGl0KCdjaGVjayBtYW55IGRpZmZlcmVudCBzdHJpbmdzJywgKCkgPT4ge1xuICAgIGNvbnN0IGl0ZXJzID0gODAwO1xuICAgIGNvbnN0IHMgPSBuZXcgVWludDhBcnJheSg0ICogaXRlcnMpO1xuICAgIGxldCBsZW4gPSAwO1xuICAgIGxldCBoID0gaGV4VG9Mb25nKCcwJyk7XG5cbiAgICAvLyBIZWxwZXIgdGhhdCByZXBsYWNlcyBoIHdpdGggYSBoYXNoIG9mIGl0c2VsZiBhbmQgcmV0dXJuIGFcbiAgICAvLyBjaGFyIHRoYXQgaXMgYWxzbyBhIGhhc2ggb2YgaC4gIE5laXRoZXIgaGFzaCBuZWVkcyB0byBiZSBwYXJ0aWN1bGFybHlcbiAgICAvLyBnb29kLlxuICAgIGNvbnN0IHJlbWl4ID0gKCk6IG51bWJlciA9PiB7XG4gICAgICBoID0gaC54b3IoaC5zaHJ1KDQxKSk7XG4gICAgICBoID0gaC5tdWwoOTQ5OTIxOTc5KTtcbiAgICAgIHJldHVybiAnYScuY2hhckNvZGVBdCgwKSArIGguYW5kKDB4ZmZmZmYpLm1vZCgyNikuZ2V0TG93Qml0cygpO1xuICAgIH07XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGl0ZXJzOyBpKyspIHtcbiAgICAgIGggPSBoLnhvcihmaW5nZXJQcmludDY0KHMsIGkpKTtcbiAgICAgIHNbbGVuKytdID0gcmVtaXgoKTtcbiAgICAgIGggPSBoLnhvcihmaW5nZXJQcmludDY0KHMsIGkgKiBpICUgbGVuKSk7XG4gICAgICBzW2xlbisrXSA9IHJlbWl4KCk7XG4gICAgICBoID0gaC54b3IoZmluZ2VyUHJpbnQ2NChzLCBpICogaSAqIGkgJSBsZW4pKTtcbiAgICAgIHNbbGVuKytdID0gcmVtaXgoKTtcbiAgICAgIGggPSBoLnhvcihmaW5nZXJQcmludDY0KHMsIGxlbikpO1xuICAgICAgc1tsZW4rK10gPSByZW1peCgpO1xuICAgICAgY29uc3QgeDAgPSBzW2xlbiAtIDFdO1xuICAgICAgY29uc3QgeDEgPSBzW2xlbiAtIDJdO1xuICAgICAgY29uc3QgeDIgPSBzW2xlbiAtIDNdO1xuICAgICAgY29uc3QgeDMgPSBzW2xlbiA+PiAxXTtcbiAgICAgIHNbKCh4MCA8PCAxNikgKyAoeDEgPDwgOCkgKyB4MikgJSBsZW5dIF49IHgzO1xuICAgICAgc1soKHgxIDw8IDE2KSArICh4MiA8PCA4KSArIHgzKSAlIGxlbl0gXj0gaSAlIDI1NjtcbiAgICB9XG5cbiAgICBleHBlY3QoaCkudG9FcXVhbChoZXhUb0xvbmcoJzdhMWQ2N2M1MGVjN2UxNjcnKSk7XG4gIH0pO1xuXG4gIGl0KCdjaGVjayBzdHJpbmcgaGFzaCcsICgpID0+IHtcbiAgICBjb25zdCBmaW5nZXJQcmludEhhc2ggPSAoaGFzaDogTG9uZykgPT4ge1xuICAgICAgY29uc3QgbXVsID0gaGV4VG9Mb25nKCc5ZGRmZWEwOGViMzgyZDY5Jyk7XG4gICAgICBsZXQgYiA9IGhhc2gubXVsKG11bCk7XG4gICAgICBiID0gYi54b3IoYi5zaHJ1KDQ0KSk7XG4gICAgICBiID0gYi5tdWwobXVsKTtcbiAgICAgIGIgPSBiLnhvcihiLnNocnUoNDEpKTtcbiAgICAgIGIgPSBiLm11bChtdWwpO1xuICAgICAgcmV0dXJuIGI7XG4gICAgfTtcblxuICAgIGNvbnN0IGdldFN0cmluZyA9IChsZW5ndGg6IG51bWJlcikgPT4gVWludDhBcnJheS5mcm9tKFxuICAgICAgICAneCcucmVwZWF0KGxlbmd0aCkuc3BsaXQoJycpLm1hcChjaGFyID0+IGNoYXIuY2hhckNvZGVBdCgwKSkpO1xuXG4gICAgZXhwZWN0KGZpbmdlclByaW50SGFzaChmaW5nZXJQcmludDY0KGdldFN0cmluZyg0MCkpKSlcbiAgICAgICAgLnRvRXF1YWwoaGV4VG9Mb25nKCcyMTE3MTcwYzRhZWJhZmZlJykpO1xuICAgIGV4cGVjdChmaW5nZXJQcmludEhhc2goZmluZ2VyUHJpbnQ2NChnZXRTdHJpbmcoNjApKSkpXG4gICAgICAgIC50b0VxdWFsKGhleFRvTG9uZygnZTI1Mjk2M2YzZmQ3YTNhZicpKTtcbiAgICBleHBlY3QoZmluZ2VyUHJpbnRIYXNoKGZpbmdlclByaW50NjQoZ2V0U3RyaW5nKDcwKSkpKVxuICAgICAgICAudG9FcXVhbChoZXhUb0xvbmcoJ2IwYThjZjRhNTZjNTcwZmEnKSk7XG4gICAgZXhwZWN0KGZpbmdlclByaW50SGFzaChmaW5nZXJQcmludDY0KGdldFN0cmluZyg4MCkpKSlcbiAgICAgICAgLnRvRXF1YWwoaGV4VG9Mb25nKCdkNmRkYWE0OWRkZWY1ODM5JykpO1xuICAgIGV4cGVjdChmaW5nZXJQcmludEhhc2goZmluZ2VyUHJpbnQ2NChnZXRTdHJpbmcoOTApKSkpXG4gICAgICAgIC50b0VxdWFsKGhleFRvTG9uZygnMTY4ZjNhNjk0YjRkY2UyOScpKTtcbiAgfSk7XG59KTtcbiJdfQ==