cacophony 0.30.3 → 0.30.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/basePlayback.d.ts +98 -0
- package/dist/cacophony-PShY6dPK.cjs.map +1 -1
- package/dist/cacophony-o1zE5oDP.js.map +1 -1
- package/dist/container.d.ts +10 -105
- package/dist/processors/timestretch-core.d.ts +53 -0
- package/dist/routableSource.d.ts +2 -27
- package/dist/volumeMixin.d.ts +23 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/AudioCache.html +4 -4
- package/docs/classes/BarberpoleEffect.html +6 -6
- package/docs/classes/BiquadEffect.html +3 -3
- package/docs/classes/Bus.html +21 -21
- package/docs/classes/Cacophony.html +85 -85
- package/docs/classes/DynamicsEffect.html +6 -6
- package/docs/classes/FdnReverbEffect.html +6 -6
- package/docs/classes/FoaDecoder.html +5 -5
- package/docs/classes/FoaDecoderEffect.html +3 -3
- package/docs/classes/FrequencyShifterEffect.html +6 -6
- package/docs/classes/Group.html +32 -32
- package/docs/classes/HarmonizerEffect.html +6 -6
- package/docs/classes/ImpulseResponseEffect.html +3 -3
- package/docs/classes/KWeightingFilter.html +4 -4
- package/docs/classes/LoudnessMeter.html +11 -11
- package/docs/classes/MediaStreamPlayback.html +78 -113
- package/docs/classes/MediaStreamSound.html +73 -143
- package/docs/classes/MicrophoneStream.html +74 -144
- package/docs/classes/ModulatedDelayEffect.html +6 -6
- package/docs/classes/PcmStreamPlayback.html +79 -114
- package/docs/classes/PcmStreamSound.html +83 -153
- package/docs/classes/PhaserEffect.html +6 -6
- package/docs/classes/Playback.html +94 -122
- package/docs/classes/ReverbEffect.html +6 -6
- package/docs/classes/ShareEffect.html +3 -3
- package/docs/classes/Sound.html +92 -162
- package/docs/classes/SpectralFreezeEffect.html +6 -6
- package/docs/classes/StereoWidenerEffect.html +3 -3
- package/docs/classes/Synth.html +91 -161
- package/docs/classes/SynthGroup.html +28 -28
- package/docs/classes/TremoloEffect.html +6 -6
- package/docs/classes/TruePeakDetector.html +8 -8
- package/docs/classes/WaveshaperEffect.html +6 -6
- package/docs/functions/encodeMonoToFoaSN3D.html +1 -1
- package/docs/functions/integratedLoudness.html +1 -1
- package/docs/functions/integratedUngatedLoudness.html +1 -1
- package/docs/functions/loudnessRange.html +1 -1
- package/docs/functions/timeStretch.html +1 -1
- package/docs/functions/timeStretchChannels.html +1 -1
- package/docs/functions/truePeakDb.html +1 -1
- package/docs/hierarchy.html +7 -7
- package/docs/interfaces/AudioBuffer.html +8 -8
- package/docs/interfaces/AudioBufferSourceNode.html +21 -21
- package/docs/interfaces/AudioEventCallbacks.html +1 -1
- package/docs/interfaces/AudioListener.html +12 -12
- package/docs/interfaces/AudioNode.html +13 -13
- package/docs/interfaces/AudioParam.html +6 -6
- package/docs/interfaces/AudioWorklet.html +2 -2
- package/docs/interfaces/AudioWorkletNode.html +15 -15
- package/docs/interfaces/BarberpoleOptions.html +5 -5
- package/docs/interfaces/BaseContext.html +25 -25
- package/docs/interfaces/BaseSound.html +16 -16
- package/docs/interfaces/BiquadCoefficients.html +6 -6
- package/docs/interfaces/BiquadFilterNode.html +19 -19
- package/docs/interfaces/BuiltEffectGraph.html +6 -6
- package/docs/interfaces/CacheErrorEvent.html +5 -5
- package/docs/interfaces/CacheHitEvent.html +4 -4
- package/docs/interfaces/CacheMissEvent.html +4 -4
- package/docs/interfaces/CacophonyEffect.html +2 -2
- package/docs/interfaces/CacophonyLogger.html +4 -4
- package/docs/interfaces/ChannelMergerNode.html +13 -13
- package/docs/interfaces/ChannelSplitterNode.html +13 -13
- package/docs/interfaces/ConvolverNode.html +15 -15
- package/docs/interfaces/DynamicsOptions.html +7 -7
- package/docs/interfaces/FadeStartEvent.html +4 -4
- package/docs/interfaces/FdnReverbOptions.html +6 -6
- package/docs/interfaces/FoaDecoderOptions.html +2 -2
- package/docs/interfaces/FrequencyShifterOptions.html +3 -3
- package/docs/interfaces/GainNode.html +14 -14
- package/docs/interfaces/GlobalPlaybackEvent.html +3 -3
- package/docs/interfaces/HarmonizerOptions.html +6 -6
- package/docs/interfaces/ImpulseResponseOptions.html +5 -5
- package/docs/interfaces/LoadingCompleteEvent.html +5 -5
- package/docs/interfaces/LoadingErrorEvent.html +5 -5
- package/docs/interfaces/LoadingProgressEvent.html +6 -6
- package/docs/interfaces/LoadingStartEvent.html +3 -3
- package/docs/interfaces/LoudnessChannelInput.html +3 -3
- package/docs/interfaces/LoudnessReading.html +5 -5
- package/docs/interfaces/MediaElementSourceNode.html +14 -14
- package/docs/interfaces/MediaStreamAudioSourceNode.html +14 -14
- package/docs/interfaces/MediaStreamSoundOptions.html +4 -4
- package/docs/interfaces/MicrophoneStreamOptions.html +8 -8
- package/docs/interfaces/ModulatedDelayOptions.html +8 -8
- package/docs/interfaces/OfflineOptions.html +5 -5
- package/docs/interfaces/OscillatorNode.html +19 -19
- package/docs/interfaces/PannerNode.html +27 -27
- package/docs/interfaces/PcmStreamBufferEvent.html +2 -2
- package/docs/interfaces/PcmStreamSoundOptions.html +6 -6
- package/docs/interfaces/PhaserOptions.html +7 -7
- package/docs/interfaces/PlayOptions.html +5 -5
- package/docs/interfaces/PlaybackErrorEvent.html +5 -5
- package/docs/interfaces/ReverbOptions.html +13 -13
- package/docs/interfaces/RuntimeOptions.html +6 -6
- package/docs/interfaces/SoundCleanupHoldings.html +4 -4
- package/docs/interfaces/SoundErrorEvent.html +6 -6
- package/docs/interfaces/SpectralFreezeOptions.html +4 -4
- package/docs/interfaces/StereoPannerNode.html +14 -14
- package/docs/interfaces/StereoWidenerOptions.html +4 -4
- package/docs/interfaces/StreamCapabilities.html +5 -5
- package/docs/interfaces/TimeStretchOptions.html +5 -5
- package/docs/interfaces/TremoloOptions.html +5 -5
- package/docs/interfaces/WaveshaperOptions.html +5 -5
- package/docs/types/BaseAudioEvents.html +11 -11
- package/docs/types/BuiltEffect.html +1 -1
- package/docs/types/BusConnectionTarget.html +1 -1
- package/docs/types/CacheEventCallback.html +1 -1
- package/docs/types/CacophonyEvents.html +17 -17
- package/docs/types/ErrorEventCallback.html +1 -1
- package/docs/types/FadeType.html +1 -1
- package/docs/types/HrtfPannerOptions.html +1 -1
- package/docs/types/ImpulseResponseSource.html +1 -1
- package/docs/types/LoadingEventCallback.html +1 -1
- package/docs/types/LoopCount.html +1 -1
- package/docs/types/LoudnessChannel.html +1 -1
- package/docs/types/Orientation.html +3 -3
- package/docs/types/PanCloneOverrides.html +5 -5
- package/docs/types/PanType.html +1 -1
- package/docs/types/PcmStreamEvents.html +6 -6
- package/docs/types/PcmStreamState.html +1 -1
- package/docs/types/PlaybackEvents.html +1 -1
- package/docs/types/Position.html +1 -1
- package/docs/types/SoundEvents.html +1 -1
- package/docs/types/SoundType.html +1 -1
- package/docs/types/SourceNode.html +1 -1
- package/docs/types/SynthEvents.html +1 -1
- package/docs/types/ThreeDOptions.html +1 -1
- package/docs/types/WebCodecsStreamSound.html +1 -1
- package/docs/variables/CHANNEL_WEIGHTS.html +1 -1
- package/docs/variables/K_WEIGHTING_STAGE1_48K.html +1 -1
- package/docs/variables/K_WEIGHTING_STAGE2_48K.html +1 -1
- package/docs/variables/consoleLogger.html +1 -1
- package/docs/variables/noopLogger.html +1 -1
- package/package.json +3 -2
- package/dist/cli/exit.test.d.ts +0 -1
- package/dist/cli/index.test.d.ts +0 -1
- package/dist/cli/live.test.d.ts +0 -1
- package/dist/cli/loudness.test.d.ts +0 -1
- package/dist/cli/package.test.d.ts +0 -1
- package/dist/cli/params.test.d.ts +0 -1
- package/dist/cli/render-core.test.d.ts +0 -1
- package/dist/cli/render.test.d.ts +0 -1
- package/dist/cli/repl.test.d.ts +0 -1
- package/dist/cli/wav.test.d.ts +0 -1
- package/dist/meters/loudness-core.test.d.ts +0 -1
- package/dist/meters/loudness-meter.test.d.ts +0 -1
- package/dist/meters/truepeak-core.test.d.ts +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cacophony-PShY6dPK.cjs","names":[],"sources":["../src/bundles/barberpole-bundle.js?url","../src/bundles/dattorro-reverb-bundle.js?url","../src/bundles/dynamics-bundle.js?url","../src/bundles/fdn-reverb-bundle.js?url","../src/bundles/frequency-shifter-bundle.js?url","../src/bundles/harmonizer-bundle.js?url","../src/bundles/loudness-meter-bundle.js?url","../src/bundles/modulated-delay-bundle.js?url","../src/bundles/pcm-stream-bundle.js?url","../src/bundles/phase-vocoder-bundle.js?url","../src/bundles/phaser-bundle.js?url","../src/bundles/spectral-freeze-bundle.js?url","../src/bundles/stereo-to-bformat-bundle.js?url","../src/bundles/stereo-widener-bundle.js?url","../src/bundles/tremolo-bundle.js?url","../src/bundles/waveshaper-bundle.js?url","../src/worklets.ts","../src/effects.ts","../src/effectChain.ts","../src/bus.ts","../src/cache.ts","../src/assets/sh_hrir_order_1.wav?url","../src/autoplayUnlock.ts","../src/eventEmitter.ts","../src/group.ts","../src/hlsAdapter.ts","../src/logger.ts","../src/filters.ts","../src/pannerMixin.ts","../src/volumeMixin.ts","../src/basePlayback.ts","../src/container.ts","../src/routableSource.ts","../src/mediaStream.ts","../src/meters/loudness-meter.ts","../src/microphone.ts","../src/pcmStream.ts","../src/processors/dynamics-core.ts","../src/processors/modulated-delay-core.ts","../src/processors/timestretch-core.ts","../src/playback.ts","../src/sound.ts","../src/oscillatorMixin.ts","../src/synthPlayback.ts","../src/synth.ts","../src/webCodecsStream.ts","../src/cacophony.ts"],"sourcesContent":["export default \"data:text/javascript;base64,dmFyIGJhcmJlcnBvbGUgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIEZJUi1IaWxiZXJ0IHNpbmdsZS1zaWRlYmFuZCBmcmVxdWVuY3kgc2hpZnRlci4KICAgICAqCiAgICAgKiBTY290dCBXYXJkbGUsICJBIEhpbGJlcnQtVHJhbnNmb3JtZXIgRnJlcXVlbmN5IFNoaWZ0ZXIgZm9yIEF1ZGlvIiwKICAgICAqIERBRngtOTgsIGVxLiA1YzoKICAgICAqCiAgICAgKiAgIHkodCkgPSB4KHQpIGNvcyh3X2MgdCkgLSBIe3godCl9IHNpbih3X2MgdCkKICAgICAqCiAgICAgKiBUaGUgcmVhbCBwYXRoIGlzIGRlbGF5ZWQgYnkgdGhlIEZJUiBncm91cCBkZWxheSBzbyBpdCByZW1haW5zIGluIHF1YWRyYXR1cmUKICAgICAqIHdpdGggdGhlIEhpbGJlcnQgcGF0aC4gQSBzaWduZWQgY2FycmllciBmcmVxdWVuY3kgY2hvb3NlcyB1cHBlci9sb3dlcgogICAgICogc2lkZWJhbmQgdHJhbnNsYXRpb24gd2l0aG91dCBjaGFuZ2luZyB0aGUgYWxnb3JpdGhtLgogICAgICovCiAgICBjb25zdCBERUZBVUxUX1RBUFMgPSAxMjc7CiAgICAvKiogV2luZG93ZWQgaWRlYWwgb2RkLXN5bW1ldHJpYyBIaWxiZXJ0LXRyYW5zZm9ybSBGSVIuICovCiAgICBmdW5jdGlvbiBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgaWYgKHRhcENvdW50IDwgNyB8fCB0YXBDb3VudCAlIDIgPT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIkhpbGJlcnQgRklSIHRhcCBjb3VudCBtdXN0IGJlIGFuIG9kZCBpbnRlZ2VyID49IDciKTsKICAgICAgICB9CiAgICAgICAgY29uc3QgY2VudGVyID0gKHRhcENvdW50IC0gMSkgLyAyOwogICAgICAgIGNvbnN0IGNvZWZmaWNpZW50cyA9IG5ldyBGbG9hdDY0QXJyYXkodGFwQ291bnQpOwogICAgICAgIGZvciAobGV0IG4gPSAwOyBuIDwgdGFwQ291bnQ7IG4rKykgewogICAgICAgICAgICBjb25zdCBtID0gbiAtIGNlbnRlcjsKICAgICAgICAgICAgaWYgKG0gIT09IDAgJiYgTWF0aC5hYnMobSkgJSAyID09PSAxKSB7CiAgICAgICAgICAgICAgICBjb25zdCB3aW5kb3cgPSAwLjQyIC0gMC41ICogTWF0aC5jb3MoKDIgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSkgKyAwLjA4ICogTWF0aC5jb3MoKDQgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSk7CiAgICAgICAgICAgICAgICBjb2VmZmljaWVudHNbbl0gPSAoMiAvIChNYXRoLlBJICogbSkpICogd2luZG93OwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiBjb2VmZmljaWVudHM7CiAgICB9CiAgICAvKiogU3RyZWFtaW5nIEZJUiBIaWxiZXJ0IHRyYW5zZm9ybWVyIHdpdGggYW4gYWxpZ25lZCBkaXJlY3QgdGFwLiAqLwogICAgY2xhc3MgSGlsYmVydFRyYW5zZm9ybWVyIHsKICAgICAgICBsYXRlbmN5U2FtcGxlczsKICAgICAgICBjb2VmZmljaWVudHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodGFwQ291bnQgPSBERUZBVUxUX1RBUFMpIHsKICAgICAgICAgICAgdGhpcy5jb2VmZmljaWVudHMgPSBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5U2FtcGxlcyA9ICh0YXBDb3VudCAtIDEpIC8gMjsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0YXBDb3VudCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQ2NEFycmF5KHNpemUpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBzaXplIC0gMTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlclt0aGlzLndyaXRlSW5kZXhdID0gaW5wdXQ7CiAgICAgICAgICAgIGxldCBxdWFkcmF0dXJlID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNvZWZmaWNpZW50cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgcXVhZHJhdHVyZSArPSB0aGlzLmNvZWZmaWNpZW50c1tpXSAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSBpKSAmIHRoaXMubWFza107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGlyZWN0ID0gdGhpcy5idWZmZXJbKHRoaXMud3JpdGVJbmRleCAtIHRoaXMubGF0ZW5jeVNhbXBsZXMpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4geyBkaXJlY3QsIHF1YWRyYXR1cmUgfTsKICAgICAgICB9CiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIC8qKiBGaXJzdC1vcmRlciBhbGxwYXNzIEEoeik9KGErel4tMSkvKDErYSB6Xi0xKSwgREFGeC0xNSBlcS4gMTQuICovCiAgICBjbGFzcyBGaXJzdE9yZGVyQWxscGFzcyB7CiAgICAgICAgeDEgPSAwOwogICAgICAgIHkxID0gMDsKICAgICAgICBwcm9jZXNzKGlucHV0LCBjb2VmZmljaWVudCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBjb2VmZmljaWVudCAqIGlucHV0ICsgdGhpcy54MSAtIGNvZWZmaWNpZW50ICogdGhpcy55MTsKICAgICAgICAgICAgdGhpcy54MSA9IGlucHV0OwogICAgICAgICAgICB0aGlzLnkxID0gb3V0cHV0OwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgIH0KICAgIC8qKgogICAgICogU1NCIGJhcmJlcnBvbGUgcGhhc2VyIGZyb20gRXNxdWVkYSwgVmFsaW1ha2kgJiBQYXJrZXIsIERBRngtMTUgRmlnLiAxMi4KICAgICAqIFRoZSBIaWxiZXJ0IHNoaWZ0ZXIgc3VwcGxpZXMgdGhlIG1vdmluZyBjb3B5OyBhIGNhc2NhZGVkIGFsbHBhc3Mgc3BlY3RyYWwKICAgICAqIGRlbGF5IHdhcnBzIHRoZSBub3RjaCBzcGFjaW5nIHRvd2FyZCB0aGUgU2hlcGFyZC1SaXNzZXQgb2N0YXZlIGRpc3RyaWJ1dGlvbi4KICAgICAqLwogICAgY2xhc3MgQmFyYmVycG9sZUNvcmUgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgaGlsYmVydCA9IG5ldyBIaWxiZXJ0VHJhbnNmb3JtZXIoKTsKICAgICAgICBhbGxwYXNzZXMgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiA2NCB9LCAoKSA9PiBuZXcgRmlyc3RPcmRlckFsbHBhc3MoKSk7CiAgICAgICAgcGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5zYW1wbGVSYXRlID0gc2FtcGxlUmF0ZTsKICAgICAgICB9CiAgICAgICAgcHJvY2Vzc1NhbXBsZShpbnB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IHBhaXIgPSB0aGlzLmhpbGJlcnQucHJvY2VzcyhpbnB1dCk7CiAgICAgICAgICAgIGxldCBzaGlmdGVkID0gcGFpci5kaXJlY3QgKiBNYXRoLmNvcyh0aGlzLnBoYXNlKSAtIHBhaXIucXVhZHJhdHVyZSAqIE1hdGguc2luKHRoaXMucGhhc2UpOwogICAgICAgICAgICBjb25zdCBzdGFnZXMgPSBNYXRoLm1heCgyLCBNYXRoLm1pbih0aGlzLmFsbHBhc3Nlcy5sZW5ndGgsIE1hdGgucm91bmQocGFyYW1zLnN0YWdlcykpKTsKICAgICAgICAgICAgY29uc3QgY29lZmZpY2llbnQgPSBNYXRoLm1heCgtMC45NSwgTWF0aC5taW4oMC45NSwgcGFyYW1zLmNvZWZmaWNpZW50KSk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhZ2VzOyBpKyspCiAgICAgICAgICAgICAgICBzaGlmdGVkID0gdGhpcy5hbGxwYXNzZXNbaV0ucHJvY2VzcyhzaGlmdGVkLCBjb2VmZmljaWVudCk7CiAgICAgICAgICAgIGNvbnN0IHJhdGUgPSBNYXRoLm1heCgtMiwgTWF0aC5taW4oMiwgcGFyYW1zLnJhdGUpKTsKICAgICAgICAgICAgdGhpcy5waGFzZSArPSAoMiAqIE1hdGguUEkgKiByYXRlKSAvIHRoaXMuc2FtcGxlUmF0ZTsKICAgICAgICAgICAgaWYgKHRoaXMucGhhc2UgPiBNYXRoLlBJIHx8IHRoaXMucGhhc2UgPCAtTWF0aC5QSSkKICAgICAgICAgICAgICAgIHRoaXMucGhhc2UgLT0gTWF0aC5yb3VuZCh0aGlzLnBoYXNlIC8gKDIgKiBNYXRoLlBJKSkgKiAyICogTWF0aC5QSTsKICAgICAgICAgICAgY29uc3QgYmFyYmVycG9sZSA9IDAuNSAqIChwYWlyLmRpcmVjdCArIHNoaWZ0ZWQpOwogICAgICAgICAgICBjb25zdCBtaXggPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBwYXJhbXMubWl4KSk7CiAgICAgICAgICAgIHJldHVybiBwYWlyLmRpcmVjdCAqICgxIC0gbWl4KSArIGJhcmJlcnBvbGUgKiBtaXg7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5wdXQubGVuZ3RoOyBpKyspCiAgICAgICAgICAgICAgICBvdXRwdXRbaV0gPSB0aGlzLnByb2Nlc3NTYW1wbGUoaW5wdXRbaV0sIHBhcmFtcyk7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIEJhcmJlcnBvbGVXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJyYXRlIiwgZGVmYXVsdFZhbHVlOiAwLjEsIG1pblZhbHVlOiAtMiwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICAgICAgeyBuYW1lOiAic3RhZ2VzIiwgZGVmYXVsdFZhbHVlOiAzMiwgbWluVmFsdWU6IDIsIG1heFZhbHVlOiA2NCwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJjb2VmZmljaWVudCIsIGRlZmF1bHRWYWx1ZTogLTAuNSwgbWluVmFsdWU6IC0wLjk1LCBtYXhWYWx1ZTogMC45NSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJtaXgiLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgIF07CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdID8/IFtdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdID8/IFtdOwogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICByYXRlOiBwYXJhbWV0ZXJzLnJhdGVbcGFyYW1ldGVycy5yYXRlLmxlbmd0aCAtIDFdLAogICAgICAgICAgICAgICAgc3RhZ2VzOiBwYXJhbWV0ZXJzLnN0YWdlc1twYXJhbWV0ZXJzLnN0YWdlcy5sZW5ndGggLSAxXSwKICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50OiBwYXJhbWV0ZXJzLmNvZWZmaWNpZW50W3BhcmFtZXRlcnMuY29lZmZpY2llbnQubGVuZ3RoIC0gMV0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4W3BhcmFtZXRlcnMubWl4Lmxlbmd0aCAtIDFdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dC5sZW5ndGg7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgY29uc3Qgc291cmNlID0gaW5wdXRbY2hhbm5lbF0gPz8gaW5wdXRbMF07CiAgICAgICAgICAgICAgICBpZiAoIXNvdXJjZSkKICAgICAgICAgICAgICAgICAgICBvdXRwdXRbY2hhbm5lbF0uZmlsbCgwKTsKICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgICAgICAodGhpcy5jb3Jlc1tjaGFubmVsXSA/Pz0gbmV3IEJhcmJlcnBvbGVDb3JlKHNhbXBsZVJhdGUpKS5wcm9jZXNzKHNvdXJjZSwgb3V0cHV0W2NoYW5uZWxdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJiYXJiZXJwb2xlIiwgQmFyYmVycG9sZVdvcmtsZXRQcm9jZXNzb3IpOwoKICAgIGV4cG9ydHMuQmFyYmVycG9sZVdvcmtsZXRQcm9jZXNzb3IgPSBCYXJiZXJwb2xlV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9YmFyYmVycG9sZS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIGRhdHRvcnJvUmV2ZXJiID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgIERhdHRvcnJvIFJldmVyYiBBdWRpb1dvcmtsZXQgSW1wbGVtZW50YXRpb24KICAgIE9yaWdpbmFsIEphdmFTY3JpcHQgaW1wbGVtZW50YXRpb24gYnkgS2jDtGkgTmd1eeG7hW4gKGtob2luKQogICAgaHR0cHM6Ly9naXRodWIuY29tL2tob2luL0RhdHRvcnJvUmV2ZXJiTm9kZQoKICAgIEJhc2VkIG9uIEpvbiBEYXR0b3JybydzIDE5OTcgQUVTIHBhcGVyOgogICAgIkVmZmVjdCBEZXNpZ24gUGFydCAxOiBSZXZlcmJlcmF0b3IgYW5kIE90aGVyIEZpbHRlcnMiCgogICAgSW4ganVyaXNkaWN0aW9ucyB0aGF0IHJlY29nbml6ZSBjb3B5cmlnaHQgbGF3cywgdGhpcyBzb2Z0d2FyZSBpcyB0bwogICAgYmUgcmVsZWFzZWQgaW50byB0aGUgcHVibGljIGRvbWFpbi4KCiAgICBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORC4KICAgIFRIRSBBVVRIT1IoUykgU0hBTEwgTk9UIEJFIExJQUJMRSBGT1IgQU5ZVEhJTkcsIEFSSVNJTkcgRlJPTSwgT1IgSU4KICAgIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIERJU1RSSUJVVElPTiBPRiBUSEUgU09GVFdBUkUuCiAgICAqLwogICAgLyoqCiAgICAgKiBEYXR0b3JybyBQbGF0ZSBSZXZlcmIgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yCiAgICAgKgogICAgICogQmFzZWQgb24gSm9uIERhdHRvcnJvJ3MgMTk5NyBBRVMgcGFwZXIgIkVmZmVjdCBEZXNpZ24gUGFydCAxOiBSZXZlcmJlcmF0b3IgYW5kIE90aGVyIEZpbHRlcnMiCiAgICAgKiBJbXBsZW1lbnRzIGEgaGlnaC1xdWFsaXR5IHBsYXRlIHJldmVyYiBhbGdvcml0aG0gd2l0aCBtb2R1bGF0ZWQgZGVsYXkgbGluZXMuCiAgICAgKgogICAgICogQHNlZSBodHRwczovL2Njcm1hLnN0YW5mb3JkLmVkdS9+ZGF0dG9ycm8vRWZmZWN0RGVzaWduUGFydDEucGRmCiAgICAgKi8KICAgIGNsYXNzIERhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBfRGVsYXlzID0gW107CiAgICAgICAgX3BETGVuZ3RoOwogICAgICAgIF9wcmVEZWxheTsKICAgICAgICBfcERXcml0ZSA9IDA7CiAgICAgICAgX2xwMSA9IDAuMDsKICAgICAgICBfbHAyID0gMC4wOwogICAgICAgIF9scDMgPSAwLjA7CiAgICAgICAgX2V4Y1BoYXNlID0gMC4wOwogICAgICAgIF90YXBzOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbInByZURlbGF5IiwgMCwgMCwgc2FtcGxlUmF0ZSAtIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiYmFuZHdpZHRoIiwgMC45OTk5LCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImlucHV0RGlmZnVzaW9uMSIsIDAuNzUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiaW5wdXREaWZmdXNpb24yIiwgMC42MjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVjYXkiLCAwLjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVjYXlEaWZmdXNpb24xIiwgMC43LCAwLCAwLjk5OTk5OSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkZWNheURpZmZ1c2lvbjIiLCAwLjUsIDAsIDAuOTk5OTk5LCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImRhbXBpbmciLCAwLjAwNSwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJleGN1cnNpb25SYXRlIiwgMC41LCAwLCAyLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImV4Y3Vyc2lvbkRlcHRoIiwgMC43LCAwLCAyLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbIndldCIsIDAuMywgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkcnkiLCAwLjYsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKICAgICAgICAgICAgc3VwZXIob3B0aW9ucyk7CiAgICAgICAgICAgIC8vIFByZS1kZWxheSBpcyBhbHdheXMgb25lLXNlY29uZCBsb25nLCByb3VuZGVkIHRvIHRoZSBuZWFyZXN0IDEyOC1jaHVuawogICAgICAgICAgICB0aGlzLl9wRExlbmd0aCA9IHNhbXBsZVJhdGUgKyAoMTI4IC0gKHNhbXBsZVJhdGUgJSAxMjgpKTsKICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXkgPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuX3BETGVuZ3RoKTsKICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZSBkZWxheSBsaW5lcyB3aXRoIHNwZWNpZmllZCBsZW5ndGhzIChpbiBzZWNvbmRzKQogICAgICAgICAgICBbCiAgICAgICAgICAgICAgICAwLjAwNDc3MTM0NSwgMC4wMDM1OTUzMDksIDAuMDEyNzM0Nzg3LCAwLjAwOTMwNzQ4MywgMC4wMjI1Nzk4ODYsIDAuMTQ5NjI1MzQ5LCAwLjA2MDQ4MTgzOSwgMC4xMjQ5OTU4LCAwLjAzMDUwOTcyNywKICAgICAgICAgICAgICAgIDAuMTQxNjk1NTA4LCAwLjA4OTI0NDMxMywgMC4xMDYyODAwMzEsCiAgICAgICAgICAgIF0uZm9yRWFjaCgobGVuZ3RoKSA9PiB0aGlzLm1ha2VEZWxheShsZW5ndGgpKTsKICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZSB0YXAgcG9zaXRpb25zIChpbiBzZWNvbmRzKSBmb3Igc3RlcmVvIG91dHB1dAogICAgICAgICAgICB0aGlzLl90YXBzID0gSW50MTZBcnJheS5mcm9tKFsKICAgICAgICAgICAgICAgIDAuMDA4OTM3ODcyLCAwLjA5OTkyOTQzOCwgMC4wNjQyNzg3NTQsIDAuMDY3MDY3NjM5LCAwLjA2Njg2NjAzMywgMC4wMDYyODMzOTEsIDAuMDM1ODE4Njg5LCAwLjAxMTg2MTE2MSwKICAgICAgICAgICAgICAgIDAuMTIxODcwOTA1LCAwLjA0MTI2MjA1NCwgMC4wODk4MTU1MywgMC4wNzA5MzE3NTYsIDAuMDExMjU2MzQyLCAwLjAwNDA2NTcyNCwKICAgICAgICAgICAgXSwgKGxlbmd0aCkgPT4gTWF0aC5yb3VuZChsZW5ndGggKiBzYW1wbGVSYXRlKSk7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBkZWxheSBsaW5lIHdpdGggdGhlIHNwZWNpZmllZCBsZW5ndGgKICAgICAgICAgKiBVc2VzIHBvd2VyLW9mLTIgc2l6aW5nIGZvciBlZmZpY2llbnQgd3JhcHBpbmcgd2l0aCBiaXR3aXNlIEFORAogICAgICAgICAqLwogICAgICAgIG1ha2VEZWxheShsZW5ndGgpIHsKICAgICAgICAgICAgY29uc3QgbGVuID0gTWF0aC5yb3VuZChsZW5ndGggKiBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgY29uc3QgbmV4dFBvdzIgPSAyICoqIE1hdGguY2VpbChNYXRoLmxvZzIobGVuKSk7CiAgICAgICAgICAgIHRoaXMuX0RlbGF5cy5wdXNoKFsKICAgICAgICAgICAgICAgIG5ldyBGbG9hdDMyQXJyYXkobmV4dFBvdzIpLAogICAgICAgICAgICAgICAgbGVuIC0gMSwgLy8gd3JpdGUgaW5kZXgKICAgICAgICAgICAgICAgIDAsIC8vIHJlYWQgaW5kZXgKICAgICAgICAgICAgICAgIG5leHRQb3cyIC0gMSwgLy8gbWFzayBmb3Igd3JhcHBpbmcKICAgICAgICAgICAgXSk7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFdyaXRlcyBhIHNhbXBsZSB0byBhIGRlbGF5IGxpbmUgYW5kIHJldHVybnMgdGhlIHdyaXR0ZW4gdmFsdWUKICAgICAgICAgKi8KICAgICAgICB3cml0ZURlbGF5KGluZGV4LCBkYXRhKSB7CiAgICAgICAgICAgIHJldHVybiAodGhpcy5fRGVsYXlzW2luZGV4XVswXVt0aGlzLl9EZWxheXNbaW5kZXhdWzFdXSA9IGRhdGEpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBSZWFkcyB0aGUgY3VycmVudCBzYW1wbGUgZnJvbSBhIGRlbGF5IGxpbmUKICAgICAgICAgKi8KICAgICAgICByZWFkRGVsYXkoaW5kZXgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuX0RlbGF5c1tpbmRleF1bMF1bdGhpcy5fRGVsYXlzW2luZGV4XVsyXV07CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFJlYWRzIGEgc2FtcGxlIGF0IGEgc3BlY2lmaWMgb2Zmc2V0IGZyb20gdGhlIGN1cnJlbnQgcmVhZCBwb3NpdGlvbgogICAgICAgICAqLwogICAgICAgIHJlYWREZWxheUF0KGluZGV4LCBvZmZzZXQpIHsKICAgICAgICAgICAgY29uc3QgZCA9IHRoaXMuX0RlbGF5c1tpbmRleF07CiAgICAgICAgICAgIHJldHVybiBkWzBdWyhkWzJdICsgb2Zmc2V0KSAmIGRbM11dOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBSZWFkcyBhIHNhbXBsZSB3aXRoIGN1YmljIGludGVycG9sYXRpb24gYXQgYSBmcmFjdGlvbmFsIG9mZnNldAogICAgICAgICAqIFVzZXMgT2xsaSBOaWVtaXRhbG8ncyBvcHRpbWFsIGN1YmljIGludGVycG9sYXRpb24KICAgICAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lm11c2ljZHNwLm9yZy9lbi9sYXRlc3QvT3RoZXIvNDktY3ViaWMtaW50ZXJwb2xsYXRpb24uaHRtbAogICAgICAgICAqLwogICAgICAgIHJlYWREZWxheUNBdChpbmRleCwgb2Zmc2V0KSB7CiAgICAgICAgICAgIGNvbnN0IGQgPSB0aGlzLl9EZWxheXNbaW5kZXhdOwogICAgICAgICAgICBjb25zdCBmcmFjID0gb2Zmc2V0IC0gfn5vZmZzZXQ7CiAgICAgICAgICAgIGxldCBpbnQgPSB+fm9mZnNldCArIGRbMl0gLSAxOwogICAgICAgICAgICBjb25zdCBtYXNrID0gZFszXTsKICAgICAgICAgICAgY29uc3QgeDAgPSBkWzBdW2ludCsrICYgbWFza107CiAgICAgICAgICAgIGNvbnN0IHgxID0gZFswXVtpbnQrKyAmIG1hc2tdOwogICAgICAgICAgICBjb25zdCB4MiA9IGRbMF1baW50KysgJiBtYXNrXTsKICAgICAgICAgICAgY29uc3QgeDMgPSBkWzBdW2ludCAmIG1hc2tdOwogICAgICAgICAgICBjb25zdCBhID0gKDMgKiAoeDEgLSB4MikgLSB4MCArIHgzKSAvIDI7CiAgICAgICAgICAgIGNvbnN0IGIgPSAyICogeDIgKyB4MCAtICg1ICogeDEgKyB4MykgLyAyOwogICAgICAgICAgICBjb25zdCBjID0gKHgyIC0geDApIC8gMjsKICAgICAgICAgICAgcmV0dXJuICgoYSAqIGZyYWMgKyBiKSAqIGZyYWMgKyBjKSAqIGZyYWMgKyB4MTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhdWRpbyBzYW1wbGVzCiAgICAgICAgICogRmlyc3QgaW5wdXQgd2lsbCBiZSBkb3dubWl4ZWQgdG8gbW9ubyBpZiBudW1iZXIgb2YgY2hhbm5lbHMgaXMgbm90IDIKICAgICAgICAgKiBPdXRwdXRzIHN0ZXJlbwogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IHBkID0gfn5wYXJhbWV0ZXJzLnByZURlbGF5WzBdOwogICAgICAgICAgICBjb25zdCBidyA9IHBhcmFtZXRlcnMuYmFuZHdpZHRoWzBdOwogICAgICAgICAgICBjb25zdCBmaSA9IHBhcmFtZXRlcnMuaW5wdXREaWZmdXNpb24xWzBdOwogICAgICAgICAgICBjb25zdCBzaSA9IHBhcmFtZXRlcnMuaW5wdXREaWZmdXNpb24yWzBdOwogICAgICAgICAgICBjb25zdCBkYyA9IHBhcmFtZXRlcnMuZGVjYXlbMF07CiAgICAgICAgICAgIGNvbnN0IGZ0ID0gcGFyYW1ldGVycy5kZWNheURpZmZ1c2lvbjFbMF07CiAgICAgICAgICAgIGNvbnN0IHN0ID0gcGFyYW1ldGVycy5kZWNheURpZmZ1c2lvbjJbMF07CiAgICAgICAgICAgIGNvbnN0IGRwID0gMSAtIHBhcmFtZXRlcnMuZGFtcGluZ1swXTsKICAgICAgICAgICAgY29uc3QgZXggPSBwYXJhbWV0ZXJzLmV4Y3Vyc2lvblJhdGVbMF0gLyBzYW1wbGVSYXRlOwogICAgICAgICAgICBjb25zdCBlZCA9IChwYXJhbWV0ZXJzLmV4Y3Vyc2lvbkRlcHRoWzBdICogc2FtcGxlUmF0ZSkgLyAxMDAwOwogICAgICAgICAgICBjb25zdCB3ZSA9IHBhcmFtZXRlcnMud2V0WzBdICogMC42OyAvLyBsbyAmIHJvIGJvdGggbXVsdC4gYnkgMC42IGFueXdheXMKICAgICAgICAgICAgY29uc3QgZHIgPSBwYXJhbWV0ZXJzLmRyeVswXTsKICAgICAgICAgICAgLy8gV3JpdGUgdG8gcHJlZGVsYXkgYW5kIGRyeSBvdXRwdXQKICAgICAgICAgICAgaWYgKGlucHV0c1swXS5sZW5ndGggPT09IDIpIHsKICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAxMjc7IGkgPj0gMDsgaS0tKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXlbdGhpcy5fcERXcml0ZSArIGldID0gKGlucHV0c1swXVswXVtpXSArIGlucHV0c1swXVsxXVtpXSkgKiAwLjU7CiAgICAgICAgICAgICAgICAgICAgb3V0cHV0c1swXVswXVtpXSA9IGlucHV0c1swXVswXVtpXSAqIGRyOwogICAgICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMV1baV0gPSBpbnB1dHNbMF1bMV1baV0gKiBkcjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIGlmIChpbnB1dHNbMF0ubGVuZ3RoID4gMCkgewogICAgICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXkuc2V0KGlucHV0c1swXVswXSwgdGhpcy5fcERXcml0ZSk7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMTI3OyBpID49IDA7IGktLSkgewogICAgICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMF1baV0gPSBvdXRwdXRzWzBdWzFdW2ldID0gaW5wdXRzWzBdWzBdW2ldICogZHI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICB0aGlzLl9wcmVEZWxheS5zZXQobmV3IEZsb2F0MzJBcnJheSgxMjgpLCB0aGlzLl9wRFdyaXRlKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBsZXQgaSA9IDA7CiAgICAgICAgICAgIHdoaWxlIChpIDwgMTI4KSB7CiAgICAgICAgICAgICAgICBsZXQgbG8gPSAwLjA7CiAgICAgICAgICAgICAgICBsZXQgcm8gPSAwLjA7CiAgICAgICAgICAgICAgICAvLyBJbnB1dCBsb3ctcGFzcyBmaWx0ZXIgKGJhbmR3aWR0aCkKICAgICAgICAgICAgICAgIHRoaXMuX2xwMSArPSBidyAqICh0aGlzLl9wcmVEZWxheVsodGhpcy5fcERMZW5ndGggKyB0aGlzLl9wRFdyaXRlIC0gcGQgKyBpKSAlIHRoaXMuX3BETGVuZ3RoXSAtIHRoaXMuX2xwMSk7CiAgICAgICAgICAgICAgICAvLyBQcmUtdGFuayBkaWZmdXNpb24gKDQgYWxsLXBhc3MgZmlsdGVycykKICAgICAgICAgICAgICAgIGxldCBwcmUgPSB0aGlzLndyaXRlRGVsYXkoMCwgdGhpcy5fbHAxIC0gZmkgKiB0aGlzLnJlYWREZWxheSgwKSk7CiAgICAgICAgICAgICAgICBwcmUgPSB0aGlzLndyaXRlRGVsYXkoMSwgZmkgKiAocHJlIC0gdGhpcy5yZWFkRGVsYXkoMSkpICsgdGhpcy5yZWFkRGVsYXkoMCkpOwogICAgICAgICAgICAgICAgcHJlID0gdGhpcy53cml0ZURlbGF5KDIsIGZpICogcHJlICsgdGhpcy5yZWFkRGVsYXkoMSkgLSBzaSAqIHRoaXMucmVhZERlbGF5KDIpKTsKICAgICAgICAgICAgICAgIHByZSA9IHRoaXMud3JpdGVEZWxheSgzLCBzaSAqIChwcmUgLSB0aGlzLnJlYWREZWxheSgzKSkgKyB0aGlzLnJlYWREZWxheSgyKSk7CiAgICAgICAgICAgICAgICBjb25zdCBzcGxpdCA9IHNpICogcHJlICsgdGhpcy5yZWFkRGVsYXkoMyk7CiAgICAgICAgICAgICAgICAvLyBNb2R1bGF0ZWQgZXhjdXJzaW9ucyBmb3IgY2hvcnVzIGVmZmVjdAogICAgICAgICAgICAgICAgY29uc3QgZXhjID0gZWQgKiAoMSArIE1hdGguY29zKHRoaXMuX2V4Y1BoYXNlICogNi4yOCkpOwogICAgICAgICAgICAgICAgY29uc3QgZXhjMiA9IGVkICogKDEgKyBNYXRoLnNpbih0aGlzLl9leGNQaGFzZSAqIDYuMjg0NykpOwogICAgICAgICAgICAgICAgLy8gTGVmdCBsb29wICh0YW5rIGRpZmZ1c2UgMSAtPiBsb25nIGRlbGF5IDEgLT4gZGFtcCAxIC0+IHRhbmsgZGlmZnVzZSAyIC0+IGxvbmcgZGVsYXkgMikKICAgICAgICAgICAgICAgIGxldCB0ZW1wID0gdGhpcy53cml0ZURlbGF5KDQsIHNwbGl0ICsgZGMgKiB0aGlzLnJlYWREZWxheSgxMSkgKyBmdCAqIHRoaXMucmVhZERlbGF5Q0F0KDQsIGV4YykpOwogICAgICAgICAgICAgICAgdGhpcy53cml0ZURlbGF5KDUsIHRoaXMucmVhZERlbGF5Q0F0KDQsIGV4YykgLSBmdCAqIHRlbXApOwogICAgICAgICAgICAgICAgdGhpcy5fbHAyICs9IGRwICogKHRoaXMucmVhZERlbGF5KDUpIC0gdGhpcy5fbHAyKTsKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoNiwgZGMgKiB0aGlzLl9scDIgLSBzdCAqIHRoaXMucmVhZERlbGF5KDYpKTsKICAgICAgICAgICAgICAgIHRoaXMud3JpdGVEZWxheSg3LCB0aGlzLnJlYWREZWxheSg2KSArIHN0ICogdGVtcCk7CiAgICAgICAgICAgICAgICAvLyBSaWdodCBsb29wICh0YW5rIGRpZmZ1c2UgMyAtPiBsb25nIGRlbGF5IDMgLT4gZGFtcCAyIC0+IHRhbmsgZGlmZnVzZSA0IC0+IGxvbmcgZGVsYXkgNCkKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoOCwgc3BsaXQgKyBkYyAqIHRoaXMucmVhZERlbGF5KDcpICsgZnQgKiB0aGlzLnJlYWREZWxheUNBdCg4LCBleGMyKSk7CiAgICAgICAgICAgICAgICB0aGlzLndyaXRlRGVsYXkoOSwgdGhpcy5yZWFkRGVsYXlDQXQoOCwgZXhjMikgLSBmdCAqIHRlbXApOwogICAgICAgICAgICAgICAgdGhpcy5fbHAzICs9IGRwICogKHRoaXMucmVhZERlbGF5KDkpIC0gdGhpcy5fbHAzKTsKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoMTAsIGRjICogdGhpcy5fbHAzIC0gc3QgKiB0aGlzLnJlYWREZWxheSgxMCkpOwogICAgICAgICAgICAgICAgdGhpcy53cml0ZURlbGF5KDExLCB0aGlzLnJlYWREZWxheSgxMCkgKyBzdCAqIHRlbXApOwogICAgICAgICAgICAgICAgLy8gTGVmdCBvdXRwdXQ6IHN1bSBvZiB0YXBzIGZyb20gYm90aCBsb29wcwogICAgICAgICAgICAgICAgbG8gPQogICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoOSwgdGhpcy5fdGFwc1swXSkgKwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDksIHRoaXMuX3RhcHNbMV0pIC0KICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCgxMCwgdGhpcy5fdGFwc1syXSkgKwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDExLCB0aGlzLl90YXBzWzNdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoNSwgdGhpcy5fdGFwc1s0XSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDYsIHRoaXMuX3RhcHNbNV0pIC0KICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg3LCB0aGlzLl90YXBzWzZdKTsKICAgICAgICAgICAgICAgIC8vIFJpZ2h0IG91dHB1dDogc3VtIG9mIHRhcHMgZnJvbSBib3RoIGxvb3BzCiAgICAgICAgICAgICAgICBybyA9CiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg1LCB0aGlzLl90YXBzWzddKSArCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoNSwgdGhpcy5fdGFwc1s4XSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDYsIHRoaXMuX3RhcHNbOV0pICsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg3LCB0aGlzLl90YXBzWzEwXSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDksIHRoaXMuX3RhcHNbMTFdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoMTAsIHRoaXMuX3RhcHNbMTJdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoMTEsIHRoaXMuX3RhcHNbMTNdKTsKICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMF1baV0gKz0gbG8gKiB3ZTsKICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMV1baV0gKz0gcm8gKiB3ZTsKICAgICAgICAgICAgICAgIHRoaXMuX2V4Y1BoYXNlICs9IGV4OwogICAgICAgICAgICAgICAgaSsrOwogICAgICAgICAgICAgICAgLy8gQWR2YW5jZSBhbGwgZGVsYXkgbGluZSBpbmRpY2VzCiAgICAgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuX0RlbGF5cy5sZW5ndGg7IGorKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGQgPSB0aGlzLl9EZWxheXNbal07CiAgICAgICAgICAgICAgICAgICAgZFsxXSA9IChkWzFdICsgMSkgJiBkWzNdOwogICAgICAgICAgICAgICAgICAgIGRbMl0gPSAoZFsyXSArIDEpICYgZFszXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBVcGRhdGUgcHJlRGVsYXkgaW5kZXgKICAgICAgICAgICAgdGhpcy5fcERXcml0ZSA9ICh0aGlzLl9wRFdyaXRlICsgMTI4KSAlIHRoaXMuX3BETGVuZ3RoOwogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3NvcigiZGF0dG9ycm8tcmV2ZXJiIiwgRGF0dG9ycm9SZXZlcmJQcm9jZXNzb3IpOwogICAgY29uc29sZS5sb2coIkRhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yIHJlZ2lzdGVyZWQiKTsKCiAgICBleHBvcnRzLkRhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yID0gRGF0dG9ycm9SZXZlcmJQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWRhdHRvcnJvLXJldmVyYi1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIGR5bmFtaWNzID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgICAqIER5bmFtaWNzIHByb2Nlc3NvciBjb3JlIOKAlCBjb250ZXh0LWZyZWUgRFNQIG1hdGggZm9yIGEgZmVlZC1mb3J3YXJkIGR5bmFtaWMKICAgICAqIHJhbmdlIGNvbXByZXNzb3IgLyBsaW1pdGVyIC8gZXhwYW5kZXIgLyBnYXRlLgogICAgICoKICAgICAqIEltcGxlbWVudHMgdGhlIGNhbm9uaWNhbCBkZXNpZ24gZnJvbToKICAgICAqICAgRC4gR2lhbm5vdWxpcywgTS4gTWFzc2JlcmcsIEouIEQuIFJlaXNzLAogICAgICogICAiRGlnaXRhbCBEeW5hbWljIFJhbmdlIENvbXByZXNzb3IgRGVzaWduIOKAlCBBIFR1dG9yaWFsIGFuZCBBbmFseXNpcyIsCiAgICAgKiAgIEouIEF1ZGlvIEVuZy4gU29jLiwgVm9sLiA2MCwgTm8uIDYsIHBwLiAzOTktNDA4ICgyMDEyKS4KICAgICAqCiAgICAgKiBUb3BvbG9neSAodGhlIHBhcGVyJ3MgcmVjb21tZW5kZWQgYmVzdCBwcmFjdGljZSwgcC4zOTkgYWJzdHJhY3QgLyBwcC40MDQtNDA1KToKICAgICAqICAgLSBmZWVkLWZvcndhcmQgKHNpZGUtY2hhaW4gZHJpdmVuIGZyb20gdGhlIElOUFVUKSwKICAgICAqICAgLSBnYWluIGNvbXB1dGVyID0gc3RhdGljIGRCLT5kQiBjdXJ2ZSAoZXFzIDItNCksCiAgICAgKiAgIC0gbGV2ZWwgZGV0ZWN0b3IgcGxhY2VkIGluIHRoZSBMT0cgZG9tYWluIEFGVEVSIHRoZSBnYWluIGNvbXB1dGVyCiAgICAgKiAgICAgKGVxIDIzKTogc21vb3RoIHRoZSBnYWluLXJlZHVjdGlvbiBjb250cm9sIHNpZ25hbCwgbm90IHRoZSByYXcgbGV2ZWwsCiAgICAgKiAgIC0gc21vb3RoIGJyYW5jaGluZyBwZWFrIGRldGVjdG9yIGZvciB0aGUgYmFsbGlzdGljcyAoZXEgMTYpLgogICAgICoKICAgICAqIEEgU0lOR0xFIGdhaW4gY29tcHV0ZXIsIHBhcmFtZXRlcml6ZWQgYnkgdGhyZXNob2xkIFQsIHJhdGlvIFIgYW5kIGtuZWUgVywKICAgICAqIHNlcnZlcyBhbGwgZm91ciBiZWhhdmlvdXJzIChHaWFubm91bGlzIDIwMTIsIHAuNDAzOyBzZWUgbm90ZXMubWQKICAgICAqICJFeHBhbmRlciAvIEdhdGUgZnJvbSB0aGUgU0FNRSBtYWNoaW5lcnkiKToKICAgICAqICAgLSBSID4gMSAgICAgICAgICAgIC0+IGNvbXByZXNzb3IgKHNsb3BlIDEvUiA8IDEgYWJvdmUgVCksCiAgICAgKiAgIC0gUiAtPiBpbmZpbml0eSAgICAgLT4gbGltaXRlciAoc2xvcGUgLT4gMCBhYm92ZSBULCBlcXMgMTgtMTkpLAogICAgICogICAtIFIgPCAxICAgICAgICAgICAgLT4gZG93bndhcmQgZXhwYW5kZXIgKHNsb3BlID4gMSBCRUxPVyBUKSwKICAgICAqICAgLSBleHRyZW1lIFIgPCAxICAgIC0+IGdhdGUgKHZlcnkgc3RlZXAgZG93bndhcmQgZXhwYW5zaW9uIGJlbG93IFQpLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChvcGVyYXRlcyBvbiBwbGFpbiBudW1iZXJzIGFuZAogICAgICogRmxvYXQzMkFycmF5KS4gSXQgaGFzIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMgc28gaXQgY2FuIGJlCiAgICAgKiB1bml0LXRlc3RlZCBkaXJlY3RseSAodGhlIHdvcmtsZXQgc2hlbGwgaW4gZHluYW1pY3MudHMgZGVsZWdhdGVzIHRvIGl0KS4KICAgICAqLwogICAgLyoqCiAgICAgKiBEZWZhdWx0IEF1ZGlvUGFyYW0gdmFsdWVzIGZvciB0aGUgZHluYW1pY3Mgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncyBvd24KICAgICAqIHVuaXRzLiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AKICAgICAqIChkeW5hbWljcy50cykgYnVpbGRzIGl0cyBkZXNjcmlwdG9ycyBmcm9tIHRoaXMgdGFibGUsIGFuZCB0ZXN0cyB0aGF0IG5lZWQgdGhlCiAgICAgKiBzaGlwcGVkIGRlZmF1bHRzIGltcG9ydCBpdCBoZXJlIHJhdGhlciB0aGFuIHJlLXR5cGluZyBsaXRlcmFscy4gTGl2aW5nIGluIHRoZQogICAgICogd29ya2xldC1nbG9iYWwtZnJlZSBjb3JlIGtlZXBzIGl0IGltcG9ydGFibGUgZnJvbSBwbGFpbiBOb2RlIHRlc3QgcnVubmVycy4KICAgICAqCiAgICAgKiBEZWZhdWx0czogYSBnZW50bGUsIGF1ZGlibGUtYnV0LXNhZmUgY29tcHJlc3Nvci4gUmF0aW9zIGNvdmVyIGNvbXByZXNzb3IKICAgICAqIChyYXRpbyA+PSAxKSwgbGltaXRlciAocmF0aW8gLT4gbGFyZ2UpIGFuZCBkb3dud2FyZCBleHBhbmRlci9nYXRlIChyYXRpbyA8IDEpCiAgICAgKiBmcm9tIG9uZSBwYXJhbWV0ZXIgc2V0LgogICAgICovCiAgICBjb25zdCBEWU5BTUlDU19ERUZBVUxUUyA9IHsKICAgICAgICB0aHJlc2hvbGQ6IC0yNCwKICAgICAgICByYXRpbzogNCwKICAgICAgICBrbmVlOiA2LAogICAgICAgIGF0dGFjazogMC4wMDMsCiAgICAgICAgcmVsZWFzZTogMC4yNSwKICAgICAgICBtYWtldXA6IDAsCiAgICB9OwogICAgLyoqCiAgICAgKiBTbWFsbGVzdCBsaW5lYXIgbGV2ZWwgd2UgYXJlIHdpbGxpbmcgdG8gdGFrZSBhIGxvZ2FyaXRobSBvZi4gQmVsb3cgdGhpcyB0aGUKICAgICAqIGlucHV0IGlzIHRyZWF0ZWQgYXMgc2lsZW5jZSAoZ2Fpbi1yZWR1Y3Rpb24gY29udHJvbCA9IDApLCBhdm9pZGluZyAtSW5maW5pdHkKICAgICAqIGRCIGZyb20gbG9nMTAoMCkuCiAgICAgKi8KICAgIGNvbnN0IExFVkVMX0ZMT09SID0gMWUtMTI7CiAgICAvKioKICAgICAqIFN0YXRpYyBnYWluIGNvbXB1dGVyIOKAlCBtYXBzIGFuIGlucHV0IGxldmVsIHhfRyAoZEIpIHRvIGFuIG91dHB1dCBsZXZlbAogICAgICogeV9HIChkQikgZ2l2ZW4gdGhyZXNob2xkIFQsIHJhdGlvIFIgYW5kIGtuZWUgd2lkdGggVy4KICAgICAqCiAgICAgKiBIYXJkIGtuZWUgKFcgPSAwKSwgR2lhbm5vdWxpcyAyMDEyIGVxLjM6CiAgICAgKiAgIHlfRyA9IHhfRyAgICAgICAgICAgICAgICAgICAgICAgZm9yIHhfRyA8PSBUCiAgICAgKiAgIHlfRyA9IFQgKyAoeF9HIC0gVCkgLyBSICAgICAgICAgZm9yIHhfRyA+ICBUCiAgICAgKgogICAgICogU29mdCBrbmVlIChXID4gMCksIGVxLjQg4oCUIHF1YWRyYXRpYyBpbnRlcnBvbGF0aW9uIGFjcm9zcyArLy0gVy8yIGFyb3VuZCBUOgogICAgICogICB5X0cgPSB4X0cgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMih4X0ctVCkgPCAtVwogICAgICogICB5X0cgPSB4X0cgKyAoMS9SIC0gMSkoeF9HIC0gVCArIFcvMileMiAvICgyVykgICAgICAgICAgfDIoeF9HLVQpfCA8PSBXCiAgICAgKiAgIHlfRyA9IFQgKyAoeF9HIC0gVCkgLyBSICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyKHhfRy1UKSA+ICBXCiAgICAgKgogICAgICogQSBsaW1pdGVyIGlzIFIgLT4gaW5maW5pdHk6IDEvUiAtPiAwLCBzbyBhYm92ZSBUIHRoZSBjdXJ2ZSBmbGF0dGVucyB0byB5X0cgPSBUCiAgICAgKiAoZXFzIDE4LTE5KS4gV2UgaGFuZGxlIHRoZSBpbmZpbml0ZS96ZXJvLXNsb3BlIGNhc2UgZXhwbGljaXRseSB0byBhdm9pZAogICAgICogZGl2aXNpb24gYnkgaW5maW5pdHkuCiAgICAgKgogICAgICogRG93bndhcmQgRVhQQU5TSU9OIChSIDwgMSwgR2lhbm5vdWxpcyAyMDEyIHAuNDAzICJmb3IgcmF0aW9zIDwgMSAuLi4gdGhlCiAgICAgKiB2YXJpYWJsZSBzaW1wbHkgYmVjb21lcyBzbWFsbGVyIHRoYW4gLTEiKTogdGhlIHNsb3BlLWFsdGVyZWQgcmVnaW9uIGlzIEJFTE9XCiAgICAgKiB0aGUgdGhyZXNob2xkIHJhdGhlciB0aGFuIGFib3ZlIGl0LiBXaXRoIHNsb3BlIDEvUiA+IDEgYXBwbGllZCBmb3IgeF9HIDwgVCwKICAgICAqIHlfRyA9IFQgKyAoeF9HIC0gVCkvUiBpcyBtb3JlIG5lZ2F0aXZlIHRoYW4geF9HLCBzbyBxdWlldCBzaWduYWxzIGFyZSBwdXNoZWQKICAgICAqIGZ1cnRoZXIgZG93biAoaW5jcmVhc2luZyBkeW5hbWljIHJhbmdlOyBhbiBleHRyZW1lIFI8MSByYXRpbyBpcyBhIGdhdGUpLgogICAgICogU2VlIG5vdGVzLm1kICJFeHBhbmRlciAvIEdhdGUgZnJvbSB0aGUgU0FNRSBtYWNoaW5lcnkiLiBUaGUgY29tcHJlc3NvciBicmFuY2gKICAgICAqIChSID49IDEpIGFuZCB0aGUgZXhwYW5kZXIgYnJhbmNoIChSIDwgMSkgYXJlIG1pcnJvciBpbWFnZXMgYWJvdXQgVCwgc28gb25lCiAgICAgKiBmdW5jdGlvbiBwYXJhbWV0ZXJpemVkIGJ5IFQvUi9XIHNlcnZlcyBjb21wcmVzc29yLCBsaW1pdGVyLCBleHBhbmRlciBhbmQgZ2F0ZS4KICAgICAqLwogICAgZnVuY3Rpb24gY29tcHV0ZVN0YXRpY0dhaW4oeEcsIHRocmVzaG9sZCwgcmF0aW8sIGtuZWUpIHsKICAgICAgICBjb25zdCBzbG9wZSA9IHJhdGlvID09PSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkgPyAwIDogMSAvIHJhdGlvOyAvLyAxL1I7IGxpbWl0ZXIgLT4gMAogICAgICAgIGNvbnN0IGRlbHRhID0geEcgLSB0aHJlc2hvbGQ7CiAgICAgICAgY29uc3QgaXNFeHBhbmRlciA9IHJhdGlvIDwgMTsgLy8gUiA8IDEgPT4gZG93bndhcmQgZXhwYW5zaW9uIGJlbG93IFQKICAgICAgICBpZiAoaXNFeHBhbmRlcikgewogICAgICAgICAgICAvLyBEb3dud2FyZCBleHBhbmRlcjogc2xvcGUtYWx0ZXJlZCByZWdpb24gaXMgQkVMT1cgdGhyZXNob2xkIChlcXMgMi00CiAgICAgICAgICAgIC8vIG1pcnJvcmVkKS4gU29mdCBrbmVlIGNlbnRlcmVkIG9uIFQgKGVxLjQgZm9ybSwgbWlycm9yZWQgdG8gdGhlIGxvd2VyCiAgICAgICAgICAgIC8vIHNpZGUpOiBibGVuZCB0aGUgdW5pdHkgc2xvcGUgKGFib3ZlKSBpbnRvIHNsb3BlIDEvUiAoYmVsb3cpLgogICAgICAgICAgICBpZiAoa25lZSA+IDAgJiYgTWF0aC5hYnMoMiAqIGRlbHRhKSA8PSBrbmVlKSB7CiAgICAgICAgICAgICAgICAvLyBlcS40IHF1YWRyYXRpYyBpbnRlcnBvbGF0aW9uLCBtaXJyb3JlZCBhYm91dCBUIGZvciB0aGUgZXhwYW5kZXIuCiAgICAgICAgICAgICAgICAvLyBUaGUgZXhwYW5kZXIncyBzbG9wZS1hbHRlcmVkIHJlZ2lvbiBpcyBCRUxPVyBULCBzbyB0aGUga25lZSBtdXN0IGpvaW4KICAgICAgICAgICAgICAgIC8vIHRoZSBzbG9wZSBsaW5lIGF0IHRoZSBMT1dFUiBlZGdlIChkZWx0YSA9IC1XLzIpIGFuZCB1bml0eSBhdCB0aGUgVVBQRVIKICAgICAgICAgICAgICAgIC8vIGVkZ2UgKGRlbHRhID0gK1cvMikuIEFuY2hvciB0aGUgcGFyYWJvbGEgdG8gdGhlIHVwcGVyIGVkZ2UgKHQgPSAwIHRoZXJlKQogICAgICAgICAgICAgICAgLy8gYW5kIHN1YnRyYWN0IHRoZSBjdXJ2YXR1cmUgc28geV9HIG1lZXRzIHRoZSBzbG9wZSBsaW5lIGJlbG93OiB0aGlzCiAgICAgICAgICAgICAgICAvLyB5aWVsZHMgeV9HIDw9IHhfRyBldmVyeXdoZXJlIChuZXZlciBib29zdHMpIGFuZCBDMC9DMSBjb250aW51aXR5IGF0IGJvdGgKICAgICAgICAgICAgICAgIC8vIGVkZ2VzIChzbG9wZSAxL1IgYmVsb3csIHVuaXR5IGFib3ZlKS4gVGhlIE1JTlVTIHNpZ24gaXMgdGhlIG1pcnJvciBvZgogICAgICAgICAgICAgICAgLy8gdGhlIGNvbXByZXNzb3IgYnJhbmNoJ3MgUExVUyDigJQgc2FtZSBlcS40IGZvcm0sIG9wcG9zaXRlIGFsdGVyZWQgc2lkZS4KICAgICAgICAgICAgICAgIGNvbnN0IHQgPSBkZWx0YSAtIGtuZWUgLyAyOyAvLyAwIGF0IHRoZSB1cHBlciBlZGdlIChkZWx0YSA9ICtXLzIpCiAgICAgICAgICAgICAgICByZXR1cm4geEcgLSAoKHNsb3BlIC0gMSkgKiAodCAqIHQpKSAvICgyICoga25lZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKGRlbHRhID49IDApIHsKICAgICAgICAgICAgICAgIC8vIHhfRyA+PSBUIChhYm92ZSB0aHJlc2hvbGQpIOKAlCB1bml0eSBmb3IgYSBkb3dud2FyZCBleHBhbmRlci4KICAgICAgICAgICAgICAgIHJldHVybiB4RzsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyB4X0cgPCBUIChiZWxvdyB0aHJlc2hvbGQpIOKAlCBzbG9wZSAxL1IgPiAxIHB1c2hlcyB0aGUgbGV2ZWwgZG93bi4KICAgICAgICAgICAgcmV0dXJuIHRocmVzaG9sZCArIGRlbHRhICogc2xvcGU7CiAgICAgICAgfQogICAgICAgIC8vIENvbXByZXNzb3IgLyBsaW1pdGVyIChSID49IDEpOiBzbG9wZS1hbHRlcmVkIHJlZ2lvbiBpcyBBQk9WRSB0aHJlc2hvbGQuCiAgICAgICAgLy8gU29mdCBrbmVlIChlcS40IG1pZGRsZSBicmFuY2gpOiAyfHhfRyAtIFR8IDw9IFcuCiAgICAgICAgaWYgKGtuZWUgPiAwICYmIE1hdGguYWJzKDIgKiBkZWx0YSkgPD0ga25lZSkgewogICAgICAgICAgICAvLyBlcS40OiB5X0cgPSB4X0cgKyAoMS9SIC0gMSkoeF9HIC0gVCArIFcvMileMiAvICgyVykKICAgICAgICAgICAgY29uc3QgdCA9IGRlbHRhICsga25lZSAvIDI7CiAgICAgICAgICAgIHJldHVybiB4RyArICgoc2xvcGUgLSAxKSAqICh0ICogdCkpIC8gKDIgKiBrbmVlKTsKICAgICAgICB9CiAgICAgICAgLy8gSGFyZCBrbmVlIC8gb3V0c2lkZSBrbmVlIHJlZ2lvbiAoZXEuMyAvIGVxLjQgb3V0ZXIgYnJhbmNoZXMpLgogICAgICAgIGlmIChkZWx0YSA8PSAwKSB7CiAgICAgICAgICAgIC8vIHhfRyA8PSBUIChiZWxvdyB0aHJlc2hvbGQpIOKAlCB1bml0eS4gKGVxLjMgZmlyc3QgY2FzZS4pCiAgICAgICAgICAgIHJldHVybiB4RzsKICAgICAgICB9CiAgICAgICAgLy8geF9HID4gVCAoYWJvdmUgdGhyZXNob2xkKSDigJQgc2xvcGUgMS9SLiAoZXEuMyBzZWNvbmQgY2FzZTsgZXEuNCB1cHBlciBicmFuY2guKQogICAgICAgIHJldHVybiB0aHJlc2hvbGQgKyBkZWx0YSAqIHNsb3BlOwogICAgfQogICAgLyoqCiAgICAgKiBUaW1lLWNvbnN0YW50IC0+IG9uZS1wb2xlIGNvZWZmaWNpZW50IG1hcHBpbmcsIEdpYW5ub3VsaXMgMjAxMiBlcS43OgogICAgICogICBhbHBoYSA9IGV4cCgtMSAvICh0YXUgKiBmX3MpKQogICAgICogdGF1IGlzIHRoZSB0aW1lIHRvIHJlYWNoIDEgLSAxL2Ugb2YgdGhlIGZpbmFsIHZhbHVlLiBGb3IgdGF1ID4gMCwgZl9zID4gMAogICAgICogdGhlIHJlc3VsdCBsaWVzIGluICgwLCAxKTsgbGFyZ2VyIHRhdSAtPiBhbHBoYSBjbG9zZXIgdG8gMSAoc2xvd2VyKS4KICAgICAqLwogICAgZnVuY3Rpb24gdGltZUNvbnN0YW50VG9Db2VmZmljaWVudCh0YXUsIHNhbXBsZVJhdGUpIHsKICAgICAgICBpZiAodGF1IDw9IDApCiAgICAgICAgICAgIHJldHVybiAwOyAvLyBkZWdlbmVyYXRlOiBpbnN0YW50YW5lb3VzIHJlc3BvbnNlCiAgICAgICAgcmV0dXJuIE1hdGguZXhwKC0xIC8gKHRhdSAqIHNhbXBsZVJhdGUpKTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgZmVlZC1mb3J3YXJkIGR5bmFtaWNzIHByb2Nlc3Nvci4gT25lIGluc3RhbmNlIG93bnMgdGhlIGJhbGxpc3RpY3MKICAgICAqIHN0YXRlICh0aGUgc21vb3RoZWQgZ2Fpbi1yZWR1Y3Rpb24gZW52ZWxvcGUgeV9MIG9mIHRoZSBlcS4xNiBkZXRlY3RvcikgYW5kIGlzCiAgICAgKiByZXVzZWQgYmxvY2stdG8tYmxvY2sgc28gdGhlIElJUiBtZW1vcnkgaXMgcHJlc2VydmVkIGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMg4oCUCiAgICAgKiBjb25zdHJ1Y3RpbmcgYSBmcmVzaCBpbnN0YW5jZSBwZXIgYmxvY2sgd291bGQgcmVzZXQgdGhlIGVudmVsb3BlIGFuZCBwcm9kdWNlCiAgICAgKiBhdWRpYmxlIHRyYW5zaWVudHMgKHNhbWUgaGF6YXJkIGRvY3VtZW50ZWQgZm9yIHRoZSBiLWZvcm1hdCB1cG1peGVyKS4KICAgICAqLwogICAgY2xhc3MgRHluYW1pY3NQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgLyoqCiAgICAgICAgICogU21vb3RoZWQgZ2Fpbi1yZWR1Y3Rpb24gY29udHJvbCBlbnZlbG9wZSB5X0xbbi0xXSAodGhlIHN0YXRlIG9mIHRoZSBlcS4xNgogICAgICAgICAqIHNtb290aCBicmFuY2hpbmcgcGVhayBkZXRlY3RvcikuIE9wZXJhdGVzIG9uIHRoZSBnYWluLXJlZHVjdGlvbiBzaWduYWwKICAgICAgICAgKiB4X0wgPSB4X0cgLSB5X0cgKGVxLjIzKSwgd2hpY2ggaXMgMCAobm8gcmVkdWN0aW9uKSB3aGVuIG5vdCBjb21wcmVzc2luZywgc28KICAgICAgICAgKiB0aGUgZW52ZWxvcGUgcmVzdHMgYXQgMC4KICAgICAgICAgKi8KICAgICAgICBlbnZlbG9wZURiID0gMDsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgfQogICAgICAgIC8qKiBSZXNldCB0aGUgYmFsbGlzdGljcyBzdGF0ZSAoZS5nLiBvbiBhIGRpc2NvbnRpbnVpdHkpLiAqLwogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmVudmVsb3BlRGIgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIG9uZSBzYW1wbGUsIHJldHVybmluZyB0aGUgZ2Fpbi1hcHBsaWVkIG91dHB1dCBzYW1wbGUuCiAgICAgICAgICoKICAgICAgICAgKiBTaWduYWwgZmxvdyAoR2lhbm5vdWxpcyAyMDEyIHJlY29tbWVuZGVkIGZlZWQtZm9yd2FyZCwgbG9nLWRvbWFpbiBkZXRlY3RvcgogICAgICAgICAqIGFmdGVyIHRoZSBnYWluIGNvbXB1dGVyKToKICAgICAgICAgKiAgIDEuIHhfRyA9IDIwIGxvZzEwIHx4fCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZXEuMjMgbGluZSAxKQogICAgICAgICAqICAgMi4geV9HID0gZ2FpbiBjb21wdXRlcih4X0c7IFQsIFIsIFcpICAgICAgICAgICAgICAgICAgIChlcXMgMy00KQogICAgICAgICAqICAgMy4geF9MID0geF9HIC0geV9HICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChlcS4yMyBsaW5lIDIpCiAgICAgICAgICogICAgICDigJQgdGhlIGluc3RhbnRhbmVvdXMgZ2Fpbi1yZWR1Y3Rpb24gKD49IDAgZm9yIGNvbXByZXNzaW9uL2xpbWl0aW5nLAogICAgICAgICAqICAgICAgICBhbHNvID49IDAgZm9yIGRvd253YXJkIGV4cGFuc2lvbikuCiAgICAgICAgICogICA0LiBzbW9vdGggYnJhbmNoaW5nIHBlYWsgZGV0ZWN0b3Igb24geF9MICAgICAgICAgICAgICAgKGVxLjE2KQogICAgICAgICAqICAgNS4gY19kQiA9IC15X0wgKyBNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChlcS4yMyBsaW5lIDMsIGVxLjEpCiAgICAgICAgICogICA2LiB5W25dID0gMTBeKGNfZEIgLyAyMCkgKiB4W25dICAgICAgICAgICAgICAgICAgICAgICAgKGVxLjEgbGluZWFyIGZvcm0pCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0gYWxwaGFBdHRhY2sgIHByZWNvbXB1dGVkIGF0dGFjayBjb2VmZmljaWVudCBhbHBoYV9BIChlcS43KQogICAgICAgICAqIEBwYXJhbSBhbHBoYVJlbGVhc2UgcHJlY29tcHV0ZWQgcmVsZWFzZSBjb2VmZmljaWVudCBhbHBoYV9SIChlcS43KQogICAgICAgICAqLwogICAgICAgIHByb2Nlc3NTYW1wbGUoeCwgdGhyZXNob2xkLCByYXRpbywga25lZSwgYWxwaGFBdHRhY2ssIGFscGhhUmVsZWFzZSwgbWFrZXVwKSB7CiAgICAgICAgICAgIGNvbnN0IGxldmVsID0gTWF0aC5hYnMoeCk7CiAgICAgICAgICAgIC8vIGVxLjIzIGxpbmUgMTogeF9HID0gMjAgbG9nMTAgfHh8LiBGbG9vciBzaWxlbmNlIHRvIGF2b2lkIC1JbmYgZEIuCiAgICAgICAgICAgIGNvbnN0IHhHID0gbGV2ZWwgPCBMRVZFTF9GTE9PUiA/IDIwICogTWF0aC5sb2cxMChMRVZFTF9GTE9PUikgOiAyMCAqIE1hdGgubG9nMTAobGV2ZWwpOwogICAgICAgICAgICAvLyBlcXMgMy00OiBzdGF0aWMgY3VydmUuCiAgICAgICAgICAgIGNvbnN0IHlHID0gY29tcHV0ZVN0YXRpY0dhaW4oeEcsIHRocmVzaG9sZCwgcmF0aW8sIGtuZWUpOwogICAgICAgICAgICAvLyBlcS4yMyBsaW5lIDI6IGdhaW4tcmVkdWN0aW9uIGNvbnRyb2wgc2lnbmFsIChsb2cgZG9tYWluLCBhZnRlciB0aGUgZ2FpbgogICAgICAgICAgICAvLyBjb21wdXRlcikuIHhfTCA+PSAwIG1lYW5zICJ0aGlzIG11Y2ggcmVkdWN0aW9uIGlzIHdhbnRlZCByaWdodCBub3ciLgogICAgICAgICAgICBjb25zdCB4TCA9IHhHIC0geUc7CiAgICAgICAgICAgIC8vIGVxLjE2IHNtb290aCBicmFuY2hpbmcgcGVhayBkZXRlY3RvciDigJQgc21vb3RoIHRoZSBnYWluLXJlZHVjdGlvbiBzaWduYWwuCiAgICAgICAgICAgIC8vIEF0dGFjayBicmFuY2ggd2hlbiBtb3JlIHJlZHVjdGlvbiBpcyB3YW50ZWQgdGhhbiBjdXJyZW50bHkgYXBwbGllZDsKICAgICAgICAgICAgLy8gcmVsZWFzZSBicmFuY2ggb3RoZXJ3aXNlLiBUaGUgc21vb3RoICh2cyBwbGFpbikgYnJhbmNoaW5nIGZvcm0gbG93LXBhc3NlcwogICAgICAgICAgICAvLyBCT1RIIGJyYW5jaGVzIHNvIHRoZXJlIGlzIG5vIGRpc2NvbnRpbnVpdHkgYXQgdGhlIGF0dGFjazwtPnJlbGVhc2Ugc3dpdGNoLgogICAgICAgICAgICBpZiAoeEwgPiB0aGlzLmVudmVsb3BlRGIpIHsKICAgICAgICAgICAgICAgIC8vIGF0dGFjazogeF9MW25dID4geV9MW24tMV0KICAgICAgICAgICAgICAgIHRoaXMuZW52ZWxvcGVEYiA9IGFscGhhQXR0YWNrICogdGhpcy5lbnZlbG9wZURiICsgKDEgLSBhbHBoYUF0dGFjaykgKiB4TDsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIC8vIHJlbGVhc2U6IHhfTFtuXSA8PSB5X0xbbi0xXQogICAgICAgICAgICAgICAgdGhpcy5lbnZlbG9wZURiID0gYWxwaGFSZWxlYXNlICogdGhpcy5lbnZlbG9wZURiICsgKDEgLSBhbHBoYVJlbGVhc2UpICogeEw7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gZXEuMjMgbGluZSAzICsgZXEuMSBtYWtlLXVwOiBjb250cm9sIHZvbHRhZ2UgY19kQiA9IC15X0wgKyBNLgogICAgICAgICAgICBjb25zdCBjb250cm9sRGIgPSAtdGhpcy5lbnZlbG9wZURiICsgbWFrZXVwOwogICAgICAgICAgICAvLyBlcS4xIGxpbmVhciBmb3JtOiB5W25dID0gY1tuXSAqIHhbbl0sIHdpdGggY1tuXSA9IDEwXihjX2RCLzIwKS4KICAgICAgICAgICAgY29uc3QgY29udHJvbExpbmVhciA9IDEwICoqIChjb250cm9sRGIgLyAyMCk7CiAgICAgICAgICAgIHJldHVybiBjb250cm9sTGluZWFyICogeDsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhIGJsb2NrLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtYXkgYWxpYXMgKGluLXBsYWNlIGlzIGZpbmUpLiBUaGUKICAgICAgICAgKiBhdHRhY2svcmVsZWFzZSBjb2VmZmljaWVudHMgYXJlIGNvbXB1dGVkIG9uY2UgcGVyIGJsb2NrIChrLXJhdGUgcGFyYW1zKTsKICAgICAgICAgKiB0aGUgcGVyLXNhbXBsZSBzaWRlLWNoYWluIHJ1bnMgdGhlIGVxLjE2IGRldGVjdG9yIGFuZCBlcS4xIG11bHRpcGx5LgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIC8vIGVxLjc6IHRhdSAtPiBhbHBoYSwgb25jZSBwZXIgYmxvY2sgKGstcmF0ZSkuCiAgICAgICAgICAgIGNvbnN0IGFscGhhQXR0YWNrID0gdGltZUNvbnN0YW50VG9Db2VmZmljaWVudChwYXJhbXMuYXR0YWNrLCB0aGlzLnNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBhbHBoYVJlbGVhc2UgPSB0aW1lQ29uc3RhbnRUb0NvZWZmaWNpZW50KHBhcmFtcy5yZWxlYXNlLCB0aGlzLnNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBuID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IHRoaXMucHJvY2Vzc1NhbXBsZShpbnB1dFtpXSwgcGFyYW1zLnRocmVzaG9sZCwgcGFyYW1zLnJhdGlvLCBwYXJhbXMua25lZSwgYWxwaGFBdHRhY2ssIGFscGhhUmVsZWFzZSwgcGFyYW1zLm1ha2V1cCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoKICAgICAqIER5bmFtaWNzIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMCiAgICAgKiBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIER5bmFtaWNzUHJvY2Vzc29yIGluIGR5bmFtaWNzLWNvcmUudHMuIE1pcnJvcnMKICAgICAqIHRoZSBkYXR0b3Jyby1yZXZlcmIudHMgLyBzdGVyZW8tdG8tYmZvcm1hdC50cyBjb3JlL3NoZWxsIHNwbGl0OiB0aGlzIGZpbGUKICAgICAqIG93bnMgb25seSB0aGUgd29ya2xldCBwbHVtYmluZyAocGFyYW1ldGVyRGVzY3JpcHRvcnMsIHByb2Nlc3MoKSwgdGhlCiAgICAgKiByZWdpc3RlclByb2Nlc3NvciBjYWxsKTsgdGhlIGFsZ29yaXRobSBsaXZlcyBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KICAgICAqCiAgICAgKiBBbGdvcml0aG06IGZlZWQtZm9yd2FyZCBkeW5hbWljIHJhbmdlIGNvbXByZXNzb3IvbGltaXRlci9leHBhbmRlci9nYXRlIHBlcgogICAgICogR2lhbm5vdWxpcywgTWFzc2JlcmcgJiBSZWlzcyAyMDEyIChzZWUgZHluYW1pY3MtY29yZS50cyBoZWFkZXIgZm9yIHRoZSBmdWxsCiAgICAgKiBjaXRhdGlvbiBhbmQgcGVyLWVxdWF0aW9uIGNvbW1lbnRzKS4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpkeW5hbWljc10iOwogICAgLyoqCiAgICAgKiBSYXRpbyBzZW50aW5lbDogdGhlIHdvcmtsZXQncyBBdWRpb1BhcmFtIGNhbm5vdCBjYXJyeSArSW5maW5pdHksIHNvIGEKICAgICAqIGxpbWl0ZXIgaXMgZXhwcmVzc2VkIGFzIGEgdmVyeSBsYXJnZSBmaW5pdGUgcmF0aW8gaGVyZSBhbmQgdHJlYXRlZCBhcwogICAgICogIi0+aW5maW5pdHkiIGJ5IHRoZSBnYWluIGNvbXB1dGVyIChzbG9wZSBlZmZlY3RpdmVseSAwIGFib3ZlIHRocmVzaG9sZCkuCiAgICAgKiBjcmVhdGVMaW1pdGVyKCkgaW4gY2Fjb3Bob255LnRzIHNldHMgdGhlIHJhdGlvIHRvIHRoaXMgdmFsdWUuCiAgICAgKi8KICAgIGNvbnN0IExJTUlURVJfUkFUSU8gPSAxMDAwOwogICAgY2xhc3MgRHluYW1pY3NXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBiYWxsaXN0aWNzCiAgICAgICAgLy8gZW52ZWxvcGUgKGVxLjE2IGRldGVjdG9yIHN0YXRlKSBhY3Jvc3MgcHJvY2VzcygpIGJsb2Nrcy4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBEWU5BTUlDU19ERUZBVUxUUyAoZHluYW1pY3MtY29yZS50cykg4oCUIHRoZSBzaW5nbGUKICAgICAgICAgICAgLy8gc291cmNlIG9mIHRydXRoIHNoYXJlZCB3aXRoIHRoZSBnYXRlIHJlZ3Jlc3Npb24gdGVzdHMuIFJhbmdlcyBsaXZlIGhlcmUKICAgICAgICAgICAgLy8gYW5kIGNvdmVyIGNvbXByZXNzb3IgKHJhdGlvID49IDEpLCBsaW1pdGVyIChyYXRpbyAtPiBsYXJnZSksIGFuZCBkb3dud2FyZAogICAgICAgICAgICAvLyBleHBhbmRlci9nYXRlIChyYXRpbyA8IDEpIGZyb20gb25lIHBhcmFtZXRlciBzZXQuCiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbInRocmVzaG9sZCIsIERZTkFNSUNTX0RFRkFVTFRTLnRocmVzaG9sZCwgLTEwMCwgMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJyYXRpbyIsIERZTkFNSUNTX0RFRkFVTFRTLnJhdGlvLCAwLjA1LCBMSU1JVEVSX1JBVElPLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImtuZWUiLCBEWU5BTUlDU19ERUZBVUxUUy5rbmVlLCAwLCA0MCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJhdHRhY2siLCBEWU5BTUlDU19ERUZBVUxUUy5hdHRhY2ssIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsicmVsZWFzZSIsIERZTkFNSUNTX0RFRkFVTFRTLnJlbGVhc2UsIDAsIDUsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWFrZXVwIiwgRFlOQU1JQ1NfREVGQVVMVFMubWFrZXVwLCAtMjQsIDI0LCAiay1yYXRlIl0sCiAgICAgICAgICAgIF0ubWFwKChbbmFtZSwgZGVmYXVsdFZhbHVlLCBtaW5WYWx1ZSwgbWF4VmFsdWUsIGF1dG9tYXRpb25SYXRlXSkgPT4gKHsKICAgICAgICAgICAgICAgIG5hbWU6IG5hbWUsCiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU6IGRlZmF1bHRWYWx1ZSwKICAgICAgICAgICAgICAgIG1pblZhbHVlOiBtaW5WYWx1ZSwKICAgICAgICAgICAgICAgIG1heFZhbHVlOiBtYXhWYWx1ZSwKICAgICAgICAgICAgICAgIGF1dG9tYXRpb25SYXRlOiBhdXRvbWF0aW9uUmF0ZSwKICAgICAgICAgICAgfSkpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgaWYgKCFpbnB1dCB8fCBpbnB1dC5sZW5ndGggPT09IDAgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoID09PSAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBrLXJhdGUgcGFyYW1zOiB0YWtlIHRoZSBmaXJzdCAoY29uc3RhbnQtb3Zlci1ibG9jaykgdmFsdWUuIEEgcmF0aW8gYXQgb3IKICAgICAgICAgICAgLy8gYWJvdmUgdGhlIGxpbWl0ZXIgc2VudGluZWwgaXMgbWFwcGVkIHRvICtJbmZpbml0eSBzbyB0aGUgY29yZSdzIGdhaW4KICAgICAgICAgICAgLy8gY29tcHV0ZXIgdXNlcyB0aGUgZXhhY3QgbGltaXRlciBzbG9wZSAoMCkgcmF0aGVyIHRoYW4gMS9MSU1JVEVSX1JBVElPLgogICAgICAgICAgICBjb25zdCByYXRpb1BhcmFtID0gcGFyYW1ldGVycy5yYXRpb1swXTsKICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgdGhyZXNob2xkOiBwYXJhbWV0ZXJzLnRocmVzaG9sZFswXSwKICAgICAgICAgICAgICAgIHJhdGlvOiByYXRpb1BhcmFtID49IExJTUlURVJfUkFUSU8gPyBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkgOiByYXRpb1BhcmFtLAogICAgICAgICAgICAgICAga25lZTogcGFyYW1ldGVycy5rbmVlWzBdLAogICAgICAgICAgICAgICAgYXR0YWNrOiBwYXJhbWV0ZXJzLmF0dGFja1swXSwKICAgICAgICAgICAgICAgIHJlbGVhc2U6IHBhcmFtZXRlcnMucmVsZWFzZVswXSwKICAgICAgICAgICAgICAgIG1ha2V1cDogcGFyYW1ldGVycy5tYWtldXBbMF0sCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHsKICAgICAgICAgICAgICAgIGlmICghdGhpcy5jb3Jlc1tjaF0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBEeW5hbWljc1Byb2Nlc3NvcihzYW1wbGVSYXRlKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJkeW5hbWljcyIsIER5bmFtaWNzV29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5EeW5hbWljc1dvcmtsZXRQcm9jZXNzb3IgPSBEeW5hbWljc1dvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWR5bmFtaWNzLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGZkblJldmVyYiA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qCiAgICAgKiBGRE4gcmV2ZXJiIHByb2Nlc3NvciBjb3JlIOKAlCBjb250ZXh0LWZyZWUgRFNQIG1hdGggZm9yIGEgRmVlZGJhY2sgRGVsYXkKICAgICAqIE5ldHdvcmsgKEZETikgcmV2ZXJiZXJhdG9yIHdpdGggYSBEZWxheSBGZWVkYmFjayBNYXRyaXggKHNjYXR0ZXJpbmcpIGFuZAogICAgICogcGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2lvbi4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBjYW5vbmljYWwgRkROIHJlY3Vyc2lvbiBhbmQgYSBsb3NzbGVzcyAocGFyYXVuaXRhcnkpIGZlZWRiYWNrCiAgICAgKiBtYXRyaXggZnJvbToKICAgICAqICAgUy4gSi4gU2NobGVjaHQsIEUuIEEuIFAuIEhhYmV0cywgIlNjYXR0ZXJpbmcgaW4gRmVlZGJhY2sgRGVsYXkgTmV0d29ya3MiLAogICAgICogICBJRUVFL0FDTSBUcmFucy4gQXVkaW8sIFNwZWVjaCwgTGFuZy4gUHJvY2Vzcy4sIFZvbC4gMjgsIDIwMjAgKGFyWGl2CiAgICAgKiAgIDE5MTIuMDg4ODgpLiBbaGVyZWFmdGVyICJTY2hsZWNodCAyMDIwIl0KICAgICAqCiAgICAgKiBhbmQgdGhlIG11bHRpcGxpY2F0aW9uLWZyZWUgcGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2lvbiBGSVIgZnJvbToKICAgICAqICAgSi4gRmFnZXJzdHLDtm0sIEIuIEFsYXJ5LCBTLiBKLiBTY2hsZWNodCwgVi4gVsOkbGltw6RraSwKICAgICAqICAgIlZlbHZldC1Ob2lzZSBGZWVkYmFjayBEZWxheSBOZXR3b3JrIiwgUHJvYy4gREFGeCAyMDIwLCBwcC4gMjE5LTIyNi4KICAgICAqICAgW2hlcmVhZnRlciAiRmFnZXJzdHLDtm0gMjAyMCJdCiAgICAgKgogICAgICogRnJlcXVlbmN5LWRlcGVuZGVudCBkZWNheSAoVDYwKSBpcyBzZXQgYnkgcGVyLWRlbGF5LWxpbmUgYWJzb3JwdGlvbiBmaWx0ZXJzLAogICAgICogTk9UIGJ5IHRoZSBmZWVkYmFjayBtYXRyaXgsIGZvbGxvd2luZyB0aGUgc3RhbmRhcmQgRkROIGRlc2lnbiByZWNpcGUgb2Y6CiAgICAgKiAgIEouLU0uIEpvdCwgQS4gQ2hhaWduZSwgIkRpZ2l0YWwgZGVsYXkgbmV0d29ya3MgZm9yIGRlc2lnbmluZyBhcnRpZmljaWFsCiAgICAgKiAgIHJldmVyYmVyYXRvcnMiLCBBRVMgQ29udmVudGlvbiA5MCwgMTk5MS4gW2hlcmVhZnRlciAiSm90IDE5OTEiXQogICAgICoKICAgICAqIOKUgOKUgCBTdHJ1Y3R1cmUgKFNjaGxlY2h0IDIwMjAgwqdJSTsgRmFnZXJzdHLDtm0gMjAyMCBlcXMuIDEtMykg4pSA4pSACiAgICAgKiBBbiBGRE4gb2YgTiBkZWxheSBsaW5lcyB6XnstbV9pfSBtaXhlZCB0aHJvdWdoIGFuIE7Dl04gZmVlZGJhY2sgbWF0cml4IEEoeiksCiAgICAgKiB3aXRoIGlucHV0IGdhaW5zIGIsIG91dHB1dCBnYWlucyBjLCBhbmQgZGlyZWN0IChkcnkpIGdhaW4gZC4gVGhlIHRyYW5zZmVyIGlzCiAgICAgKiAgIEgoeikgPSBj4bWAIChEX20oeileey0xfSDiiJIgQSh6KSleey0xfSBiICsgZCAsICBEX20oeikgPSBkaWFnKHpeey1tXzF94oCmel57LW1fTn0pCiAgICAgKiAoU2NobGVjaHQgMjAyMCDCp0lJOyBGYWdlcnN0csO2bSAyMDIwIGVxLiAzKS4gUGVyLXNhbXBsZSwgd2l0aCBxX2kobikgdGhlCiAgICAgKiBvdXRwdXQgb2YgZGVsYXkgbGluZSBpIChGYWdlcnN0csO2bSAyMDIwIGVxcy4gMS0yKToKICAgICAqICAgeShuKSAgICAgICAgICAgID0gzqNfaSBjX2kgcV9pKG4pICsgZMK3eChuKQogICAgICogICBzX2kobiArIG1faSkgICAgPSDOo19qIEFfe2lqfSBxX2oobikgKyBiX2nCt3gobikKICAgICAqCiAgICAgKiDilIDilIAgU2NhdHRlcmluZzogRGVsYXkgRmVlZGJhY2sgTWF0cml4LCBERk0gKFNjaGxlY2h0IDIwMjAgwqdJVi1BLCBlcS4gMTQpIOKUgOKUgAogICAgICogVGhlIGNsYXNzaWMgRkROIHVzZXMgYSBTQ0FMQVIgZmVlZGJhY2sgbWF0cml4IEEgKGRlZ3JlZS0wIHBhcmF1bml0YXJ5KS4gVGhhdAogICAgICogaXMgdGhlIGxvd2VzdC1lY2hvLWRlbnNpdHkgYmFzZWxpbmUgKFNjaGxlY2h0IDIwMjAgRmlnLiA0YSAiU2NhbGFyL0VCRk0iKS4gVG8KICAgICAqIHNjYXR0ZXIgZWNob2VzIGluc2lkZSB0aGUgZmVlZGJhY2sgcGF0aCBXSVRIT1VUIGFkZGluZyBkZWxheSBsaW5lcywgU2NobGVjaHQKICAgICAqIDIwMjAgZ2VuZXJhbGl6ZXMgQSB0byBhICpmaWx0ZXIgZmVlZGJhY2sgbWF0cml4KiBBKHopLiBUaGUgY2hlYXBlc3Qgc3VjaAogICAgICogY29uc3RydWN0aW9uIGlzIHRoZSAqKkRlbGF5IEZlZWRiYWNrIE1hdHJpeCoqIChTY2hsZWNodCAyMDIwIGVxLiAxNCk6CiAgICAgKiAgIEEoeikgPSBVIMK3IERfzrooeikgLCAgIERfzrooeikgPSBkaWFnKHpeey3Oul8xfSwg4oCmLCB6XnstzrpfTn0pCiAgICAgKiB3aGVyZSBVIGlzIGEgc2NhbGFyIG9ydGhvZ29uYWwvdW5pdGFyeSBtYXRyaXggKGhlcmUgYSBub3JtYWxpemVkIEhhZGFtYXJkKQogICAgICogYW5kIERfzrooeikgaXMgYSBkaWFnb25hbCBvZiAqcHVyZSBpbnRlZ2VyIGRlbGF5cyogzrpfaSBvbiB0aGUgZmVlZGJhY2sgcGF0aHMuCiAgICAgKiBJdCBpcyBwYXJhdW5pdGFyeSDigJQgYSB1bml0YXJ5IFUgdGltZXMgYSBkaWFnb25hbCBvZiBwdXJlIGRlbGF5cyBpcyBsb3NzbGVzcywKICAgICAqIMODKHopQSh6KSA9IERfzrooel57LTF9KSBV4bWAIFUgRF/Ouih6KSA9IEkgKFNjaGxlY2h0IDIwMjAgwqdJSUksIMKnSVYtQSkg4oCUIHNvIHRoZQogICAgICogcmVjdXJzaXZlIGNvcmUgc3RpbGwgaGFzIGFsbCBwb2xlcyBvbiB0aGUgdW5pdCBjaXJjbGUuIEVhY2ggcHJlLW1peCBkZWxheSDOul9pCiAgICAgKiBzcHJlYWRzIHRoZSBlY2hvIGVhY2ggZGVsYXktbGluZSBvdXRwdXQgcHJvZHVjZXMgYWNyb3NzIHRoZSBmZWVkYmFjayBwYXRocywKICAgICAqIHJhaXNpbmcgZWNobyBkZW5zaXR5IHBlciB0cmF2ZXJzYWwgKFNjaGxlY2h0IDIwMjAgwqdWSSwgZXEuIDM2OiBlY2hvIGdhaW4gaXMgYQogICAgICogUFJPRFVDVCBvdmVyIHRoZSBwYXRoIHNvIGVhY2ggZXh0cmEgaW50ZXJuYWwgZGVsYXkgbXVsdGlwbGllcyB0aGUgZWNobwogICAgICogY291bnQpLiBTZXR0aW5nIGFsbCDOul9pID0gMCByZWNvdmVycyB0aGUgc2NhbGFyIEhhZGFtYXJkIGJhc2VsaW5lLiBUaGlzIGNvcmUKICAgICAqIHVzZXMgc21hbGwsIG11dHVhbGx5LXNwcmVhZCDOul9pIHNvIHRoZSBERk0gZ2VudWluZWx5IHNjYXR0ZXJzLgogICAgICoKICAgICAqIOKUgOKUgCBQZXItbGluZSB2ZWx2ZXQgZGlmZnVzaW9uOiBWRkROICJzaW5nbGUiIChGYWdlcnN0csO2bSAyMDIwIMKnMywgRmlnLiA0KSDilIDilIAKICAgICAqIEZhZ2Vyc3Ryw7ZtIDIwMjAncyBWRkROIGluc2VydHMgdmVsdmV0LW5vaXNlIEZJUnMgaW50byB0aGUgRkROLiBUaGlzIGNvcmUKICAgICAqIGFwcGxpZXMgYSBESVNUSU5DVCBwZXItbGluZSBWTlMgdG8gdGhlIElOUFVUIGluamVjdGlvbiBvZiBlYWNoIGRlbGF5IGxpbmUg4oCUCiAgICAgKiB0aGUgc2luZ2xlIGlucHV0LXNldCBwbGFjZW1lbnQgKEZhZ2Vyc3Ryw7ZtIDIwMjAgZXEuIDExKTogZWFjaCBsaW5lIGlzCiAgICAgKiBkZWNvcnJlbGF0ZWQgYW5kIGVhcmx5IGVjaG8gZGVuc2l0eSByaXNlcyB0byB+TSBzcGFyc2UgZWNob2VzIHBlciBsaW5lLgogICAgICogTk9URSDigJQgaG9uZXN0IHNjb3BlOiB0aGlzIGNvcmUgZG9lcyBOT1QgcGxhY2UgVk5TIG9uIHRoZSBvdXRwdXQgYnJhbmNoIG9yCiAgICAgKiBpbnNpZGUgdGhlIGZlZWRiYWNrIGxvb3AsIHNvIHRoZSBkZW5zaXR5IGRvZXMgTk9UIGNvbXBvdW5kIHRvIHRoZSDiiYggTcKyIG9mIHRoZQogICAgICogZnVsbCBpbnB1dCtvdXRwdXQgY29uZmlndXJhdGlvbiAoZXEuIDEyKSDigJQgdGhhdCB3b3VsZCByZXF1aXJlIGFkZGl0aW9uYWwgVk5TCiAgICAgKiBzZXRzIG5vdCBpbXBsZW1lbnRlZCBoZXJlLiBBIFZOUyBoYXMgdGFwcyDiiIggeysxLCAwLCDiiJIxfSwgc28gdGhlIGNvbnZvbHV0aW9uIGlzCiAgICAgKiBhIHN1bSBvZiBzaWduZWQgZGVsYXllZCBzYW1wbGVzIOKAlCBtdWx0aXBsaWNhdGlvbi1mcmVlIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KS4KICAgICAqIERpc3RpbmN0IHBlci1saW5lIGZpbHRlcnMgKHRoZSAiVkZETiBzaW5nbGUiIGNvbmZpZ3VyYXRpb24pIGRlY29ycmVsYXRlIHRoZQogICAgICogbGluZXMsIGluc3RlYWQgb2Ygb25lIHNoYXJlZCBkaWZmdXNlciB3cml0aW5nIHRoZSBzYW1lIHZhbHVlIGludG8gZXZlcnkgbGluZS4gR3JpZCBzaXplCiAgICAgKiBUX2QgPSBmcy/PgV9kIChlcS4gNCksIE0gPSBMX3MvVF9kIGltcHVsc2VzIChlcS4gNSksIHRhcCBsb2NhdGlvbgogICAgICogayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoIobSkoVF9kIOKIkiAxKSkgKGVxLiA3KSwgc2lnbiBz4oKBKG0pID0gMsK3cm91bmQocuKCgSkg4oiSIDEKICAgICAqIOKIiCB7KzEs4oiSMX0gKGVxLiA2KS4gSml0dGVyZWQgbG9jYXRpb25zIGF2b2lkIGNvbWIgY29sb3JhdGlvbiAoRmFnZXJzdHLDtm0gMjAyMAogICAgICogwqcyLjMpLiBUaGUgcGVyLWxpbmUgZGlmZnVzaW9uIGlzIHBhcmFtZXRlcml6ZWQgYnkgYGRpZmZ1c2lvbmAgKDAgPSBieXBhc3MpLgogICAgICoKICAgICAqIOKUgOKUgCBEZWNheSAoSm90IDE5OTEsIHBlci1saW5lIGFic29ycHRpb24gZmlsdGVycykg4pSA4pSACiAgICAgKiBFYWNoIGRlbGF5IGxpbmUgaSBjYXJyaWVzIGEgcGVyLXNhbXBsZSBnYWluIGdfaSA9IDEwXnstMyBtX2kgLyAoVDYwwrdmcyl9IHNvCiAgICAgKiB0aGF0IGFmdGVyIFQ2MCBzZWNvbmRzIGEgc2lnbmFsIGNpcmN1bGF0aW5nIHRoYXQgbGluZSBoYXMgZGVjYXllZCBieSA2MCBkQgogICAgICogKEpvdCAxOTkxOiBnYWluIHBlciBkZWxheSA9IDEwXnstMyBtX2kgLyAoVDYwIGZzKX0g4oeSIOKIkjYwIGRCIG92ZXIgVDYwKS4KICAgICAqIEhpZ2gtZnJlcXVlbmN5IGRhbXBpbmcgaXMgYSBvbmUtcG9sZSBsb3ctcGFzcyBwZXIgbGluZSB0aGF0IHNob3J0ZW5zIHRoZSBIRgogICAgICogVDYwIHJlbGF0aXZlIHRvIHRoZSBicm9hZGJhbmQgVDYwIChKb3QgMTk5MSBmcmVxdWVuY3ktZGVwZW5kZW50IGFic29ycHRpb24pLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzIGFuZCBGbG9hdDMyQXJyYXkpLiBJdAogICAgICogaGFzIE5PIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMgc28gaXQgY2FuIGJlIHVuaXQtdGVzdGVkIGRpcmVjdGx5OwogICAgICogdGhlIHdvcmtsZXQgc2hlbGwgaW4gZmRuLXJldmVyYi50cyBkZWxlZ2F0ZXMgdG8gaXQuCiAgICAgKi8KICAgIC8qKiBTcGVlZCBvZiBzb3VuZCBpcyBpcnJlbGV2YW50IGhlcmU7IHRoaXMgaXMgdGhlIOKIkjYwIGRCIGNvbnN0YW50IDEwXigtMykuICovCiAgICAvKiogU21hbGxlc3QgVDYwIHdlIHdpbGwgaG9ub3IsIHRvIGtlZXAgdGhlIHBlci1saW5lIGdhaW4gZmluaXRlIGFuZCA8IDEuICovCiAgICBjb25zdCBNSU5fREVDQVlfVElNRSA9IDFlLTM7CiAgICAvKioKICAgICAqIERlZmF1bHQgZGVsYXktbGluZSBsZW5ndGhzIGluIHNhbXBsZXMgKE4gPSA4KSwgbXV0dWFsbHkgY29wcmltZSAvIHNwcmVhZCB0bwogICAgICogYXZvaWQgY29pbmNpZGVudCBlY2hvZXMgKFNjaGxlY2h0IDIwMjAgwqdJSSBkZXNpZ24gbG9yZTogImNob29zZSBOIGRlbGF5CiAgICAgKiBsZW5ndGhzIGNvcHJpbWUvc3ByZWFkIikuIFRoZXNlIHByaW1lcyBzcGFuIH4xM+KAkzU4IG1zIGF0IDQ4IGtIeiwgYSB0eXBpY2FsCiAgICAgKiBtZWRpdW0tcm9vbSBzZXQuIFNjYWxlZCB0byB0aGUgYWN0dWFsIHNhbXBsZSByYXRlIGF0IGNvbnN0cnVjdGlvbi4KICAgICAqLwogICAgY29uc3QgREVGQVVMVF9ERUxBWVNfQVRfNDhLID0gWzYxNywgNzY5LCA5MTksIDEwOTcsIDEyNTksIDE0MjksIDE2MDcsIDE3ODldOwogICAgLyoqCiAgICAgKiBEZWZhdWx0IERGTSBmZWVkYmFjay1wYXRoIGRlbGF5cyDOul9pIGluIHNhbXBsZXMgYXQgNDgga0h6IChTY2hsZWNodCAyMDIwIGVxLgogICAgICogMTQsIERfzrooeikgPSBkaWFnKHpeey3Oul9pfSkpLiBTbWFsbCwgbXV0dWFsbHktc3ByZWFkIGNvcHJpbWUgaW50ZWdlcnMgc28gZWFjaAogICAgICogZmVlZGJhY2sgcGF0aCBpcyBkZWxheWVkIGJ5IGEgZGlmZmVyZW50IHNob3J0IGFtb3VudCBiZWZvcmUgdGhlIEhhZGFtYXJkIG1peCwKICAgICAqIHNjYXR0ZXJpbmcgZWFjaCBlY2hvIGFjcm9zcyB0aGUgbGluZXMgKFNjaGxlY2h0IDIwMjAgwqdJVi1BLCDCp1ZJKS4gS2VwdCBzbWFsbAogICAgICogKHN1Yi1tcyB0byBhIGZldyBtcykgc28gdGhlIERGTSBzY2F0dGVycyBlYXJseSByZWZsZWN0aW9ucyB3aXRob3V0IHNtZWFyaW5nCiAgICAgKiB0aGUgYnJvYWRiYW5kIGRlY2F5LiBBbGwtemVybyB3b3VsZCBjb2xsYXBzZSB0aGUgREZNIHRvIHRoZSBzY2FsYXIgYmFzZWxpbmUuCiAgICAgKi8KICAgIGNvbnN0IERFRkFVTFRfRkVFREJBQ0tfREVMQVlTX0FUXzQ4SyA9IFs3LCAxMywgMjMsIDMxLCA0MSwgNTMsIDYxLCA3M107CiAgICAvKioKICAgICAqIEJ1aWxkIGEgbm9ybWFsaXplZCBIYWRhbWFyZCBtYXRyaXggSC/iiJpOIGZvciBOIGEgcG93ZXIgb2YgdHdvIHZpYSB0aGUKICAgICAqIFN5bHZlc3RlciBjb25zdHJ1Y3Rpb24uIEggaGFzIMKxMSBlbnRyaWVzIGFuZCBI4bWASCA9IE7Ct0ksIHNvIEgv4oiaTiBpcwogICAgICogb3J0aG9nb25hbCDigJQgdGhlIHNjYWxhciB1bml0YXJ5IFUgb2YgdGhlIERlbGF5IEZlZWRiYWNrIE1hdHJpeCBBKHopID0KICAgICAqIFXCt0RfzrooeikgKFNjaGxlY2h0IDIwMjAgZXEuIDE0KSwgYSBwYXJhdW5pdGFyeSBGRk0uIE9ydGhvZ29uYWxpdHkgb2YgVSBwbHVzIGEKICAgICAqIHB1cmUtZGVsYXkgZGlhZ29uYWwg4oeSIHRoZSBGRE4gZmVlZGJhY2sgY29yZSBpcyBsb3NzbGVzcyDih5IgcG9sZXMgb24gdGhlIHVuaXQKICAgICAqIGNpcmNsZSDih5Igc3RhYmxlLCB3aXRoIGRlY2F5IHN1cHBsaWVkIHNlcGFyYXRlbHkgYnkgdGhlIGFic29ycHRpb24gZmlsdGVycy4KICAgICAqCiAgICAgKiBAcGFyYW0gbiBOdW1iZXIgb2YgZGVsYXkgbGluZXMgKG11c3QgYmUgYSBwb3dlciBvZiB0d28pLgogICAgICogQHJldHVybnMgRmxhdCByb3ctbWFqb3IgTsOXTiBtYXRyaXgsIHNjYWxlZCBieSAxL+KImk4gKHNvIGl0IGlzIG9ydGhvbm9ybWFsKS4KICAgICAqLwogICAgZnVuY3Rpb24gYnVpbGRIYWRhbWFyZE1hdHJpeChuKSB7CiAgICAgICAgaWYgKG4gPCAxIHx8IChuICYgKG4gLSAxKSkgIT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBIYWRhbWFyZCBtYXRyaXggcmVxdWlyZXMgYSBwb3dlci1vZi10d28gc2l6ZSwgZ290ICR7bn1gKTsKICAgICAgICB9CiAgICAgICAgLy8gU3lsdmVzdGVyOiBIXzEgPSBbMV07IEhfezJrfSA9IFtbSF9rLCBIX2tdLCBbSF9rLCDiiJJIX2tdXS4KICAgICAgICBjb25zdCBoID0gbmV3IEZsb2F0MzJBcnJheShuICogbik7CiAgICAgICAgaFswXSA9IDE7CiAgICAgICAgZm9yIChsZXQgc2l6ZSA9IDE7IHNpemUgPCBuOyBzaXplIDw8PSAxKSB7CiAgICAgICAgICAgIGZvciAobGV0IHIgPSAwOyByIDwgc2l6ZTsgcisrKSB7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHNpemU7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHYgPSBoW3IgKiBuICsgY107CiAgICAgICAgICAgICAgICAgICAgaFtyICogbiArIChjICsgc2l6ZSldID0gdjsKICAgICAgICAgICAgICAgICAgICBoWyhyICsgc2l6ZSkgKiBuICsgY10gPSB2OwogICAgICAgICAgICAgICAgICAgIGhbKHIgKyBzaXplKSAqIG4gKyAoYyArIHNpemUpXSA9IC12OwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGNvbnN0IG5vcm0gPSAxIC8gTWF0aC5zcXJ0KG4pOwogICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbiAqIG47IGkrKykKICAgICAgICAgICAgaFtpXSAqPSBub3JtOwogICAgICAgIHJldHVybiBoOwogICAgfQogICAgLyoqCiAgICAgKiBDb25zdHJ1Y3QgYSB2ZWx2ZXQtbm9pc2Ugc2VxdWVuY2UgKEZhZ2Vyc3Ryw7ZtIDIwMjAgwqcyLjMsIGVxcy4gNC03KS4KICAgICAqCiAgICAgKiBHcmlkIHNpemUgVF9kID0gZnMvz4FfZCAoZXEuIDQpOyBudW1iZXIgb2YgaW1wdWxzZXMgTSA9IExfcy9UX2QgKGVxLiA1KTsgb25lCiAgICAgKiBqaXR0ZXJlZCBpbXB1bHNlIHBlciBncmlkIGNlbGwgYXQgayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoIobSkoVF9kIOKIkiAxKSkgKGVxLgogICAgICogNykgd2l0aCBzaWduIHPigoEobSkgPSAywrdyb3VuZChy4oKBKG0pKSDiiJIgMSDiiIggeysxLOKIkjF9IChlcS4gNikuIFRoZSBqaXR0ZXIgYXZvaWRzCiAgICAgKiB0aGUgcGVyaW9kaWMgY29tYiBjb2xvcmF0aW9uIGEgcmVndWxhciBwdWxzZSB0cmFpbiB3b3VsZCBjYXVzZSAoRmFnZXJzdHLDtm0KICAgICAqIDIwMjAgwqcyLjMgZGVzaWduIHJhdGlvbmFsZSkuIEFsbCBub24tdGFwIHBvc2l0aW9ucyBhcmUgaW1wbGljaXRseSAwLCBzbyB0aGUKICAgICAqIHNlcXVlbmNlIGhhcyBleGFjdGx5IE0gbm9uLXplcm8gwrExIGVudHJpZXMg4oCUIGV2ZXJ5IHZhbHVlIGlzICsxLCAwLCBvciDiiJIxLgogICAgICoKICAgICAqIEBwYXJhbSBsZW5ndGhTYW1wbGVzIExfcywgdG90YWwgbGVuZ3RoIG9mIHRoZSBzZXF1ZW5jZSBpbiBzYW1wbGVzLgogICAgICogQHBhcmFtIGRlbnNpdHkgz4FfZCwgcHVsc2UgZGVuc2l0eSBpbiBpbXB1bHNlcy9zZWNvbmQuCiAgICAgKiBAcGFyYW0gc2FtcGxlUmF0ZSBmcyBpbiBIei4KICAgICAqIEBwYXJhbSBybmcgQSBmdW5jdGlvbiByZXR1cm5pbmcgYSB1bmlmb3JtIHJhbmRvbSBudW1iZXIgaW4gWzAsMSkuIEluamVjdGVkIHNvCiAgICAgKiAgIHRlc3RzIGFyZSBkZXRlcm1pbmlzdGljLgogICAgICogQHJldHVybnMgVGhlIHNwYXJzZSB0YXAgbGlzdCAobGVuZ3RoIE0pLCBzb3J0ZWQgYnkgbG9jYXRpb24uCiAgICAgKi8KICAgIGZ1bmN0aW9uIGJ1aWxkVmVsdmV0Tm9pc2UobGVuZ3RoU2FtcGxlcywgZGVuc2l0eSwgc2FtcGxlUmF0ZSwgcm5nID0gTWF0aC5yYW5kb20pIHsKICAgICAgICBjb25zdCBncmlkU2l6ZSA9IHNhbXBsZVJhdGUgLyBkZW5zaXR5OyAvLyBUX2QgKGVxLiA0KQogICAgICAgIGNvbnN0IG0gPSBNYXRoLm1heCgwLCBNYXRoLmZsb29yKGxlbmd0aFNhbXBsZXMgLyBncmlkU2l6ZSkpOyAvLyBNIChlcS4gNSkKICAgICAgICBjb25zdCB0YXBzID0gW107CiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtOyBpKyspIHsKICAgICAgICAgICAgY29uc3QgcjEgPSBybmcoKTsKICAgICAgICAgICAgY29uc3QgcjIgPSBybmcoKTsKICAgICAgICAgICAgLy8gayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoLCtyhUX2Qg4oiSIDEpKSAoZXEuIDcpLgogICAgICAgICAgICBjb25zdCBsb2NhdGlvbiA9IE1hdGgucm91bmQoaSAqIGdyaWRTaXplICsgcjIgKiAoZ3JpZFNpemUgLSAxKSk7CiAgICAgICAgICAgIGlmIChsb2NhdGlvbiA+PSBsZW5ndGhTYW1wbGVzKQogICAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICAgIC8vIHPigoEobSkgPSAywrdyb3VuZChy4oKBKSDiiJIgMSDiiIggeysxLOKIkjF9IChlcS4gNikuCiAgICAgICAgICAgIGNvbnN0IHNpZ24gPSAyICogTWF0aC5yb3VuZChyMSkgLSAxID49IDAgPyAxIDogLTE7CiAgICAgICAgICAgIHRhcHMucHVzaCh7IGxvY2F0aW9uLCBzaWduIH0pOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdGFwczsKICAgIH0KICAgIC8qKgogICAgICogQSBtdWx0aXBsaWNhdGlvbi1mcmVlIHZlbHZldC1ub2lzZSBGSVIgZmlsdGVyIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KS4gSG9sZHMKICAgICAqIGl0cyBvd24gc3BhcnNlIMKxMSB0YXAgbGlzdCBhbmQgYSBwb3dlci1vZi10d28gcmluZyBidWZmZXIgb2YgcmVjZW50IGlucHV0cyBzbwogICAgICogYHByb2Nlc3MoeClgIHJldHVybnMgzqNfbSBz4oKBKG0pwrd4KG4g4oiSIGsobSkpIOKAlCBhIHN1bSBvZiBzaWduZWQgZGVsYXllZCBzYW1wbGVzLAogICAgICogbm8gbXVsdGlwbGljYXRpb25zLiBPbmUgb2YgdGhlc2UgbGl2ZXMgb24gZWFjaCBGRE4gZGVsYXkgbGluZSBpbiB0aGUgVkZETgogICAgICogInNpbmdsZSIgY29uZmlndXJhdGlvbiAoaW5wdXQtc2V0IHBsYWNlbWVudCksIHNvIGVhY2ggbGluZSBpcyBkZWNvcnJlbGF0ZWQgYW5kCiAgICAgKiBlYXJseSBlY2hvIGRlbnNpdHkgcmlzZXMgKGlucHV0IGluamVjdGlvbiBvbmx5IOKAlCBubyBNwrIgbG9vcCBjb21wb3VuZGluZykuCiAgICAgKi8KICAgIGNsYXNzIFZlbHZldEZpbHRlciB7CiAgICAgICAgdGFwczsKICAgICAgICBidWZmZXI7CiAgICAgICAgbWFzazsKICAgICAgICB3cml0ZSA9IDA7CiAgICAgICAgbm9ybTsKICAgICAgICBjb25zdHJ1Y3Rvcih0YXBzLCBzcGFuU2FtcGxlcykgewogICAgICAgICAgICB0aGlzLnRhcHMgPSB0YXBzOwogICAgICAgICAgICBjb25zdCBwb3cyID0gMiAqKiBNYXRoLmNlaWwoTWF0aC5sb2cyKE1hdGgubWF4KDIsIHNwYW5TYW1wbGVzICsgMSkpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICAgICAgLy8gTm9ybWFsaXplIGJ5IOKImk0gc28gdGhlIGRpZmZ1c2VyIGlzIH51bml0LWVuZXJneSAoYSBWTlMgb2YgTSB1bml0IHRhcHMgaGFzCiAgICAgICAgICAgIC8vIGV4cGVjdGVkIGVuZXJneSBNKTsga2VlcHMgdGhlIHBlci1saW5lIGluamVjdGlvbiBsZXZlbCBjb21wYXJhYmxlIHRvIHRoZQogICAgICAgICAgICAvLyB1bmRpZmZ1c2VkIHBhdGggc28gYGRpZmZ1c2lvbmAgaXMgYSBjbGVhbiBkcnnihpR3ZXQgYmxlbmQuCiAgICAgICAgICAgIHRoaXMubm9ybSA9IHRhcHMubGVuZ3RoID4gMCA/IDEgLyBNYXRoLnNxcnQodGFwcy5sZW5ndGgpIDogMTsKICAgICAgICB9CiAgICAgICAgLyoqIE51bWJlciBvZiBub256ZXJvIHRhcHMgTSAoZWNoby1kZW5zaXR5IG11bHRpcGxpZXIgb2YgdGhpcyBmaWx0ZXIpLiAqLwogICAgICAgIGdldCB0YXBDb3VudCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMudGFwcy5sZW5ndGg7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIENvbnZvbHZlIG9uZSBpbnB1dCBzYW1wbGUgd2l0aCB0aGUgVk5TIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KTogcHVzaCB4IGludG8KICAgICAgICAgKiB0aGUgcmluZywgdGhlbiBhY2N1bXVsYXRlIM6jX20gc+KCgShtKcK3eChuIOKIkiBrKG0pKSBhcyBzaWduZWQgYWRkcy4gUmV0dXJucyB0aGUKICAgICAgICAgKiDiiJpNLW5vcm1hbGl6ZWQgcmVzdWx0LiBNdWx0aXBsaWNhdGlvbi1mcmVlOiBlYWNoIHRlcm0gYWRkcyBvciBzdWJ0cmFjdHMgYQogICAgICAgICAqIHN0b3JlZCBzYW1wbGUuCiAgICAgICAgICovCiAgICAgICAgcHJvY2Vzcyh4KSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyW3RoaXMud3JpdGVdID0geDsKICAgICAgICAgICAgbGV0IGFjYyA9IDA7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy50YXBzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCB0YXAgPSB0aGlzLnRhcHNbaV07CiAgICAgICAgICAgICAgICBjb25zdCBpZHggPSAodGhpcy53cml0ZSAtIHRhcC5sb2NhdGlvbikgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgICAgICBhY2MgKz0gdGFwLnNpZ24gPT09IDEgPyB0aGlzLmJ1ZmZlcltpZHhdIDogLXRoaXMuYnVmZmVyW2lkeF07CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy53cml0ZSA9ICh0aGlzLndyaXRlICsgMSkgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgIHJldHVybiBhY2MgKiB0aGlzLm5vcm07CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlci5maWxsKDApOwogICAgICAgICAgICB0aGlzLndyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEEgc2luZ2xlIGRlbGF5IGxpbmUgd2l0aCBhbiBpbnRlZ3JhdGVkIG9uZS1wb2xlIEhGLWRhbXBpbmcgYWJzb3JwdGlvbiBmaWx0ZXIKICAgICAqIChKb3QgMTk5MSkuIFJlYWRzIHRhaWwsIGFwcGxpZXMgYnJvYWRiYW5kIGRlY2F5IGdhaW4gZyBhbmQgYW4gSEYgbG93LXBhc3MsCiAgICAgKiB0aGVuIGFjY2VwdHMgdGhlIGZlZWRiYWNrLW1peGVkIGlucHV0IHdyaXRlLiBCYWNrZWQgYnkgYSBwb3dlci1vZi10d28gcmluZwogICAgICogYnVmZmVyIGZvciBjaGVhcCBiaXRtYXNrIHdyYXBwaW5nIChtaXJyb3JzIGRhdHRvcnJvLXJldmVyYi50cykuCiAgICAgKi8KICAgIGNsYXNzIERhbXBlZERlbGF5TGluZSB7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgbHBTdGF0ZSA9IDA7IC8vIG9uZS1wb2xlIGxvdy1wYXNzIG1lbW9yeSAoSm90IEhGIGFic29ycHRpb24pCiAgICAgICAgbGVuZ3RoOwogICAgICAgIGNvbnN0cnVjdG9yKGxlbmd0aFNhbXBsZXMpIHsKICAgICAgICAgICAgdGhpcy5sZW5ndGggPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKGxlbmd0aFNhbXBsZXMpKTsKICAgICAgICAgICAgY29uc3QgcG93MiA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0aGlzLmxlbmd0aCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICB9CiAgICAgICAgLyoqIFJlYWQgdGhlIHNhbXBsZSBkZWxheWVkIGJ5IGBsZW5ndGhgICh0aGUgdGFpbCBvZiB0aGUgbGluZSkuICovCiAgICAgICAgcmVhZCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSB0aGlzLmxlbmd0aCkgJiB0aGlzLm1hc2tdOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBBcHBseSBicm9hZGJhbmQgZGVjYXkgZ2FpbiBgZ2AgYW5kIEhGIGRhbXBpbmcgY29lZmZpY2llbnQgYGRhbXBgIHRvIGEKICAgICAgICAgKiB0YWlsIHNhbXBsZS4geVtuXSA9IGfCtygoMeKIkmRhbXApwrd0YWlsICsgZGFtcMK3bHBTdGF0ZSk7IGxwU3RhdGUgdHJhY2tzIHRoZQogICAgICAgICAqIHNtb290aGVkIChsb3ctZnJlcXVlbmN5KSBjb21wb25lbnQsIHNvIGxhcmdlciBgZGFtcGAgcmVtb3ZlcyBtb3JlIEhGIGVuZXJneQogICAgICAgICAqIGVhY2ggcGFzcyDih5Igc2hvcnRlciBIRiBUNjAgKEpvdCAxOTkxIGZyZXF1ZW5jeS1kZXBlbmRlbnQgYWJzb3JwdGlvbikuCiAgICAgICAgICovCiAgICAgICAgYWJzb3JiKHRhaWwsIGcsIGRhbXApIHsKICAgICAgICAgICAgdGhpcy5scFN0YXRlID0gKDEgLSBkYW1wKSAqIHRhaWwgKyBkYW1wICogdGhpcy5scFN0YXRlOwogICAgICAgICAgICByZXR1cm4gZyAqIHRoaXMubHBTdGF0ZTsKICAgICAgICB9CiAgICAgICAgLyoqIFdyaXRlIHRoZSBmZWVkYmFjay1taXhlZCBpbnB1dCBhbmQgYWR2YW5jZSB0aGUgd3JpdGUgaGVhZCBvbmUgc2FtcGxlLiAqLwogICAgICAgIHdyaXRlKHZhbHVlKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyW3RoaXMud3JpdGVJbmRleF0gPSB2YWx1ZTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXIuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5scFN0YXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEEgcHVyZS1kZWxheSByaW5nIGJ1ZmZlcjogdGhlIGRpYWdvbmFsIHB1cmUtZGVsYXkgZWxlbWVudCB6Xnstzrp9IG9mIHRoZSBEZWxheQogICAgICogRmVlZGJhY2sgTWF0cml4IChTY2hsZWNodCAyMDIwIGVxLiAxNCwgRF/Ouih6KSA9IGRpYWcoel57Lc66X2l9KSkuIEFwcGxpZWQgdG8gYQogICAgICogZmVlZGJhY2sgcGF0aCBiZWZvcmUgdGhlIEhhZGFtYXJkIG1peC4gQSDOuiBvZiAwIGlzIGEgdHJhbnNwYXJlbnQgcGFzcy10aHJvdWdoCiAgICAgKiAocmVjb3ZlcnMgdGhlIHNjYWxhciBmZWVkYmFjayBtYXRyaXgpLiBObyBmaWx0ZXJpbmcsIG5vIGdhaW4g4oCUIHBhcmF1bml0YXJpdHkKICAgICAqIGlzIHByZXNlcnZlZCBiZWNhdXNlIGEgcHVyZSBpbnRlZ2VyIGRlbGF5IGlzIGFsbHBhc3MvbG9zc2xlc3MuCiAgICAgKi8KICAgIGNsYXNzIFB1cmVEZWxheSB7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGUgPSAwOwogICAgICAgIGRlbGF5OwogICAgICAgIGNvbnN0cnVjdG9yKGRlbGF5U2FtcGxlcykgewogICAgICAgICAgICB0aGlzLmRlbGF5ID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihkZWxheVNhbXBsZXMpKTsKICAgICAgICAgICAgY29uc3QgcG93MiA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMihNYXRoLm1heCgyLCB0aGlzLmRlbGF5ICsgMSkpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICB9CiAgICAgICAgLyoqIFB1c2ggeCwgcmV0dXJuIHRoZSBzYW1wbGUgZGVsYXllZCBieSDOuiAozro9MCByZXR1cm5zIHgganVzdCB3cml0dGVuKS4gKi8KICAgICAgICBwcm9jZXNzKHgpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZV0gPSB4OwogICAgICAgICAgICBjb25zdCBvdXQgPSB0aGlzLmJ1ZmZlclsodGhpcy53cml0ZSAtIHRoaXMuZGVsYXkpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZSA9ICh0aGlzLndyaXRlICsgMSkgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgIHJldHVybiBvdXQ7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlci5maWxsKDApOwogICAgICAgICAgICB0aGlzLndyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEZlZWRiYWNrIERlbGF5IE5ldHdvcmsgcmV2ZXJiZXJhdG9yIGNvcmUuCiAgICAgKgogICAgICogTiBwYXJhbGxlbCB7QGxpbmsgRGFtcGVkRGVsYXlMaW5lfXMgbWl4ZWQgYnkgYSBsb3NzbGVzcyBEZWxheSBGZWVkYmFjayBNYXRyaXgKICAgICAqIEEoeikgPSBIwrdEX866KHopIChTY2hsZWNodCAyMDIwIGVxLiAxNDogSGFkYW1hcmQgVSBzY2F0dGVyZWQgYnkgcGVyLXBhdGggcHVyZQogICAgICogZGVsYXlzIERfzropLCB3aXRoIHBlci1saW5lIGFic29ycHRpb24gZmlsdGVycyBzZXR0aW5nIFQ2MCAoSm90IDE5OTEpIGFuZCBhCiAgICAgKiBwZXItZGVsYXktbGluZSB2ZWx2ZXQtbm9pc2UgZGlmZnVzZXIgKEZhZ2Vyc3Ryw7ZtIDIwMjAgVkZETiAic2luZ2xlIikuIFB1cmU6CiAgICAgKiBvcGVyYXRlcyBvbiBudW1iZXJzIC8gRmxvYXQzMkFycmF5LCBubyBnbG9iYWxzLgogICAgICovCiAgICBjbGFzcyBGZG5SZXZlcmJQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgbjsKICAgICAgICBsaW5lczsKICAgICAgICBtYXRyaXg7IC8vIHJvdy1tYWpvciBOw5dOLCBvcnRob25vcm1hbCAoVSkKICAgICAgICBmYkRlbGF5czsgLy8gRF/Ouih6KTogcGVyLXBhdGggcHVyZSBkZWxheXMgKERGTSkKICAgICAgICB2ZWx2ZXRzOyAvLyBwZXItbGluZSBWTlMgKEZhZ2Vyc3Ryw7ZtIFZGRE4gc2luZ2xlKQogICAgICAgIGZlZWRiYWNrOyAvLyBzY3JhdGNoOiBtYXRyaXjCt2RlbGF5ZWQtdGFpbAogICAgICAgIHNjYXR0ZXJlZDsgLy8gc2NyYXRjaDogRF/Ouih6KcK3dGFpbAogICAgICAgIHRhaWw7IC8vIHNjcmF0Y2g6IHBlci1saW5lIHRhaWwgcmVhZHMKICAgICAgICBsaW5lR2FpbnM7IC8vIHNjcmF0Y2g6IHBlci1saW5lIGRlY2F5IGdhaW5zCiAgICAgICAgLy8gUHJlLWRlbGF5IHJpbmcgYnVmZmVyIChTY2hsZWNodCAyMDIwIMKnSUkgaW5wdXQgcGF0aCkuCiAgICAgICAgcHJlRGVsYXlCdWZmZXI7CiAgICAgICAgcHJlRGVsYXlXcml0ZSA9IDA7CiAgICAgICAgLyoqCiAgICAgICAgICogQHBhcmFtIHNhbXBsZVJhdGUgZnMgaW4gSHouCiAgICAgICAgICogQHBhcmFtIG4gTnVtYmVyIG9mIGRlbGF5IGxpbmVzOyBtdXN0IGJlIGEgcG93ZXIgb2YgdHdvIGluIFs0LDhdLiBEZWZhdWx0IDguCiAgICAgICAgICogQHBhcmFtIHJuZyBJbmplY3RlZCBSTkcgZm9yIHRoZSB2ZWx2ZXQtbm9pc2Ugc2VxdWVuY2VzIChkZXRlcm1pbmlzdGljCiAgICAgICAgICogICB0ZXN0cykuIEVhY2ggZGVsYXkgbGluZSBkcmF3cyBhIERJU1RJTkNUIFZOUyBmcm9tIHRoaXMgc3RyZWFtLCBzbyB0aGUKICAgICAgICAgKiAgIGxpbmVzIGFyZSBkZWNvcnJlbGF0ZWQgKEZhZ2Vyc3Ryw7ZtIDIwMjAgVkZETiBzaW5nbGUpLgogICAgICAgICAqIEBwYXJhbSBkZWxheXNPdmVycmlkZSBPcHRpb25hbCBleHBsaWNpdCBkZWxheS1saW5lIGxlbmd0aHMgKHNhbXBsZXMpLCBmb3IKICAgICAgICAgKiAgIHRlc3RzOyBkZWZhdWx0cyB0byB0aGUgY29wcmltZSA0OCBrSHogc2V0IHNjYWxlZCB0byBgc2FtcGxlUmF0ZWAuCiAgICAgICAgICogQHBhcmFtIGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUgT3B0aW9uYWwgZXhwbGljaXQgREZNIGZlZWRiYWNrLXBhdGggZGVsYXlzIM66X2kKICAgICAgICAgKiAgIChzYW1wbGVzKSwgZm9yIHRlc3RzLiBBbGwtemVybyBjb2xsYXBzZXMgdGhlIERGTSB0byB0aGUgc2NhbGFyIEhhZGFtYXJkCiAgICAgICAgICogICBiYXNlbGluZSAoU2NobGVjaHQgMjAyMCBlcS4gMTQgd2l0aCBEX866ID0gSSkuIERlZmF1bHRzIHRvIGEgc21hbGwgc3ByZWFkLgogICAgICAgICAqLwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIG4gPSA4LCBybmcgPSBNYXRoLnJhbmRvbSwgZGVsYXlzT3ZlcnJpZGUsIGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUpIHsKICAgICAgICAgICAgaWYgKG4gIT09IDQgJiYgbiAhPT0gOCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGZG5SZXZlcmJQcm9jZXNzb3Igc3VwcG9ydHMgTiDiiIggezQsIDh9LCBnb3QgJHtufWApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMubiA9IG47CiAgICAgICAgICAgIGNvbnN0IGJhc2VEZWxheXMgPSBkZWxheXNPdmVycmlkZSA/PyBERUZBVUxUX0RFTEFZU19BVF80OEsuc2xpY2UoMCwgbik7CiAgICAgICAgICAgIGNvbnN0IHNjYWxlID0gZGVsYXlzT3ZlcnJpZGUgPyAxIDogc2FtcGxlUmF0ZSAvIDQ4MDAwOwogICAgICAgICAgICB0aGlzLmxpbmVzID0gYmFzZURlbGF5cy5zbGljZSgwLCBuKS5tYXAoKGQpID0+IG5ldyBEYW1wZWREZWxheUxpbmUoTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChkICogc2NhbGUpKSkpOwogICAgICAgICAgICB0aGlzLm1hdHJpeCA9IGJ1aWxkSGFkYW1hcmRNYXRyaXgobik7IC8vIHNjYWxhciB1bml0YXJ5IFUgb2YgQSh6KT1VwrdEX866KHopCiAgICAgICAgICAgIC8vIERGTSBmZWVkYmFjay1wYXRoIHB1cmUgZGVsYXlzIERfzrooeikgKFNjaGxlY2h0IDIwMjAgZXEuIDE0KS4gU2NhbGVkIHRvIGZzCiAgICAgICAgICAgIC8vIHVubGVzcyBhbiBleHBsaWNpdCBvdmVycmlkZSBpcyBnaXZlbiAodGVzdHMgbWF5IHBhc3MgYWxsLXplcm8gdG8gY29tcGFyZQogICAgICAgICAgICAvLyBhZ2FpbnN0IHRoZSBzY2FsYXItSGFkYW1hcmQgYmFzZWxpbmUpLgogICAgICAgICAgICBjb25zdCBiYXNlRmJEZWxheXMgPSBmZWVkYmFja0RlbGF5c092ZXJyaWRlID8/IERFRkFVTFRfRkVFREJBQ0tfREVMQVlTX0FUXzQ4Sy5zbGljZSgwLCBuKTsKICAgICAgICAgICAgY29uc3QgZmJTY2FsZSA9IGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUgPyAxIDogc2FtcGxlUmF0ZSAvIDQ4MDAwOwogICAgICAgICAgICB0aGlzLmZiRGVsYXlzID0gYmFzZUZiRGVsYXlzLnNsaWNlKDAsIG4pLm1hcCgoaykgPT4gbmV3IFB1cmVEZWxheShNYXRoLm1heCgwLCBNYXRoLnJvdW5kKGsgKiBmYlNjYWxlKSkpKTsKICAgICAgICAgICAgdGhpcy5mZWVkYmFjayA9IG5ldyBGbG9hdDMyQXJyYXkobik7CiAgICAgICAgICAgIHRoaXMuc2NhdHRlcmVkID0gbmV3IEZsb2F0MzJBcnJheShuKTsKICAgICAgICAgICAgdGhpcy50YWlsID0gbmV3IEZsb2F0MzJBcnJheShuKTsKICAgICAgICAgICAgdGhpcy5saW5lR2FpbnMgPSBuZXcgRmxvYXQzMkFycmF5KG4pOwogICAgICAgICAgICAvLyBQcmUtZGVsYXk6IHVwIHRvIDEgc2Vjb25kLgogICAgICAgICAgICBjb25zdCBwcmVMZW4gPSBNYXRoLm1heCgxLCBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5wcmVEZWxheUJ1ZmZlciA9IG5ldyBGbG9hdDMyQXJyYXkocHJlTGVuKTsKICAgICAgICAgICAgLy8gUGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2VycyAoRmFnZXJzdHLDtm0gMjAyMCBWRkROICJzaW5nbGUiKS4KICAgICAgICAgICAgLy8gRWFjaCBsaW5lIGdldHMgaXRzIE9XTiBkaXN0aW5jdCBWTlMgKH4yMCBtcyBzcGFuLCDPgV9kID0gMTAwMCBwdWxzZXMvcyDih5IKICAgICAgICAgICAgLy8gfjIwIHRhcHMpLCBkcmF3biBmcm9tIHRoZSBzaGFyZWQgUk5HIHN0cmVhbSBzbyB0aGUgTiBmaWx0ZXJzIGRpZmZlci4gV2l0aAogICAgICAgICAgICAvLyBhIGRpc3RpbmN0IGZpbHRlciBwZXIgbGluZSwgZWFjaCBsaW5lJ3MgaW5wdXQgaW5qZWN0aW9uIGlzIGRlY29ycmVsYXRlZAogICAgICAgICAgICAvLyAoc2luZ2xlIGlucHV0LXNldCwgfk0gZWNob2VzL2xpbmU7IE5PVCB0aGUgTcKyIGlucHV0K291dHB1dCBjYXNlLCBlcS4gMTIpLgogICAgICAgICAgICBjb25zdCB2ZWx2ZXRMZW4gPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKDAuMDIgKiBzYW1wbGVSYXRlKSk7CiAgICAgICAgICAgIHRoaXMudmVsdmV0cyA9IFtdOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICAgICAgdGhpcy52ZWx2ZXRzLnB1c2gobmV3IFZlbHZldEZpbHRlcihidWlsZFZlbHZldE5vaXNlKHZlbHZldExlbiwgMTAwMCwgc2FtcGxlUmF0ZSwgcm5nKSwgdmVsdmV0TGVuKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIE51bWJlciBvZiBkZWxheSBsaW5lcy4gKi8KICAgICAgICBnZXQgc2l6ZSgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMubjsKICAgICAgICB9CiAgICAgICAgLyoqIFRoZSBzY2FsYXIgdW5pdGFyeSBVIChyb3ctbWFqb3IpIG9mIHRoZSBERk0gQSh6KT1VwrdEX866KHopLCBmb3IgdmVyaWZpY2F0aW9uLiAqLwogICAgICAgIGdldCBmZWVkYmFja01hdHJpeCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMubWF0cml4OwogICAgICAgIH0KICAgICAgICAvKiogVGhlIERGTSBmZWVkYmFjay1wYXRoIGRlbGF5cyDOul9pIChzYW1wbGVzKSwgZm9yIHZlcmlmaWNhdGlvbi4gKi8KICAgICAgICBnZXQgZmVlZGJhY2tEZWxheXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLmZiRGVsYXlzLm1hcCgoZCkgPT4gZC5kZWxheSk7CiAgICAgICAgfQogICAgICAgIC8qKiBQZXItbGluZSB2ZWx2ZXQgdGFwIGNvdW50cyBNX2kgKGVjaG8tZGVuc2l0eSBtdWx0aXBsaWVycyksIGZvciB2ZXJpZmljYXRpb24uICovCiAgICAgICAgZ2V0IHZlbHZldFRhcENvdW50cygpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMudmVsdmV0cy5tYXAoKHYpID0+IHYudGFwQ291bnQpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQZXItbGluZSBicm9hZGJhbmQgZGVjYXkgZ2FpbiBnX2kgPSAxMF57LTMgbV9pLyhUNjAgZnMpfSAoSm90IDE5OTEpOiB0aGUKICAgICAgICAgKiBnYWluIHRoYXQgeWllbGRzIOKIkjYwIGRCIGFmdGVyIFQ2MCBzZWNvbmRzIGZvciBhIHNpZ25hbCBjaXJjdWxhdGluZyBsaW5lIGkuCiAgICAgICAgICovCiAgICAgICAgbGluZUdhaW4obGluZUxlbmd0aCwgZGVjYXlUaW1lKSB7CiAgICAgICAgICAgIGNvbnN0IHQ2MCA9IE1hdGgubWF4KE1JTl9ERUNBWV9USU1FLCBkZWNheVRpbWUpOwogICAgICAgICAgICByZXR1cm4gMTAgKiogKCgtMyAqIGxpbmVMZW5ndGgpIC8gKHQ2MCAqIHRoaXMuc2FtcGxlUmF0ZSkpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2sgb2YgbW9ubyBzYW1wbGVzLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtdXN0IGJlIHRoZSBzYW1lCiAgICAgICAgICogbGVuZ3RoOyBvdXRwdXQgbWF5IGFsaWFzIGlucHV0LiBTdGF0ZWZ1bCBhY3Jvc3MgY2FsbHMgKHJpbmcgYnVmZmVycyBhbmQKICAgICAgICAgKiBhYnNvcnB0aW9uIG1lbW9yeSBwZXJzaXN0KSwgc28gc3RyZWFtaW5nIGNhbGxlcnMgcmV1c2Ugb25lIGluc3RhbmNlLgogICAgICAgICAqCiAgICAgICAgICogUGVyIHNhbXBsZSAoRmFnZXJzdHLDtm0gMjAyMCBlcXMuIDEtMjsgU2NobGVjaHQgMjAyMCDCp0lJLCBlcS4gMTQpOgogICAgICAgICAqICAgMS4gcmVhZCBwcmUtZGVsYXllZCBkcnkgc2FtcGxlIOKGkiBzCiAgICAgICAgICogICAyLiB0YWlsX2kgID0gYWJzb3JiKHJlYWRfaSwgZ19pLCBkYW1wKSAgICAgICAgICAgKEpvdCBkZWNheSArIEhGIGRhbXBpbmcpCiAgICAgICAgICogICAzLiB3ZXQgICAgID0gKDEv4oiaTikgzqNfaSB0YWlsX2kgICAgICAgICAgICAgICAgICAgIChvdXRwdXQgZ2FpbiBjKQogICAgICAgICAqICAgNC4gc2NhdF9pICA9IHpeey3Oul9pfcK3dGFpbF9pICAgICAgICAgICAgICAgICAgICAgICAoREZNIGRlbGF5cyBEX866LCBTY2hsZWNodCkKICAgICAgICAgKiAgIDUuIGZiICAgICAgPSBIIMK3IHNjYXQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobG9zc2xlc3MgSGFkYW1hcmQgbWl4IFUpCiAgICAgICAgICogICA2LiBpbmpfaSAgID0gYiDCtyBWTlNfaShzKSAgICAgICAgICAgICAgICAgICAgICAgICAgKHBlci1saW5lIHZlbHZldCwgRmFnZXJzdHLDtm0pCiAgICAgICAgICogICA3LiB3cml0ZSBsaW5lIGkg4oaQIGZiX2kgKyBpbmpfaSAgICAgICAgICAgICAgICAgICAgIChpbnB1dCBpbmplY3Rpb24pCiAgICAgICAgICogICA4LiBvdXQgICAgID0gKDHiiJJtaXgpwrdkcnkgKyBtaXjCt3dldAogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IGxlbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGNvbnN0IGRhbXAgPSBjbGFtcDAxKHBhcmFtcy5kYW1waW5nKTsKICAgICAgICAgICAgY29uc3QgZGlmZnVzaW9uID0gY2xhbXAwMShwYXJhbXMuZGlmZnVzaW9uKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gY2xhbXAwMShwYXJhbXMubWl4KTsKICAgICAgICAgICAgY29uc3QgZGVjYXlUaW1lID0gcGFyYW1zLmRlY2F5VGltZTsKICAgICAgICAgICAgY29uc3QgcHJlRGVsYXlTYW1wbGVzID0gTWF0aC5taW4odGhpcy5wcmVEZWxheUJ1ZmZlci5sZW5ndGggLSAxLCBNYXRoLm1heCgwLCBNYXRoLnJvdW5kKHBhcmFtcy5wcmVEZWxheSAqIHRoaXMuc2FtcGxlUmF0ZSkpKTsKICAgICAgICAgICAgLy8gUHJlY29tcHV0ZSBwZXItbGluZSBkZWNheSBnYWlucyBvbmNlIHBlciBibG9jayAoay1yYXRlIGRlY2F5KS4KICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm47IGkrKykgewogICAgICAgICAgICAgICAgdGhpcy5saW5lR2FpbnNbaV0gPSB0aGlzLmxpbmVHYWluKHRoaXMubGluZXNbaV0ubGVuZ3RoLCBkZWNheVRpbWUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IGxpbmVHYWlucyA9IHRoaXMubGluZUdhaW5zOwogICAgICAgICAgICBjb25zdCBvdXRHYWluID0gMSAvIE1hdGguc3FydCh0aGlzLm4pOyAvLyBvdXRwdXQgZ2FpbiBjIChlbmVyZ3ktbm9ybWFsaXplZCBzdW0pCiAgICAgICAgICAgIGNvbnN0IGluR2FpbiA9IDEgLyBNYXRoLnNxcnQodGhpcy5uKTsgLy8gaW5wdXQgZ2FpbiBiIChzcHJlYWQgZXF1YWxseSkKICAgICAgICAgICAgZm9yIChsZXQgcyA9IDA7IHMgPCBsZW47IHMrKykgewogICAgICAgICAgICAgICAgY29uc3QgZHJ5ID0gaW5wdXRbc107CiAgICAgICAgICAgICAgICAvLyBQcmUtZGVsYXkgKFNjaGxlY2h0IDIwMjAgwqdJSSBpbnB1dCBwYXRoKS4gV3JpdGUgdGhlIGN1cnJlbnQgc2FtcGxlLAogICAgICAgICAgICAgICAgLy8gdGhlbiByZWFkIGBwcmVEZWxheVNhbXBsZXNgIGJhY2sgZnJvbSBpdDsgcHJlRGVsYXk9MCByZXR1cm5zIHRoZQogICAgICAgICAgICAgICAgLy8ganVzdC13cml0dGVuIHNhbXBsZSAobm8gc3B1cmlvdXMgb25lLWJ1ZmZlci1sZW5ndGggbGF0ZW5jeSkuCiAgICAgICAgICAgICAgICBjb25zdCBwcmVMZW4gPSB0aGlzLnByZURlbGF5QnVmZmVyLmxlbmd0aDsKICAgICAgICAgICAgICAgIHRoaXMucHJlRGVsYXlCdWZmZXJbdGhpcy5wcmVEZWxheVdyaXRlXSA9IGRyeTsKICAgICAgICAgICAgICAgIGNvbnN0IHJlYWRJZHggPSAodGhpcy5wcmVEZWxheVdyaXRlIC0gcHJlRGVsYXlTYW1wbGVzICsgcHJlTGVuKSAlIHByZUxlbjsKICAgICAgICAgICAgICAgIGNvbnN0IHByZURlbGF5ZWQgPSB0aGlzLnByZURlbGF5QnVmZmVyW3JlYWRJZHhdOwogICAgICAgICAgICAgICAgdGhpcy5wcmVEZWxheVdyaXRlID0gKHRoaXMucHJlRGVsYXlXcml0ZSArIDEpICUgcHJlTGVuOwogICAgICAgICAgICAgICAgLy8gUmVhZCArIGFic29yYiBlYWNoIGRlbGF5LWxpbmUgdGFpbCAoSm90IDE5OTEgZGVjYXkvZGFtcGluZykuCiAgICAgICAgICAgICAgICBsZXQgd2V0ID0gMDsKICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uOyBpKyspIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCB0ID0gdGhpcy5saW5lc1tpXS5hYnNvcmIodGhpcy5saW5lc1tpXS5yZWFkKCksIGxpbmVHYWluc1tpXSwgZGFtcCk7CiAgICAgICAgICAgICAgICAgICAgdGhpcy50YWlsW2ldID0gdDsKICAgICAgICAgICAgICAgICAgICB3ZXQgKz0gdDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHdldCAqPSBvdXRHYWluOwogICAgICAgICAgICAgICAgLy8gREZNIHNjYXR0ZXJpbmc6IGFwcGx5IHRoZSBwZXItcGF0aCBwdXJlIGRlbGF5cyB6XnstzrpfaX0gQkVGT1JFIHRoZQogICAgICAgICAgICAgICAgLy8gbWl4IChTY2hsZWNodCAyMDIwIGVxLiAxNCwgRF/Ouih6KSkuIEVhY2ggZmVlZGJhY2sgcGF0aCBpcyBkZWxheWVkIGJ5IGEKICAgICAgICAgICAgICAgIC8vIGRpZmZlcmVudCDOul9pLCBzcHJlYWRpbmcgdGhlIGVjaG8gZWFjaCBsaW5lIHByb2R1Y2VzIGFjcm9zcyB0aGUgbGluZXMuCiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubjsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyZWRbaV0gPSB0aGlzLmZiRGVsYXlzW2ldLnByb2Nlc3ModGhpcy50YWlsW2ldKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIC8vIExvc3NsZXNzIEhhZGFtYXJkIG1peCBmYiA9IEjCt3NjYXQgKFNjaGxlY2h0IDIwMjAgZXEuIDE0LCB0aGUgdW5pdGFyeSBVKS4KICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uOyBpKyspIHsKICAgICAgICAgICAgICAgICAgICBsZXQgYWNjID0gMDsKICAgICAgICAgICAgICAgICAgICBjb25zdCByb3cgPSBpICogdGhpcy5uOwogICAgICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5uOyBqKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgYWNjICs9IHRoaXMubWF0cml4W3JvdyArIGpdICogdGhpcy5zY2F0dGVyZWRbal07CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHRoaXMuZmVlZGJhY2tbaV0gPSBhY2M7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAvLyBQZXItbGluZSB2ZWx2ZXQgZGlmZnVzaW9uIG9mIHRoZSBpbmplY3RlZCBpbnB1dCAoRmFnZXJzdHLDtm0gMjAyMCBWRkROCiAgICAgICAgICAgICAgICAvLyBzaW5nbGUsIGVxLiA4KS4gRWFjaCBsaW5lIGdldHMgaXRzIE9XTiBWTlMtZmlsdGVyZWQgY29weSBvZiB0aGUgaW5wdXQKICAgICAgICAgICAgICAgIC8vIChtdWx0aXBsaWNhdGlvbi1mcmVlKSwgYmxlbmRlZCBkcnnihpRkaWZmdXNlZCBieSBgZGlmZnVzaW9uYCwgdGhlbiBhZGRlZAogICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBsaW5lJ3MgZmVlZGJhY2suIERpc3RpbmN0IHBlci1saW5lIGZpbHRlcnMgZGVjb3JyZWxhdGUgdGhlCiAgICAgICAgICAgICAgICAvLyBsaW5lcyBhbmQgcmFpc2UgZWFybHkgZWNobyBkZW5zaXR5IChpbnB1dC1zZXQgcGxhY2VtZW50OyBub3QgTcKyIGxvb3AgY29tcG91bmRpbmcpLgogICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm47IGkrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGRpZmZ1c2VkID0gdGhpcy52ZWx2ZXRzW2ldLnByb2Nlc3MocHJlRGVsYXllZCk7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5qZWN0ZWQgPSAoKDEgLSBkaWZmdXNpb24pICogcHJlRGVsYXllZCArIGRpZmZ1c2lvbiAqIGRpZmZ1c2VkKSAqIGluR2FpbjsKICAgICAgICAgICAgICAgICAgICB0aGlzLmxpbmVzW2ldLndyaXRlKHRoaXMuZmVlZGJhY2tbaV0gKyBpbmplY3RlZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAvLyBXZXQvZHJ5IG1peC4KICAgICAgICAgICAgICAgIG91dHB1dFtzXSA9ICgxIC0gbWl4KSAqIGRyeSArIG1peCAqIHdldDsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAvKiogQ2xlYXIgYWxsIGludGVybmFsIHN0YXRlIChkZWxheSBsaW5lcywgcHJlLWRlbGF5LCBERk0gZGVsYXlzLCBkaWZmdXNlcnMpLiAqLwogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICBmb3IgKGNvbnN0IGxpbmUgb2YgdGhpcy5saW5lcykKICAgICAgICAgICAgICAgIGxpbmUucmVzZXQoKTsKICAgICAgICAgICAgZm9yIChjb25zdCBkIG9mIHRoaXMuZmJEZWxheXMpCiAgICAgICAgICAgICAgICBkLnJlc2V0KCk7CiAgICAgICAgICAgIGZvciAoY29uc3QgdiBvZiB0aGlzLnZlbHZldHMpCiAgICAgICAgICAgICAgICB2LnJlc2V0KCk7CiAgICAgICAgICAgIHRoaXMucHJlRGVsYXlCdWZmZXIuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy5wcmVEZWxheVdyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKiogQ2xhbXAgeCB0byBbMCwxXS4gKi8KICAgIGZ1bmN0aW9uIGNsYW1wMDEoeCkgewogICAgICAgIGlmICh4IDwgMCkKICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgaWYgKHggPiAxKQogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICByZXR1cm4geDsKICAgIH0KCiAgICAvKgogICAgICogRkROIHJldmVyYiBBdWRpb1dvcmtsZXQgc2hlbGwg4oCUIGEgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMKICAgICAqIEFMTCBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIEZkblJldmVyYlByb2Nlc3NvciBpbiBmZG4tcmV2ZXJiLWNvcmUudHMuCiAgICAgKiBNaXJyb3JzIHRoZSBkYXR0b3Jyby1yZXZlcmIudHMgLyBkeW5hbWljcy50cyBjb3JlL3NoZWxsIHNwbGl0OiB0aGlzIGZpbGUgb3ducwogICAgICogb25seSB0aGUgd29ya2xldCBwbHVtYmluZyAocGFyYW1ldGVyRGVzY3JpcHRvcnMsIHByb2Nlc3MoKSwgdGhlCiAgICAgKiByZWdpc3RlclByb2Nlc3NvciBjYWxsKTsgdGhlIGFsZ29yaXRobSBsaXZlcyBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KICAgICAqCiAgICAgKiBBbGdvcml0aG06IGEgRmVlZGJhY2sgRGVsYXkgTmV0d29yayByZXZlcmJlcmF0b3Igd2l0aCBhIGxvc3NsZXNzIERlbGF5CiAgICAgKiBGZWVkYmFjayBNYXRyaXggQSh6KT1IwrdEX866KHopIOKAlCBhIEhhZGFtYXJkIG1hdHJpeCBzY2F0dGVyZWQgYnkgcGVyLWZlZWRiYWNrLQogICAgICogcGF0aCBwdXJlIGRlbGF5cyAoU2NobGVjaHQgJiBIYWJldHMgMjAyMCwgIlNjYXR0ZXJpbmcgaW4gRmVlZGJhY2sgRGVsYXkKICAgICAqIE5ldHdvcmtzIiwgwqdJVi1BIGVxLiAxNCkg4oCUIHBlci1kZWxheS1saW5lIGFic29ycHRpb24gZmlsdGVycyBzZXR0aW5nIFQ2MCAoSm90CiAgICAgKiAmIENoYWlnbmUgMTk5MSksIGFuZCBhIG11bHRpcGxpY2F0aW9uLWZyZWUgdmVsdmV0LW5vaXNlIGRpZmZ1c2VyIG9uIEVBQ0ggZGVsYXkKICAgICAqIGxpbmUsIHRoZSBWRkROICJzaW5nbGUiIGNvbmZpZ3VyYXRpb24gKEZhZ2Vyc3Ryw7ZtLCBBbGFyeSwgU2NobGVjaHQgJiBWw6RsaW3DpGtpCiAgICAgKiAyMDIwLCAiVmVsdmV0LU5vaXNlIEZlZWRiYWNrIERlbGF5IE5ldHdvcmsiLCDCpzMpLiBTZWUgZmRuLXJldmVyYi1jb3JlLnRzIGZvcgogICAgICogdGhlIGZ1bGwgY2l0YXRpb24gYW5kIHBlci1lcXVhdGlvbiBjb21tZW50cy4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpmZG4tcmV2ZXJiXSI7CiAgICBjbGFzcyBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBkZWxheS1saW5lIGFuZAogICAgICAgIC8vIGFic29ycHRpb24tZmlsdGVyIHN0YXRlIGFjcm9zcyBwcm9jZXNzKCkgYmxvY2tzLgogICAgICAgIGNvcmVzID0gW107CiAgICAgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKICAgICAgICAgICAgLy8gRGVmYXVsdHM6IGEgbW9kZXJhdGUsIG5hdHVyYWwgcm9vbS4gZGVjYXlUaW1lIGluIHNlY29uZHMgKFQ2MCk7IHByZURlbGF5CiAgICAgICAgICAgIC8vIGluIHNlY29uZHM7IGRhbXBpbmcvZGlmZnVzaW9uL21peCBub3JtYWxpemVkIHRvIFswLDFdLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJkZWNheVRpbWUiLCAxLjUsIDAuMDAxLCAyMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJwcmVEZWxheSIsIDAsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGFtcGluZyIsIDAuMywgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkaWZmdXNpb24iLCAwLjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWl4IiwgMC4zLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgIF0ubWFwKChbbmFtZSwgZGVmYXVsdFZhbHVlLCBtaW5WYWx1ZSwgbWF4VmFsdWUsIGF1dG9tYXRpb25SYXRlXSkgPT4gKHsKICAgICAgICAgICAgICAgIG5hbWU6IG5hbWUsCiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU6IGRlZmF1bHRWYWx1ZSwKICAgICAgICAgICAgICAgIG1pblZhbHVlOiBtaW5WYWx1ZSwKICAgICAgICAgICAgICAgIG1heFZhbHVlOiBtYXhWYWx1ZSwKICAgICAgICAgICAgICAgIGF1dG9tYXRpb25SYXRlOiBhdXRvbWF0aW9uUmF0ZSwKICAgICAgICAgICAgfSkpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgaWYgKCFpbnB1dCB8fCBpbnB1dC5sZW5ndGggPT09IDAgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoID09PSAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBrLXJhdGUgcGFyYW1zOiB0YWtlIHRoZSBmaXJzdCAoY29uc3RhbnQtb3Zlci1ibG9jaykgdmFsdWUuCiAgICAgICAgICAgIGNvbnN0IHBhcmFtcyA9IHsKICAgICAgICAgICAgICAgIGRlY2F5VGltZTogcGFyYW1ldGVycy5kZWNheVRpbWVbMF0sCiAgICAgICAgICAgICAgICBwcmVEZWxheTogcGFyYW1ldGVycy5wcmVEZWxheVswXSwKICAgICAgICAgICAgICAgIGRhbXBpbmc6IHBhcmFtZXRlcnMuZGFtcGluZ1swXSwKICAgICAgICAgICAgICAgIGRpZmZ1c2lvbjogcGFyYW1ldGVycy5kaWZmdXNpb25bMF0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0gPSBuZXcgRmRuUmV2ZXJiUHJvY2Vzc29yKHNhbXBsZVJhdGUpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoImZkbi1yZXZlcmIiLCBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yKTsKICAgICAgICBjb25zb2xlLmluZm8oYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBjb21wbGV0ZWApOwogICAgfQogICAgY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgY29uc29sZS5lcnJvcihgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGZhaWxlZGAsIGVycm9yKTsKICAgICAgICB0aHJvdyBlcnJvcjsKICAgIH0KCiAgICBleHBvcnRzLkZkblJldmVyYldvcmtsZXRQcm9jZXNzb3IgPSBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1mZG4tcmV2ZXJiLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGZyZXF1ZW5jeVNoaWZ0ZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIEZJUi1IaWxiZXJ0IHNpbmdsZS1zaWRlYmFuZCBmcmVxdWVuY3kgc2hpZnRlci4KICAgICAqCiAgICAgKiBTY290dCBXYXJkbGUsICJBIEhpbGJlcnQtVHJhbnNmb3JtZXIgRnJlcXVlbmN5IFNoaWZ0ZXIgZm9yIEF1ZGlvIiwKICAgICAqIERBRngtOTgsIGVxLiA1YzoKICAgICAqCiAgICAgKiAgIHkodCkgPSB4KHQpIGNvcyh3X2MgdCkgLSBIe3godCl9IHNpbih3X2MgdCkKICAgICAqCiAgICAgKiBUaGUgcmVhbCBwYXRoIGlzIGRlbGF5ZWQgYnkgdGhlIEZJUiBncm91cCBkZWxheSBzbyBpdCByZW1haW5zIGluIHF1YWRyYXR1cmUKICAgICAqIHdpdGggdGhlIEhpbGJlcnQgcGF0aC4gQSBzaWduZWQgY2FycmllciBmcmVxdWVuY3kgY2hvb3NlcyB1cHBlci9sb3dlcgogICAgICogc2lkZWJhbmQgdHJhbnNsYXRpb24gd2l0aG91dCBjaGFuZ2luZyB0aGUgYWxnb3JpdGhtLgogICAgICovCiAgICBjb25zdCBERUZBVUxUX1RBUFMgPSAxMjc7CiAgICAvKiogV2luZG93ZWQgaWRlYWwgb2RkLXN5bW1ldHJpYyBIaWxiZXJ0LXRyYW5zZm9ybSBGSVIuICovCiAgICBmdW5jdGlvbiBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgaWYgKHRhcENvdW50IDwgNyB8fCB0YXBDb3VudCAlIDIgPT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIkhpbGJlcnQgRklSIHRhcCBjb3VudCBtdXN0IGJlIGFuIG9kZCBpbnRlZ2VyID49IDciKTsKICAgICAgICB9CiAgICAgICAgY29uc3QgY2VudGVyID0gKHRhcENvdW50IC0gMSkgLyAyOwogICAgICAgIGNvbnN0IGNvZWZmaWNpZW50cyA9IG5ldyBGbG9hdDY0QXJyYXkodGFwQ291bnQpOwogICAgICAgIGZvciAobGV0IG4gPSAwOyBuIDwgdGFwQ291bnQ7IG4rKykgewogICAgICAgICAgICBjb25zdCBtID0gbiAtIGNlbnRlcjsKICAgICAgICAgICAgaWYgKG0gIT09IDAgJiYgTWF0aC5hYnMobSkgJSAyID09PSAxKSB7CiAgICAgICAgICAgICAgICBjb25zdCB3aW5kb3cgPSAwLjQyIC0gMC41ICogTWF0aC5jb3MoKDIgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSkgKyAwLjA4ICogTWF0aC5jb3MoKDQgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSk7CiAgICAgICAgICAgICAgICBjb2VmZmljaWVudHNbbl0gPSAoMiAvIChNYXRoLlBJICogbSkpICogd2luZG93OwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiBjb2VmZmljaWVudHM7CiAgICB9CiAgICAvKiogU3RyZWFtaW5nIEZJUiBIaWxiZXJ0IHRyYW5zZm9ybWVyIHdpdGggYW4gYWxpZ25lZCBkaXJlY3QgdGFwLiAqLwogICAgY2xhc3MgSGlsYmVydFRyYW5zZm9ybWVyIHsKICAgICAgICBsYXRlbmN5U2FtcGxlczsKICAgICAgICBjb2VmZmljaWVudHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodGFwQ291bnQgPSBERUZBVUxUX1RBUFMpIHsKICAgICAgICAgICAgdGhpcy5jb2VmZmljaWVudHMgPSBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5U2FtcGxlcyA9ICh0YXBDb3VudCAtIDEpIC8gMjsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0YXBDb3VudCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQ2NEFycmF5KHNpemUpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBzaXplIC0gMTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlclt0aGlzLndyaXRlSW5kZXhdID0gaW5wdXQ7CiAgICAgICAgICAgIGxldCBxdWFkcmF0dXJlID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNvZWZmaWNpZW50cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgcXVhZHJhdHVyZSArPSB0aGlzLmNvZWZmaWNpZW50c1tpXSAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSBpKSAmIHRoaXMubWFza107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGlyZWN0ID0gdGhpcy5idWZmZXJbKHRoaXMud3JpdGVJbmRleCAtIHRoaXMubGF0ZW5jeVNhbXBsZXMpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4geyBkaXJlY3QsIHF1YWRyYXR1cmUgfTsKICAgICAgICB9CiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgfQogICAgfQogICAgLyoqIE9uZS1jaGFubmVsIHN0cmVhbWluZyBTU0Igc2hpZnRlci4gQ3JlYXRlIG9uZSBpbnN0YW5jZSBwZXIgYXVkaW8gY2hhbm5lbC4gKi8KICAgIGNsYXNzIEZyZXF1ZW5jeVNoaWZ0ZXJDb3JlIHsKICAgICAgICBzYW1wbGVSYXRlOwogICAgICAgIGhpbGJlcnQ7CiAgICAgICAgcGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMuaGlsYmVydCA9IG5ldyBIaWxiZXJ0VHJhbnNmb3JtZXIodGFwQ291bnQpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzU2FtcGxlKGlucHV0LCBwYXJhbXMpIHsKICAgICAgICAgICAgY29uc3QgcGFpciA9IHRoaXMuaGlsYmVydC5wcm9jZXNzKGlucHV0KTsKICAgICAgICAgICAgY29uc3Qgc2hpZnRlZCA9IHBhaXIuZGlyZWN0ICogTWF0aC5jb3ModGhpcy5waGFzZSkgLSBwYWlyLnF1YWRyYXR1cmUgKiBNYXRoLnNpbih0aGlzLnBoYXNlKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgcGFyYW1zLm1peCkpOwogICAgICAgICAgICBjb25zdCBmcmVxdWVuY3kgPSBNYXRoLm1heCgtdGhpcy5zYW1wbGVSYXRlIC8gMiwgTWF0aC5taW4odGhpcy5zYW1wbGVSYXRlIC8gMiwgcGFyYW1zLmZyZXF1ZW5jeSkpOwogICAgICAgICAgICB0aGlzLnBoYXNlICs9ICgyICogTWF0aC5QSSAqIGZyZXF1ZW5jeSkgLyB0aGlzLnNhbXBsZVJhdGU7CiAgICAgICAgICAgIGlmICh0aGlzLnBoYXNlID4gTWF0aC5QSSB8fCB0aGlzLnBoYXNlIDwgLU1hdGguUEkpCiAgICAgICAgICAgICAgICB0aGlzLnBoYXNlIC09IE1hdGgucm91bmQodGhpcy5waGFzZSAvICgyICogTWF0aC5QSSkpICogMiAqIE1hdGguUEk7CiAgICAgICAgICAgIHJldHVybiBwYWlyLmRpcmVjdCAqICgxIC0gbWl4KSArIHNoaWZ0ZWQgKiBtaXg7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5wdXQubGVuZ3RoOyBpKyspCiAgICAgICAgICAgICAgICBvdXRwdXRbaV0gPSB0aGlzLnByb2Nlc3NTYW1wbGUoaW5wdXRbaV0sIHBhcmFtcyk7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmhpbGJlcnQucmVzZXQoKTsKICAgICAgICAgICAgdGhpcy5waGFzZSA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIEZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJmcmVxdWVuY3kiLCBkZWZhdWx0VmFsdWU6IDEwMCwgbWluVmFsdWU6IC0yNGUzLCBtYXhWYWx1ZTogMjQwMDAsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICAgICAgeyBuYW1lOiAibWl4IiwgZGVmYXVsdFZhbHVlOiAxLCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDEsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICBdOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3QgZnJlcXVlbmN5ID0gcGFyYW1ldGVycy5mcmVxdWVuY3lbcGFyYW1ldGVycy5mcmVxdWVuY3kubGVuZ3RoIC0gMV07CiAgICAgICAgICAgIGNvbnN0IG1peCA9IHBhcmFtZXRlcnMubWl4W3BhcmFtZXRlcnMubWl4Lmxlbmd0aCAtIDFdOwogICAgICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dC5sZW5ndGg7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgY29uc3Qgc291cmNlID0gaW5wdXRbY2hhbm5lbF0gPz8gaW5wdXRbMF07CiAgICAgICAgICAgICAgICBpZiAoIXNvdXJjZSkgewogICAgICAgICAgICAgICAgICAgIG91dHB1dFtjaGFubmVsXS5maWxsKDApOwogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgY29yZSA9ICh0aGlzLmNvcmVzW2NoYW5uZWxdID8/PSBuZXcgRnJlcXVlbmN5U2hpZnRlckNvcmUoc2FtcGxlUmF0ZSkpOwogICAgICAgICAgICAgICAgY29yZS5wcm9jZXNzKHNvdXJjZSwgb3V0cHV0W2NoYW5uZWxdLCB7IGZyZXF1ZW5jeSwgbWl4IH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJmcmVxdWVuY3ktc2hpZnRlciIsIEZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yKTsKCiAgICBleHBvcnRzLkZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yID0gRnJlcXVlbmN5U2hpZnRlcldvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWZyZXF1ZW5jeS1zaGlmdGVyLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGhhcm1vbml6ZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKCSd1c2Ugc3RyaWN0JzsKCglmdW5jdGlvbiBnZXREZWZhdWx0RXhwb3J0RnJvbUNqcyAoeCkgewoJCXJldHVybiB4ICYmIHguX19lc01vZHVsZSAmJiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ2RlZmF1bHQnKSA/IHhbJ2RlZmF1bHQnXSA6IHg7Cgl9CgoJdmFyIGZmdDsKCXZhciBoYXNSZXF1aXJlZEZmdDsKCglmdW5jdGlvbiByZXF1aXJlRmZ0ICgpIHsKCQlpZiAoaGFzUmVxdWlyZWRGZnQpIHJldHVybiBmZnQ7CgkJaGFzUmVxdWlyZWRGZnQgPSAxOwoKCQlmdW5jdGlvbiBGRlQoc2l6ZSkgewoJCSAgdGhpcy5zaXplID0gc2l6ZSB8IDA7CgkJICBpZiAodGhpcy5zaXplIDw9IDEgfHwgKHRoaXMuc2l6ZSAmICh0aGlzLnNpemUgLSAxKSkgIT09IDApCgkJICAgIHRocm93IG5ldyBFcnJvcignRkZUIHNpemUgbXVzdCBiZSBhIHBvd2VyIG9mIHR3byBhbmQgYmlnZ2VyIHRoYW4gMScpOwoKCQkgIHRoaXMuX2NzaXplID0gc2l6ZSA8PCAxOwoKCQkgIC8vIE5PVEU6IFVzZSBvZiBgdmFyYCBpcyBpbnRlbnRpb25hbCBmb3Igb2xkIFY4IHZlcnNpb25zCgkJICB2YXIgdGFibGUgPSBuZXcgQXJyYXkodGhpcy5zaXplICogMik7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHRhYmxlLmxlbmd0aDsgaSArPSAyKSB7CgkJICAgIGNvbnN0IGFuZ2xlID0gTWF0aC5QSSAqIGkgLyB0aGlzLnNpemU7CgkJICAgIHRhYmxlW2ldID0gTWF0aC5jb3MoYW5nbGUpOwoJCSAgICB0YWJsZVtpICsgMV0gPSAtTWF0aC5zaW4oYW5nbGUpOwoJCSAgfQoJCSAgdGhpcy50YWJsZSA9IHRhYmxlOwoKCQkgIC8vIEZpbmQgc2l6ZSdzIHBvd2VyIG9mIHR3bwoJCSAgdmFyIHBvd2VyID0gMDsKCQkgIGZvciAodmFyIHQgPSAxOyB0aGlzLnNpemUgPiB0OyB0IDw8PSAxKQoJCSAgICBwb3dlcisrOwoKCQkgIC8vIENhbGN1bGF0ZSBpbml0aWFsIHN0ZXAncyB3aWR0aDoKCQkgIC8vICAgKiBJZiB3ZSBhcmUgZnVsbCByYWRpeC00IC0gaXQgaXMgMnggc21hbGxlciB0byBnaXZlIGluaXRhbCBsZW49OAoJCSAgLy8gICAqIE90aGVyd2lzZSBpdCBpcyB0aGUgc2FtZSBhcyBgcG93ZXJgIHRvIGdpdmUgbGVuPTQKCQkgIHRoaXMuX3dpZHRoID0gcG93ZXIgJSAyID09PSAwID8gcG93ZXIgLSAxIDogcG93ZXI7CgoJCSAgLy8gUHJlLWNvbXB1dGUgYml0LXJldmVyc2FsIHBhdHRlcm5zCgkJICB0aGlzLl9iaXRyZXYgPSBuZXcgQXJyYXkoMSA8PCB0aGlzLl93aWR0aCk7CgkJICBmb3IgKHZhciBqID0gMDsgaiA8IHRoaXMuX2JpdHJldi5sZW5ndGg7IGorKykgewoJCSAgICB0aGlzLl9iaXRyZXZbal0gPSAwOwoJCSAgICBmb3IgKHZhciBzaGlmdCA9IDA7IHNoaWZ0IDwgdGhpcy5fd2lkdGg7IHNoaWZ0ICs9IDIpIHsKCQkgICAgICB2YXIgcmV2U2hpZnQgPSB0aGlzLl93aWR0aCAtIHNoaWZ0IC0gMjsKCQkgICAgICB0aGlzLl9iaXRyZXZbal0gfD0gKChqID4+PiBzaGlmdCkgJiAzKSA8PCByZXZTaGlmdDsKCQkgICAgfQoJCSAgfQoKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQkgIHRoaXMuX2ludiA9IDA7CgkJfQoJCWZmdCA9IEZGVDsKCgkJRkZULnByb3RvdHlwZS5mcm9tQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gZnJvbUNvbXBsZXhBcnJheShjb21wbGV4LCBzdG9yYWdlKSB7CgkJICB2YXIgcmVzID0gc3RvcmFnZSB8fCBuZXcgQXJyYXkoY29tcGxleC5sZW5ndGggPj4+IDEpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb21wbGV4Lmxlbmd0aDsgaSArPSAyKQoJCSAgICByZXNbaSA+Pj4gMV0gPSBjb21wbGV4W2ldOwoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmNyZWF0ZUNvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIGNyZWF0ZUNvbXBsZXhBcnJheSgpIHsKCQkgIGNvbnN0IHJlcyA9IG5ldyBBcnJheSh0aGlzLl9jc2l6ZSk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHJlcy5sZW5ndGg7IGkrKykKCQkgICAgcmVzW2ldID0gMDsKCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS50b0NvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIHRvQ29tcGxleEFycmF5KGlucHV0LCBzdG9yYWdlKSB7CgkJICB2YXIgcmVzID0gc3RvcmFnZSB8fCB0aGlzLmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXMubGVuZ3RoOyBpICs9IDIpIHsKCQkgICAgcmVzW2ldID0gaW5wdXRbaSA+Pj4gMV07CgkJICAgIHJlc1tpICsgMV0gPSAwOwoJCSAgfQoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmNvbXBsZXRlU3BlY3RydW0gPSBmdW5jdGlvbiBjb21wbGV0ZVNwZWN0cnVtKHNwZWN0cnVtKSB7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoJCSAgdmFyIGhhbGYgPSBzaXplID4+PiAxOwoJCSAgZm9yICh2YXIgaSA9IDI7IGkgPCBoYWxmOyBpICs9IDIpIHsKCQkgICAgc3BlY3RydW1bc2l6ZSAtIGldID0gc3BlY3RydW1baV07CgkJICAgIHNwZWN0cnVtW3NpemUgLSBpICsgMV0gPSAtc3BlY3RydW1baSArIDFdOwoJCSAgfQoJCX07CgoJCUZGVC5wcm90b3R5cGUudHJhbnNmb3JtID0gZnVuY3Rpb24gdHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDA7CgkJICB0aGlzLl90cmFuc2Zvcm00KCk7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5yZWFsVHJhbnNmb3JtID0gZnVuY3Rpb24gcmVhbFRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAwOwoJCSAgdGhpcy5fcmVhbFRyYW5zZm9ybTQoKTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmludmVyc2VUcmFuc2Zvcm0gPSBmdW5jdGlvbiBpbnZlcnNlVHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDE7CgkJICB0aGlzLl90cmFuc2Zvcm00KCk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IG91dC5sZW5ndGg7IGkrKykKCQkgICAgb3V0W2ldIC89IHRoaXMuc2l6ZTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQkvLyByYWRpeC00IGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBVc2VzIG9mIGB2YXJgIGFyZSBpbnRlbnRpb25hbCBmb3Igb2xkZXIgVjggdmVyc2lvbiB0aGF0IGRvIG5vdAoJCS8vIHN1cHBvcnQgYm90aCBgbGV0IGNvbXBvdW5kIGFzc2lnbm1lbnRzYCBhbmQgYGNvbnN0IHBoaWAKCQlGRlQucHJvdG90eXBlLl90cmFuc2Zvcm00ID0gZnVuY3Rpb24gX3RyYW5zZm9ybTQoKSB7CgkJICB2YXIgb3V0ID0gdGhpcy5fb3V0OwoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCgkJICAvLyBJbml0aWFsIHN0ZXAgKHBlcm11dGUgYW5kIHRyYW5zZm9ybSkKCQkgIHZhciB3aWR0aCA9IHRoaXMuX3dpZHRoOwoJCSAgdmFyIHN0ZXAgPSAxIDw8IHdpZHRoOwoJCSAgdmFyIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCgkJICB2YXIgb3V0T2ZmOwoJCSAgdmFyIHQ7CgkJICB2YXIgYml0cmV2ID0gdGhpcy5fYml0cmV2OwoJCSAgaWYgKGxlbiA9PT0gNCkgewoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlVHJhbnNmb3JtMihvdXRPZmYsIG9mZiwgc3RlcCk7CgkJICAgIH0KCQkgIH0gZWxzZSB7CgkJICAgIC8vIGxlbiA9PT0gOAoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlVHJhbnNmb3JtNChvdXRPZmYsIG9mZiwgc3RlcCk7CgkJICAgIH0KCQkgIH0KCgkJICAvLyBMb29wIHRocm91Z2ggc3RlcHMgaW4gZGVjcmVhc2luZyBvcmRlcgoJCSAgdmFyIGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIHZhciB0YWJsZSA9IHRoaXMudGFibGU7CgkJICBmb3IgKHN0ZXAgPj49IDI7IHN0ZXAgPj0gMjsgc3RlcCA+Pj0gMikgewoJCSAgICBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgkJICAgIHZhciBxdWFydGVyTGVuID0gbGVuID4+PiAyOwoKCQkgICAgLy8gTG9vcCB0aHJvdWdoIG9mZnNldHMgaW4gdGhlIGRhdGEKCQkgICAgZm9yIChvdXRPZmYgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuKSB7CgkJICAgICAgLy8gRnVsbCBjYXNlCgkJICAgICAgdmFyIGxpbWl0ID0gb3V0T2ZmICsgcXVhcnRlckxlbjsKCQkgICAgICBmb3IgKHZhciBpID0gb3V0T2ZmLCBrID0gMDsgaSA8IGxpbWl0OyBpICs9IDIsIGsgKz0gc3RlcCkgewoJCSAgICAgICAgY29uc3QgQSA9IGk7CgkJICAgICAgICBjb25zdCBCID0gQSArIHF1YXJ0ZXJMZW47CgkJICAgICAgICBjb25zdCBDID0gQiArIHF1YXJ0ZXJMZW47CgkJICAgICAgICBjb25zdCBEID0gQyArIHF1YXJ0ZXJMZW47CgoJCSAgICAgICAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBBciA9IG91dFtBXTsKCQkgICAgICAgIGNvbnN0IEFpID0gb3V0W0EgKyAxXTsKCQkgICAgICAgIGNvbnN0IEJyID0gb3V0W0JdOwoJCSAgICAgICAgY29uc3QgQmkgPSBvdXRbQiArIDFdOwoJCSAgICAgICAgY29uc3QgQ3IgPSBvdXRbQ107CgkJICAgICAgICBjb25zdCBDaSA9IG91dFtDICsgMV07CgkJICAgICAgICBjb25zdCBEciA9IG91dFtEXTsKCQkgICAgICAgIGNvbnN0IERpID0gb3V0W0QgKyAxXTsKCgkJICAgICAgICAvLyBNaWRkbGUgdmFsdWVzCgkJICAgICAgICBjb25zdCBNQXIgPSBBcjsKCQkgICAgICAgIGNvbnN0IE1BaSA9IEFpOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlQnIgPSB0YWJsZVtrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlQmkgPSBpbnYgKiB0YWJsZVtrICsgMV07CgkJICAgICAgICBjb25zdCBNQnIgPSBCciAqIHRhYmxlQnIgLSBCaSAqIHRhYmxlQmk7CgkJICAgICAgICBjb25zdCBNQmkgPSBCciAqIHRhYmxlQmkgKyBCaSAqIHRhYmxlQnI7CgoJCSAgICAgICAgY29uc3QgdGFibGVDciA9IHRhYmxlWzIgKiBrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlQ2kgPSBpbnYgKiB0YWJsZVsyICogayArIDFdOwoJCSAgICAgICAgY29uc3QgTUNyID0gQ3IgKiB0YWJsZUNyIC0gQ2kgKiB0YWJsZUNpOwoJCSAgICAgICAgY29uc3QgTUNpID0gQ3IgKiB0YWJsZUNpICsgQ2kgKiB0YWJsZUNyOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlRHIgPSB0YWJsZVszICoga107CgkJICAgICAgICBjb25zdCB0YWJsZURpID0gaW52ICogdGFibGVbMyAqIGsgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1EciA9IERyICogdGFibGVEciAtIERpICogdGFibGVEaTsKCQkgICAgICAgIGNvbnN0IE1EaSA9IERyICogdGFibGVEaSArIERpICogdGFibGVEcjsKCgkJICAgICAgICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBUMHIgPSBNQXIgKyBNQ3I7CgkJICAgICAgICBjb25zdCBUMGkgPSBNQWkgKyBNQ2k7CgkJICAgICAgICBjb25zdCBUMXIgPSBNQXIgLSBNQ3I7CgkJICAgICAgICBjb25zdCBUMWkgPSBNQWkgLSBNQ2k7CgkJICAgICAgICBjb25zdCBUMnIgPSBNQnIgKyBNRHI7CgkJICAgICAgICBjb25zdCBUMmkgPSBNQmkgKyBNRGk7CgkJICAgICAgICBjb25zdCBUM3IgPSBpbnYgKiAoTUJyIC0gTURyKTsKCQkgICAgICAgIGNvbnN0IFQzaSA9IGludiAqIChNQmkgLSBNRGkpOwoKCQkgICAgICAgIC8vIEZpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgRkFyID0gVDByICsgVDJyOwoJCSAgICAgICAgY29uc3QgRkFpID0gVDBpICsgVDJpOwoKCQkgICAgICAgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCQkgICAgICAgIGNvbnN0IEZDaSA9IFQwaSAtIFQyaTsKCgkJICAgICAgICBjb25zdCBGQnIgPSBUMXIgKyBUM2k7CgkJICAgICAgICBjb25zdCBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgICAgICAgY29uc3QgRkRyID0gVDFyIC0gVDNpOwoJCSAgICAgICAgY29uc3QgRkRpID0gVDFpICsgVDNyOwoKCQkgICAgICAgIG91dFtBXSA9IEZBcjsKCQkgICAgICAgIG91dFtBICsgMV0gPSBGQWk7CgkJICAgICAgICBvdXRbQl0gPSBGQnI7CgkJICAgICAgICBvdXRbQiArIDFdID0gRkJpOwoJCSAgICAgICAgb3V0W0NdID0gRkNyOwoJCSAgICAgICAgb3V0W0MgKyAxXSA9IEZDaTsKCQkgICAgICAgIG91dFtEXSA9IEZEcjsKCQkgICAgICAgIG91dFtEICsgMV0gPSBGRGk7CgkJICAgICAgfQoJCSAgICB9CgkJICB9CgkJfTsKCgkJLy8gcmFkaXgtMiBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj00CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlVHJhbnNmb3JtMiA9IGZ1bmN0aW9uIF9zaW5nbGVUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCgkJICBjb25zdCBldmVuUiA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IGV2ZW5JID0gZGF0YVtvZmYgKyAxXTsKCQkgIGNvbnN0IG9kZFIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3Qgb2RkSSA9IGRhdGFbb2ZmICsgc3RlcCArIDFdOwoKCQkgIGNvbnN0IGxlZnRSID0gZXZlblIgKyBvZGRSOwoJCSAgY29uc3QgbGVmdEkgPSBldmVuSSArIG9kZEk7CgkJICBjb25zdCByaWdodFIgPSBldmVuUiAtIG9kZFI7CgkJICBjb25zdCByaWdodEkgPSBldmVuSSAtIG9kZEk7CgoJCSAgb3V0W291dE9mZl0gPSBsZWZ0UjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IGxlZnRJOwoJCSAgb3V0W291dE9mZiArIDJdID0gcmlnaHRSOwoJCSAgb3V0W291dE9mZiArIDNdID0gcmlnaHRJOwoJCX07CgoJCS8vIHJhZGl4LTQKCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49OAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfc2luZ2xlVHJhbnNmb3JtNChvdXRPZmYsIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgkJICBjb25zdCBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICBjb25zdCBzdGVwMiA9IHN0ZXAgKiAyOwoJCSAgY29uc3Qgc3RlcDMgPSBzdGVwICogMzsKCgkJICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEFyID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgQWkgPSBkYXRhW29mZiArIDFdOwoJCSAgY29uc3QgQnIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3QgQmkgPSBkYXRhW29mZiArIHN0ZXAgKyAxXTsKCQkgIGNvbnN0IENyID0gZGF0YVtvZmYgKyBzdGVwMl07CgkJICBjb25zdCBDaSA9IGRhdGFbb2ZmICsgc3RlcDIgKyAxXTsKCQkgIGNvbnN0IERyID0gZGF0YVtvZmYgKyBzdGVwM107CgkJICBjb25zdCBEaSA9IGRhdGFbb2ZmICsgc3RlcDMgKyAxXTsKCgkJICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICBjb25zdCBUMHIgPSBBciArIENyOwoJCSAgY29uc3QgVDBpID0gQWkgKyBDaTsKCQkgIGNvbnN0IFQxciA9IEFyIC0gQ3I7CgkJICBjb25zdCBUMWkgPSBBaSAtIENpOwoJCSAgY29uc3QgVDJyID0gQnIgKyBEcjsKCQkgIGNvbnN0IFQyaSA9IEJpICsgRGk7CgkJICBjb25zdCBUM3IgPSBpbnYgKiAoQnIgLSBEcik7CgkJICBjb25zdCBUM2kgPSBpbnYgKiAoQmkgLSBEaSk7CgoJCSAgLy8gRmluYWwgdmFsdWVzCgkJICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgkJICBjb25zdCBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgY29uc3QgRkJyID0gVDFyICsgVDNpOwoJCSAgY29uc3QgRkJpID0gVDFpIC0gVDNyOwoKCQkgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCQkgIGNvbnN0IEZDaSA9IFQwaSAtIFQyaTsKCgkJICBjb25zdCBGRHIgPSBUMXIgLSBUM2k7CgkJICBjb25zdCBGRGkgPSBUMWkgKyBUM3I7CgoJCSAgb3V0W291dE9mZl0gPSBGQXI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSBGQWk7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSBGQnI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSBGQmk7CgkJICBvdXRbb3V0T2ZmICsgNF0gPSBGQ3I7CgkJICBvdXRbb3V0T2ZmICsgNV0gPSBGQ2k7CgkJICBvdXRbb3V0T2ZmICsgNl0gPSBGRHI7CgkJICBvdXRbb3V0T2ZmICsgN10gPSBGRGk7CgkJfTsKCgkJLy8gUmVhbCBpbnB1dCByYWRpeC00IGltcGxlbWVudGF0aW9uCgkJRkZULnByb3RvdHlwZS5fcmVhbFRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfcmVhbFRyYW5zZm9ybTQoKSB7CgkJICB2YXIgb3V0ID0gdGhpcy5fb3V0OwoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCgkJICAvLyBJbml0aWFsIHN0ZXAgKHBlcm11dGUgYW5kIHRyYW5zZm9ybSkKCQkgIHZhciB3aWR0aCA9IHRoaXMuX3dpZHRoOwoJCSAgdmFyIHN0ZXAgPSAxIDw8IHdpZHRoOwoJCSAgdmFyIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCgkJICB2YXIgb3V0T2ZmOwoJCSAgdmFyIHQ7CgkJICB2YXIgYml0cmV2ID0gdGhpcy5fYml0cmV2OwoJCSAgaWYgKGxlbiA9PT0gNCkgewoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlUmVhbFRyYW5zZm9ybTIob3V0T2ZmLCBvZmYgPj4+IDEsIHN0ZXAgPj4+IDEpOwoJCSAgICB9CgkJICB9IGVsc2UgewoJCSAgICAvLyBsZW4gPT09IDgKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVJlYWxUcmFuc2Zvcm00KG91dE9mZiwgb2ZmID4+PiAxLCBzdGVwID4+PiAxKTsKCQkgICAgfQoJCSAgfQoKCQkgIC8vIExvb3AgdGhyb3VnaCBzdGVwcyBpbiBkZWNyZWFzaW5nIG9yZGVyCgkJICB2YXIgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgdmFyIHRhYmxlID0gdGhpcy50YWJsZTsKCQkgIGZvciAoc3RlcCA+Pj0gMjsgc3RlcCA+PSAyOyBzdGVwID4+PSAyKSB7CgkJICAgIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCQkgICAgdmFyIGhhbGZMZW4gPSBsZW4gPj4+IDE7CgkJICAgIHZhciBxdWFydGVyTGVuID0gaGFsZkxlbiA+Pj4gMTsKCQkgICAgdmFyIGhxdWFydGVyTGVuID0gcXVhcnRlckxlbiA+Pj4gMTsKCgkJICAgIC8vIExvb3AgdGhyb3VnaCBvZmZzZXRzIGluIHRoZSBkYXRhCgkJICAgIGZvciAob3V0T2ZmID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbikgewoJCSAgICAgIGZvciAodmFyIGkgPSAwLCBrID0gMDsgaSA8PSBocXVhcnRlckxlbjsgaSArPSAyLCBrICs9IHN0ZXApIHsKCQkgICAgICAgIHZhciBBID0gb3V0T2ZmICsgaTsKCQkgICAgICAgIHZhciBCID0gQSArIHF1YXJ0ZXJMZW47CgkJICAgICAgICB2YXIgQyA9IEIgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgdmFyIEQgPSBDICsgcXVhcnRlckxlbjsKCgkJICAgICAgICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBBciA9IG91dFtBXTsKCQkgICAgICAgIHZhciBBaSA9IG91dFtBICsgMV07CgkJICAgICAgICB2YXIgQnIgPSBvdXRbQl07CgkJICAgICAgICB2YXIgQmkgPSBvdXRbQiArIDFdOwoJCSAgICAgICAgdmFyIENyID0gb3V0W0NdOwoJCSAgICAgICAgdmFyIENpID0gb3V0W0MgKyAxXTsKCQkgICAgICAgIHZhciBEciA9IG91dFtEXTsKCQkgICAgICAgIHZhciBEaSA9IG91dFtEICsgMV07CgoJCSAgICAgICAgLy8gTWlkZGxlIHZhbHVlcwoJCSAgICAgICAgdmFyIE1BciA9IEFyOwoJCSAgICAgICAgdmFyIE1BaSA9IEFpOwoKCQkgICAgICAgIHZhciB0YWJsZUJyID0gdGFibGVba107CgkJICAgICAgICB2YXIgdGFibGVCaSA9IGludiAqIHRhYmxlW2sgKyAxXTsKCQkgICAgICAgIHZhciBNQnIgPSBCciAqIHRhYmxlQnIgLSBCaSAqIHRhYmxlQmk7CgkJICAgICAgICB2YXIgTUJpID0gQnIgKiB0YWJsZUJpICsgQmkgKiB0YWJsZUJyOwoKCQkgICAgICAgIHZhciB0YWJsZUNyID0gdGFibGVbMiAqIGtdOwoJCSAgICAgICAgdmFyIHRhYmxlQ2kgPSBpbnYgKiB0YWJsZVsyICogayArIDFdOwoJCSAgICAgICAgdmFyIE1DciA9IENyICogdGFibGVDciAtIENpICogdGFibGVDaTsKCQkgICAgICAgIHZhciBNQ2kgPSBDciAqIHRhYmxlQ2kgKyBDaSAqIHRhYmxlQ3I7CgoJCSAgICAgICAgdmFyIHRhYmxlRHIgPSB0YWJsZVszICoga107CgkJICAgICAgICB2YXIgdGFibGVEaSA9IGludiAqIHRhYmxlWzMgKiBrICsgMV07CgkJICAgICAgICB2YXIgTURyID0gRHIgKiB0YWJsZURyIC0gRGkgKiB0YWJsZURpOwoJCSAgICAgICAgdmFyIE1EaSA9IERyICogdGFibGVEaSArIERpICogdGFibGVEcjsKCgkJICAgICAgICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICAgICAgICB2YXIgVDByID0gTUFyICsgTUNyOwoJCSAgICAgICAgdmFyIFQwaSA9IE1BaSArIE1DaTsKCQkgICAgICAgIHZhciBUMXIgPSBNQXIgLSBNQ3I7CgkJICAgICAgICB2YXIgVDFpID0gTUFpIC0gTUNpOwoJCSAgICAgICAgdmFyIFQyciA9IE1CciArIE1EcjsKCQkgICAgICAgIHZhciBUMmkgPSBNQmkgKyBNRGk7CgkJICAgICAgICB2YXIgVDNyID0gaW52ICogKE1CciAtIE1Ecik7CgkJICAgICAgICB2YXIgVDNpID0gaW52ICogKE1CaSAtIE1EaSk7CgoJCSAgICAgICAgLy8gRmluYWwgdmFsdWVzCgkJICAgICAgICB2YXIgRkFyID0gVDByICsgVDJyOwoJCSAgICAgICAgdmFyIEZBaSA9IFQwaSArIFQyaTsKCgkJICAgICAgICB2YXIgRkJyID0gVDFyICsgVDNpOwoJCSAgICAgICAgdmFyIEZCaSA9IFQxaSAtIFQzcjsKCgkJICAgICAgICBvdXRbQV0gPSBGQXI7CgkJICAgICAgICBvdXRbQSArIDFdID0gRkFpOwoJCSAgICAgICAgb3V0W0JdID0gRkJyOwoJCSAgICAgICAgb3V0W0IgKyAxXSA9IEZCaTsKCgkJICAgICAgICAvLyBPdXRwdXQgZmluYWwgbWlkZGxlIHBvaW50CgkJICAgICAgICBpZiAoaSA9PT0gMCkgewoJCSAgICAgICAgICB2YXIgRkNyID0gVDByIC0gVDJyOwoJCSAgICAgICAgICB2YXIgRkNpID0gVDBpIC0gVDJpOwoJCSAgICAgICAgICBvdXRbQ10gPSBGQ3I7CgkJICAgICAgICAgIG91dFtDICsgMV0gPSBGQ2k7CgkJICAgICAgICAgIGNvbnRpbnVlOwoJCSAgICAgICAgfQoKCQkgICAgICAgIC8vIERvIG5vdCBvdmVyd3JpdGUgb3Vyc2VsdmVzCgkJICAgICAgICBpZiAoaSA9PT0gaHF1YXJ0ZXJMZW4pCgkJICAgICAgICAgIGNvbnRpbnVlOwoKCQkgICAgICAgIC8vIEluIHRoZSBmbGlwcGVkIGNhc2U6CgkJICAgICAgICAvLyBNQWkgPSAtTUFpCgkJICAgICAgICAvLyBNQnI9LU1CaSwgTUJpPS1NQnIKCQkgICAgICAgIC8vIE1Dcj0tTUNyCgkJICAgICAgICAvLyBNRHI9TURpLCBNRGk9TURyCgkJICAgICAgICB2YXIgU1QwciA9IFQxcjsKCQkgICAgICAgIHZhciBTVDBpID0gLVQxaTsKCQkgICAgICAgIHZhciBTVDFyID0gVDByOwoJCSAgICAgICAgdmFyIFNUMWkgPSAtVDBpOwoJCSAgICAgICAgdmFyIFNUMnIgPSAtaW52ICogVDNpOwoJCSAgICAgICAgdmFyIFNUMmkgPSAtaW52ICogVDNyOwoJCSAgICAgICAgdmFyIFNUM3IgPSAtaW52ICogVDJpOwoJCSAgICAgICAgdmFyIFNUM2kgPSAtaW52ICogVDJyOwoKCQkgICAgICAgIHZhciBTRkFyID0gU1QwciArIFNUMnI7CgkJICAgICAgICB2YXIgU0ZBaSA9IFNUMGkgKyBTVDJpOwoKCQkgICAgICAgIHZhciBTRkJyID0gU1QxciArIFNUM2k7CgkJICAgICAgICB2YXIgU0ZCaSA9IFNUMWkgLSBTVDNyOwoKCQkgICAgICAgIHZhciBTQSA9IG91dE9mZiArIHF1YXJ0ZXJMZW4gLSBpOwoJCSAgICAgICAgdmFyIFNCID0gb3V0T2ZmICsgaGFsZkxlbiAtIGk7CgoJCSAgICAgICAgb3V0W1NBXSA9IFNGQXI7CgkJICAgICAgICBvdXRbU0EgKyAxXSA9IFNGQWk7CgkJICAgICAgICBvdXRbU0JdID0gU0ZCcjsKCQkgICAgICAgIG91dFtTQiArIDFdID0gU0ZCaTsKCQkgICAgICB9CgkJICAgIH0KCQkgIH0KCQl9OwoKCQkvLyByYWRpeC0yIGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTQKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVSZWFsVHJhbnNmb3JtMiA9IGZ1bmN0aW9uIF9zaW5nbGVSZWFsVHJhbnNmb3JtMihvdXRPZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoKCQkgIGNvbnN0IGV2ZW5SID0gZGF0YVtvZmZdOwoJCSAgY29uc3Qgb2RkUiA9IGRhdGFbb2ZmICsgc3RlcF07CgoJCSAgY29uc3QgbGVmdFIgPSBldmVuUiArIG9kZFI7CgkJICBjb25zdCByaWdodFIgPSBldmVuUiAtIG9kZFI7CgoJCSAgb3V0W291dE9mZl0gPSBsZWZ0UjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSByaWdodFI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSAwOwoJCX07CgoJCS8vIHJhZGl4LTQKCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49OAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVJlYWxUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3NpbmdsZVJlYWxUcmFuc2Zvcm00KG91dE9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgkJICBjb25zdCBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICBjb25zdCBzdGVwMiA9IHN0ZXAgKiAyOwoJCSAgY29uc3Qgc3RlcDMgPSBzdGVwICogMzsKCgkJICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEFyID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgQnIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3QgQ3IgPSBkYXRhW29mZiArIHN0ZXAyXTsKCQkgIGNvbnN0IERyID0gZGF0YVtvZmYgKyBzdGVwM107CgoJCSAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgY29uc3QgVDByID0gQXIgKyBDcjsKCQkgIGNvbnN0IFQxciA9IEFyIC0gQ3I7CgkJICBjb25zdCBUMnIgPSBCciArIERyOwoJCSAgY29uc3QgVDNyID0gaW52ICogKEJyIC0gRHIpOwoKCQkgIC8vIEZpbmFsIHZhbHVlcwoJCSAgY29uc3QgRkFyID0gVDByICsgVDJyOwoKCQkgIGNvbnN0IEZCciA9IFQxcjsKCQkgIGNvbnN0IEZCaSA9IC1UM3I7CgoJCSAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoKCQkgIGNvbnN0IEZEciA9IFQxcjsKCQkgIGNvbnN0IEZEaSA9IFQzcjsKCgkJICBvdXRbb3V0T2ZmXSA9IEZBcjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSBGQnI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSBGQmk7CgkJICBvdXRbb3V0T2ZmICsgNF0gPSBGQ3I7CgkJICBvdXRbb3V0T2ZmICsgNV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDZdID0gRkRyOwoJCSAgb3V0W291dE9mZiArIDddID0gRkRpOwoJCX07CgkJcmV0dXJuIGZmdDsKCX0KCgl2YXIgZmZ0RXhwb3J0cyA9IHJlcXVpcmVGZnQoKTsKCXZhciBGRlQgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoZmZ0RXhwb3J0cyk7CgoJY29uc3QgV0VCQVVESU9fQkxPQ0tfU0laRSA9IDEyODsKCWNvbnN0IERFRkFVTFRfQkxPQ0tfU0laRSA9IDEwMjQ7IC8vIERlZmF1bHQgYmxvY2sgc2l6ZSBpZiBub3QgcHJvdmlkZWQgaW4gb3B0aW9ucwoJLyoqIE92ZXJsYXAtQWRkIE5vZGUgKi8KCWNsYXNzIE9MQVByb2Nlc3NvciBleHRlbmRzIEF1ZGlvV29ya2xldFByb2Nlc3NvciB7CgkgICAgbmJJbnB1dHM7CgkgICAgbmJPdXRwdXRzOwoJICAgIGJsb2NrU2l6ZTsKCSAgICBob3BTaXplOwoJICAgIG5iT3ZlcmxhcHM7CgkgICAgaW5wdXRCdWZmZXJzID0gW107CgkgICAgaW5wdXRCdWZmZXJzSGVhZCA9IFtdOwoJICAgIGlucHV0QnVmZmVyc1RvU2VuZCA9IFtdOwoJICAgIG91dHB1dEJ1ZmZlcnMgPSBbXTsKCSAgICBvdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSA9IFtdOwoJICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKCSAgICAgICAgc3VwZXIob3B0aW9ucyk7CgkgICAgICAgIHRoaXMubmJJbnB1dHMgPSBvcHRpb25zPy5udW1iZXJPZklucHV0cyB8fCAxOwoJICAgICAgICB0aGlzLm5iT3V0cHV0cyA9IG9wdGlvbnM/Lm51bWJlck9mT3V0cHV0cyB8fCAxOwoJICAgICAgICAvLyBwcm9jZXNzb3JPcHRpb25zIGlzIHR5cGVkIGB1bmtub3duYCBhdCB0aGUgYW1iaWVudCBib3VuZGFyeTsgbmFycm93IGhlcmUuCgkgICAgICAgIGNvbnN0IHByb2NPcHRzID0gKG9wdGlvbnM/LnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwoJICAgICAgICB0aGlzLmJsb2NrU2l6ZSA9IHByb2NPcHRzLmJsb2NrU2l6ZSB8fCBERUZBVUxUX0JMT0NLX1NJWkU7CgkgICAgICAgIHRoaXMuaG9wU2l6ZSA9IFdFQkFVRElPX0JMT0NLX1NJWkU7CgkgICAgICAgIHRoaXMubmJPdmVybGFwcyA9IE1hdGguZmxvb3IodGhpcy5ibG9ja1NpemUgLyB0aGlzLmhvcFNpemUpOwoJICAgICAgICB0aGlzLmluaXRpYWxpemVCdWZmZXJzKCk7CgkgICAgfQoJICAgIGluaXRpYWxpemVCdWZmZXJzKCkgewoJICAgICAgICB0aGlzLmlucHV0QnVmZmVycyA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZCA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzID0gbmV3IEFycmF5KHRoaXMubmJPdXRwdXRzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSA9IG5ldyBBcnJheSh0aGlzLm5iT3V0cHV0cyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmFsbG9jYXRlSW5wdXRDaGFubmVscyhpLCAxKTsKCSAgICAgICAgfQoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVPdXRwdXRDaGFubmVscyhpLCAxKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICBhbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaW5wdXRJbmRleCwgbmJDaGFubmVscykgewoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYkNoYW5uZWxzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSArIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0uZmlsbCgwKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpbnB1dEluZGV4XVtpXSA9IHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldLnN1YmFycmF5KDAsIHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2lucHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgYWxsb2NhdGVPdXRwdXRDaGFubmVscyhvdXRwdXRJbmRleCwgbmJDaGFubmVscykgewoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW291dHB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYkNoYW5uZWxzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF1baV0uZmlsbCgwKTsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbb3V0cHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgcmVhbGxvY2F0ZUNoYW5uZWxzSWZOZWVkZWQoaW5wdXRzLCBvdXRwdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBuYkNoYW5uZWxzID0gaW5wdXRzW2ldLmxlbmd0aDsKCSAgICAgICAgICAgIGlmIChuYkNoYW5uZWxzICE9PSB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGgpIHsKCSAgICAgICAgICAgICAgICB0aGlzLmFsbG9jYXRlSW5wdXRDaGFubmVscyhpLCBuYkNoYW5uZWxzKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IG5iQ2hhbm5lbHMgPSBvdXRwdXRzW2ldLmxlbmd0aDsKCSAgICAgICAgICAgIGlmIChuYkNoYW5uZWxzICE9PSB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZU91dHB1dENoYW5uZWxzKGksIG5iQ2hhbm5lbHMpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHJlYWRJbnB1dHMoaW5wdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgY29uc3Qgd2ViQXVkaW9CbG9jayA9IGlucHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpXVtqXS5zZXQod2ViQXVkaW9CbG9jaywgdGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHdyaXRlT3V0cHV0cyhvdXRwdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCB3ZWJBdWRpb0Jsb2NrID0gb3V0cHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICB3ZWJBdWRpb0Jsb2NrLnNldCh0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uc3ViYXJyYXkoMCwgV0VCQVVESU9fQkxPQ0tfU0laRSkpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHNoaWZ0SW5wdXRCdWZmZXJzKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2ldW2pdLmNvcHlXaXRoaW4oMCwgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgc2hpZnRPdXRwdXRCdWZmZXJzKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLmNvcHlXaXRoaW4oMCwgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLmZpbGwoMCwgdGhpcy5ibG9ja1NpemUgLSBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBwcmVwYXJlSW5wdXRCdWZmZXJzVG9TZW5kKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2ldW2pdLnNldCh0aGlzLmlucHV0QnVmZmVyc0hlYWRbaV1bal0pOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIGhhbmRsZU91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCB0aGlzLmJsb2NrU2l6ZTsgaysrKSB7CgkgICAgICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXVtrXSArPSB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW2ldW2pdW2tdIC8gdGhpcy5uYk92ZXJsYXBzOwoJICAgICAgICAgICAgICAgIH0KCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICB0aGlzLnJlYWxsb2NhdGVDaGFubmVsc0lmTmVlZGVkKGlucHV0cywgb3V0cHV0cyk7CgkgICAgICAgIHRoaXMucmVhZElucHV0cyhpbnB1dHMpOwoJICAgICAgICB0aGlzLnNoaWZ0SW5wdXRCdWZmZXJzKCk7CgkgICAgICAgIHRoaXMucHJlcGFyZUlucHV0QnVmZmVyc1RvU2VuZCgpOwoJICAgICAgICB0aGlzLnByb2Nlc3NPTEEodGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmQsIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmUsIHBhcmFtZXRlcnMpOwoJICAgICAgICB0aGlzLmhhbmRsZU91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlKCk7CgkgICAgICAgIHRoaXMud3JpdGVPdXRwdXRzKG91dHB1dHMpOwoJICAgICAgICB0aGlzLnNoaWZ0T3V0cHV0QnVmZmVycygpOwoJICAgICAgICByZXR1cm4gdHJ1ZTsKCSAgICB9Cgl9CgoJLyoKCSAqIFBoYXNlLXZvY29kZXIgY29yZSDigJQgY29udGV4dC1mcmVlLCB1bml0LXRlc3RhYmxlIERTUCBmb3IgcGVhay1iYXNlZAoJICogcGl0Y2gtc2hpZnRpbmcgd2l0aCBJZGVudGl0eSBQaGFzZS1Mb2NraW5nLgoJICoKCSAqIEFsZ29yaXRobTogSmVhbiBMYXJvY2hlICYgTWFyayBEb2xzb24sICJOZXcgUGhhc2UtVm9jb2RlciBUZWNobmlxdWVzIGZvcgoJICogUGl0Y2gtU2hpZnRpbmcsIEhhcm1vbml6aW5nIGFuZCBPdGhlciBFeG90aWMgRWZmZWN0cyIsIFByb2MuIDE5OTkgSUVFRQoJICogV0FTUEFBLCBOZXcgUGFsdHosIE5ZLiBUaGUgcGl0Y2gtc2hpZnQgZGV0ZWN0cyBzcGVjdHJhbCBwZWFrcywgZGl2aWRlcyB0aGUKCSAqIGZyZXF1ZW5jeSBheGlzIGludG8gcGVyLXBlYWsgInJlZ2lvbnMgb2YgaW5mbHVlbmNlIiwgYW5kIHJpZ2lkbHkgdHJhbnNsYXRlcwoJICogZWFjaCByZWdpb24gdG8gdGhlIHBlYWsncyBzaGlmdGVkIGZyZXF1ZW5jeSAoU2VjdGlvbiAzLjEtMy40KS4KCSAqCgkgKiBUaGUgcGhhc2UgaGFuZGxpbmcgaXMgdGhlICJJZGVudGl0eSBQaGFzZS1Mb2NraW5nIiBydWxlIG9mIExhcm9jaGUtRG9sc29uCgkgKiAxOTk5IFNlY3Rpb24gMy41OiB0byBtYWludGFpbiBmcmFtZS10by1mcmFtZSAoaG9yaXpvbnRhbCkgcGhhc2UgY29oZXJlbmNlCgkgKiBhZnRlciBzaGlmdGluZyBhIHBlYWsgYnkgRGVsdGEtb21lZ2EsIEVWRVJZIGZyZXF1ZW5jeSBiaW4gaW4gdGhhdCBwZWFrJ3MKCSAqIHJlZ2lvbiBvZiBpbmZsdWVuY2UgaXMgbXVsdGlwbGllZCBieSBhIFNJTkdMRSBjb21wbGV4IG51bWJlcgoJICoKCSAqICAgICBaX3UgPSBleHAoaiAqIERlbHRhLW9tZWdhICogUikgICAgICAgICAgICAoTGFyb2NoZS1Eb2xzb24gMTk5OSwgZXEuIHAuMykKCSAqCgkgKiAoUiA9IGhvcCBzaXplKSwgYW5kIHRoZSByb3RhdGlvbiBpcyBjdW11bGF0ZWQgZnJhbWUgdG8gZnJhbWUKCSAqCgkgKiAgICAgWl97dSsxfSA9IFpfdSAqIGV4cChqICogRGVsdGEtb21lZ2Ffe3UrMX0gKiBSKS4KCSAqCgkgKiBCZWNhdXNlIGFsbCBiaW5zIGluIGEgcmVnaW9uIGFyZSByb3RhdGVkIGJ5IHRoZSBTQU1FIGFuZ2xlLCB0aGUgcGhhc2UKCSAqIHJlbGF0aW9uc2hpcHMgQkVUV0VFTiBiaW5zIGFyb3VuZCBhIHBlYWsgKHRoZSB2ZXJ0aWNhbCAvIGludHJhLXBlYWsKCSAqIGNvaGVyZW5jZSB0aGF0IGlkZW50aWZpZXMgdGhlIHNpbnVzb2lkKSBhcmUgUFJFU0VSVkVEIGFjcm9zcyB0aGUgbW92ZSDigJQgdGhpcwoJICogaXMgZXhhY3RseSB3aGF0IHJlbW92ZXMgdGhlICJwaGFzaW5lc3MiIGFydGlmYWN0IG9mIHRoZSBuYWl2ZSBiaW4taW5kZXBlbmRlbnQKCSAqIHBoYXNlIHZvY29kZXIuIE5vIGtub3dsZWRnZSBvZiB0aGUgdHJ1ZSBwZWFrIGZyZXF1ZW5jeSBvbWVnYSBpcyBuZWVkZWQsIHNvIG5vCgkgKiBhcmN0YW5nZW50IGFuZCBubyBwaGFzZS11bndyYXBwaW5nIChMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy41KS4KCSAqCgkgKiBUaGlzIG1vZHVsZSBtaXJyb3JzIHRoZSBwcm9qZWN0J3MgY29yZS9zaGVsbCBzcGxpdCAoY2YuIHdhdmVzaGFwZXItY29yZS50cywKCSAqIGR5bmFtaWNzLWNvcmUudHMpOiB0aGUgRkZUIGZyYW1pbmcgYW5kIHdvcmtsZXQgcGx1bWJpbmcgbGl2ZSBpbiB0aGUKCSAqIEF1ZGlvV29ya2xldFByb2Nlc3NvciBzaGVsbCAocGhhc2Utdm9jb2Rlci50cyk7IHRoZSBzcGVjdHJ1bSBtYW5pcHVsYXRpb24KCSAqIHRoYXQgY2FycmllcyB0aGUgdGVzdGFibGUgaW52YXJpYW50cyBsaXZlcyBoZXJlLgoJICovCgkvKioKCSAqIFNxdWFyZWQtbWFnbml0dWRlIHNwZWN0cnVtIGZyb20gYW4gaW50ZXJsZWF2ZWQgW3JlLCBpbSwgcmUsIGltLCAuLi5dIGNvbXBsZXgKCSAqIGJ1ZmZlci4gYG1hZ25pdHVkZXNbaV1gIGNvcnJlc3BvbmRzIHRvIGJpbiBgaWAuIFdyaXRlcyBpbnRvIGBvdXRgIChsZW5ndGgKCSAqIGZmdFNpemUvMiArIDEpIHRvIGF2b2lkIHBlci1jYWxsIGFsbG9jYXRpb24gaW4gdGhlIHdvcmtsZXQuCgkgKi8KCWZ1bmN0aW9uIGNvbXB1dGVNYWduaXR1ZGVzKGNvbXBsZXgsIG91dCkgewoJICAgIGZvciAobGV0IGkgPSAwLCBqID0gMDsgaSA8IG91dC5sZW5ndGg7IGkrKywgaiArPSAyKSB7CgkgICAgICAgIGNvbnN0IHJlYWwgPSBjb21wbGV4W2pdOwoJICAgICAgICBjb25zdCBpbWFnID0gY29tcGxleFtqICsgMV07CgkgICAgICAgIG91dFtpXSA9IHJlYWwgKiogMiArIGltYWcgKiogMjsKCSAgICB9Cgl9CgkvKioKCSAqIFBlYWsgZGV0ZWN0aW9uIHBlciBMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy4yOiBhIGJpbiBpcyBhIHBlYWsgaWZmIGl0cwoJICogbWFnbml0dWRlIHN0cmljdGx5IGV4Y2VlZHMgaXRzIHR3byBuZWFyZXN0IG5laWdoYm91cnMgb24gZWFjaCBzaWRlLiBXcml0ZXMKCSAqIHRoZSBwZWFrIGJpbiBpbmRpY2VzIGludG8gYHBlYWtJbmRleGVzYCBhbmQgcmV0dXJucyB0aGUgY291bnQuCgkgKi8KCWZ1bmN0aW9uIGZpbmRQZWFrcyhtYWduaXR1ZGVzLCBwZWFrSW5kZXhlcykgewoJICAgIGxldCBuYlBlYWtzID0gMDsKCSAgICBmb3IgKGxldCBpID0gMiwgZW5kID0gbWFnbml0dWRlcy5sZW5ndGggLSAyOyBpIDwgZW5kOyBpKyspIHsKCSAgICAgICAgY29uc3QgbWFnID0gbWFnbml0dWRlc1tpXTsKCSAgICAgICAgaWYgKG1hZ25pdHVkZXNbaSAtIDFdID49IG1hZyB8fCBtYWduaXR1ZGVzW2kgLSAyXSA+PSBtYWcgfHwgbWFnbml0dWRlc1tpICsgMV0gPj0gbWFnIHx8IG1hZ25pdHVkZXNbaSArIDJdID49IG1hZykgewoJICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgIH0KCSAgICAgICAgcGVha0luZGV4ZXNbbmJQZWFrcysrXSA9IGk7CgkgICAgfQoJICAgIHJldHVybiBuYlBlYWtzOwoJfQoJLyoqCgkgKiBUaGUgcGVyLWZyYW1lIHBoYXNlIGluY3JlbWVudCBleHAoaiAqIERlbHRhLW9tZWdhICogUikgZm9yIE9ORSBwZWFrIHNoaWZ0ZWQKCSAqIGJ5IERlbHRhLW9tZWdhIG92ZXIgYSBob3Agb2YgUiBzeW50aGVzaXMgc2FtcGxlcy4gTGFyb2NoZS1Eb2xzb24gMTk5OSBlcS4KCSAqIHAuMyAoU2VjdGlvbiAzLjUpLCB0aGUgc2luZ2xlIGNvbXBsZXggbnVtYmVyIGFwcGxpZWQgdW5pZm9ybWx5IHRvIHRoZSBwZWFrJ3MKCSAqIHdob2xlIHJlZ2lvbiBvZiBpbmZsdWVuY2UuCgkgKgoJICogRGVsdGEtb21lZ2EgKHJhZC9zYW1wbGUpIGlzIHRoZSBmcmVxdWVuY3kgc2hpZnQgdGhlIHBlYWsgdW5kZXJnb2VzOgoJICogICBEZWx0YS1vbWVnYSA9IDIqcGkgKiAocGVha0luZGV4U2hpZnRlZCAtIHBlYWtJbmRleCkgLyBmZnRTaXplLgoJICoKCSAqIFRoaXMgaXMgdGhlIHBlci1mcmFtZSBmYWN0b3IsIE5PVCB0aGUgY3VtdWxhdGl2ZSByb3RhdG9yLiBDcm9zcy1mcmFtZQoJICogY3VtdWxhdGlvbiBaX3t1KzF9ID0gWl91ICogZXhwKGoqRGVsdGEtb21lZ2Ffe3UrMX0qUikgaXMgdGhlIGpvYiBvZgoJICoge0BsaW5rIFBlYWtSb3RhdG9yU3RhdGV9OyB0aGlzIGZ1bmN0aW9uIHByb2R1Y2VzIHRoZSBgZXhwKC4uLilgIGZhY3RvciB0aGF0CgkgKiBzdGF0ZSBtdWx0aXBsaWVzIGluIGVhY2ggZnJhbWUuCgkgKi8KCWZ1bmN0aW9uIGZyYW1lUm90YXRpb24ocGVha0luZGV4LCBwZWFrSW5kZXhTaGlmdGVkLCBmZnRTaXplLCBob3ApIHsKCSAgICBjb25zdCBvbWVnYURlbHRhID0gKDIgKiBNYXRoLlBJICogKHBlYWtJbmRleFNoaWZ0ZWQgLSBwZWFrSW5kZXgpKSAvIGZmdFNpemU7CgkgICAgY29uc3QgYW5nbGUgPSBvbWVnYURlbHRhICogaG9wOwoJICAgIHJldHVybiB7IHJlOiBNYXRoLmNvcyhhbmdsZSksIGltOiBNYXRoLnNpbihhbmdsZSkgfTsKCX0KCS8qKgoJICogQ3Jvc3MtZnJhbWUgY3VtdWxhdGl2ZSBwaGFzZS1sb2NrIHN0YXRlIChMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy41KS4KCSAqCgkgKiBUaGUgcGFwZXIgcmVxdWlyZXMgdGhlIHBlci1wZWFrIHJvdGF0aW9uIGJlIEFDQ1VNVUxBVEVEIGZyYW1lIHRvIGZyYW1lOgoJICoKCSAqICAgICBaX3t1KzF9ID0gWl91ICogZXhwKGogKiBEZWx0YS1vbWVnYV97dSsxfSAqIFIpCgkgKgoJICogd2l0aCBEZWx0YS1vbWVnYSBhbGxvd2VkIHRvIHZhcnkgcGVyIGZyYW1lIChhdXRvbWF0ZWQgLyB0aW1lLXZhcnlpbmcgcGl0Y2gpLgoJICogQSBuYWl2ZSBgb21lZ2FEZWx0YSAqIGVsYXBzZWRUaW1lYCByb3RhdG9yIGlzIHdyb25nOiB3aGVuIHRoZSBzaGlmdCBjaGFuZ2VzLAoJICogaXQgcmV0cm9hY3RpdmVseSByZS1waGFzZXMgZXZlcnkgcHJpb3IgZnJhbWUgYW5kIHByb2R1Y2VzIGEgZGlzY29udGludWl0eS4KCSAqCgkgKiBUaGlzIHN0YXRlIGtlZXBzIG9uZSBjdW11bGF0aXZlIHJvdGF0b3IgWl91IFBFUiBQRUFLLCBrZXllZCBieSB0aGUgcGVhaydzCgkgKiBzb3VyY2UgYmluIGluZGV4LiB7QGxpbmsgUGVha1JvdGF0b3JTdGF0ZS5hZHZhbmNlfSBtdWx0aXBsaWVzIGVhY2ggcGVhaydzIFoKCSAqIGJ5IHRoaXMgZnJhbWUncyBleHAoaipEZWx0YS1vbWVnYSpSKSAoc28gaGlzdG9yeSBpcyBwcmVzZXJ2ZWQgYWNyb3NzIHBpdGNoCgkgKiBjaGFuZ2VzKSwgYW5kIHtAbGluayBQZWFrUm90YXRvclN0YXRlLmdldH0gcmV0dXJucyB0aGUgY3VycmVudCBjdW11bGF0aXZlCgkgKiByb3RhdG9yIHRvIGFwcGx5IHRvIHRoYXQgcGVhaydzIHJlZ2lvbiBvZiBpbmZsdWVuY2UuCgkgKi8KCWNsYXNzIFBlYWtSb3RhdG9yU3RhdGUgewoJICAgIC8qKiBwZWFrIHNvdXJjZS1iaW4gaW5kZXggLT4gY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSAodW5pdCBtb2R1bHVzKS4gKi8KCSAgICByb3RhdG9ycyA9IG5ldyBNYXAoKTsKCSAgICAvKiogc2NyYXRjaCBzZXQgb2YgYmlucyBzZWVuIHRoaXMgZnJhbWUsIGZvciBwcnVuaW5nIHZhbmlzaGVkIHBlYWtzLiAqLwoJICAgIHNlZW4gPSBuZXcgU2V0KCk7CgkgICAgLyoqCgkgICAgICogQWR2YW5jZSBldmVyeSBjdXJyZW50bHktZGV0ZWN0ZWQgcGVhaydzIGN1bXVsYXRpdmUgcm90YXRvciBieSB0aGlzIGZyYW1lJ3MKCSAgICAgKiBleHAoaipEZWx0YS1vbWVnYSpSKS4gTmV3IHBlYWtzIHN0YXJ0IGF0IFogPSAxIChubyByb3RhdGlvbikgdGhlbiB0YWtlIHRoaXMKCSAgICAgKiBmcmFtZSdzIGluY3JlbWVudDsgcGVha3Mgbm90IHByZXNlbnQgdGhpcyBmcmFtZSBhcmUgZHJvcHBlZCBzbyB0aGVpciBzdGFsZQoJICAgICAqIHBoYXNlIGRvZXMgbm90IGxlYWsgaW50byBhIGxhdGVyIHJlLWRldGVjdGlvbi4KCSAgICAgKgoJICAgICAqIGBwaXRjaEZhY3RvcmAgYW5kIGBob3BgIGRlZmluZSB0aGlzIGZyYW1lJ3MgcGVyLXBlYWsgRGVsdGEtb21lZ2EgdmlhIHRoZQoJICAgICAqIHNoaWZ0ZWQgYmluIGByb3VuZChwZWFrSW5kZXggKiBwaXRjaEZhY3RvcilgLgoJICAgICAqLwoJICAgIGFkdmFuY2UocGVha0luZGV4ZXMsIG5iUGVha3MsIGZmdFNpemUsIHBpdGNoRmFjdG9yLCBob3ApIHsKCSAgICAgICAgdGhpcy5zZWVuLmNsZWFyKCk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJQZWFrczsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBwZWFrSW5kZXggPSBwZWFrSW5kZXhlc1tpXTsKCSAgICAgICAgICAgIHRoaXMuc2Vlbi5hZGQocGVha0luZGV4KTsKCSAgICAgICAgICAgIGNvbnN0IHBlYWtJbmRleFNoaWZ0ZWQgPSBNYXRoLnJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKTsKCSAgICAgICAgICAgIGNvbnN0IGluYyA9IGZyYW1lUm90YXRpb24ocGVha0luZGV4LCBwZWFrSW5kZXhTaGlmdGVkLCBmZnRTaXplLCBob3ApOwoJICAgICAgICAgICAgY29uc3QgcHJldiA9IHRoaXMucm90YXRvcnMuZ2V0KHBlYWtJbmRleCkgPz8geyByZTogMSwgaW06IDAgfTsKCSAgICAgICAgICAgIC8vIFpfe3UrMX0gPSBaX3UgKiBpbmMgIChjb21wbGV4IG11bHRpcGx5KQoJICAgICAgICAgICAgdGhpcy5yb3RhdG9ycy5zZXQocGVha0luZGV4LCB7CgkgICAgICAgICAgICAgICAgcmU6IHByZXYucmUgKiBpbmMucmUgLSBwcmV2LmltICogaW5jLmltLAoJICAgICAgICAgICAgICAgIGltOiBwcmV2LnJlICogaW5jLmltICsgcHJldi5pbSAqIGluYy5yZSwKCSAgICAgICAgICAgIH0pOwoJICAgICAgICB9CgkgICAgICAgIC8vIFBydW5lIHBlYWtzIHRoYXQgZGlzYXBwZWFyZWQgdGhpcyBmcmFtZS4KCSAgICAgICAgZm9yIChjb25zdCBrZXkgb2YgdGhpcy5yb3RhdG9ycy5rZXlzKCkpIHsKCSAgICAgICAgICAgIGlmICghdGhpcy5zZWVuLmhhcyhrZXkpKQoJICAgICAgICAgICAgICAgIHRoaXMucm90YXRvcnMuZGVsZXRlKGtleSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgLyoqIFRoZSBjdXJyZW50IGN1bXVsYXRpdmUgcm90YXRvciBaX3UgZm9yIGEgcGVhayAoaWRlbnRpdHkgaWYgdW5zZWVuKS4gKi8KCSAgICBnZXQocGVha0luZGV4KSB7CgkgICAgICAgIHJldHVybiB0aGlzLnJvdGF0b3JzLmdldChwZWFrSW5kZXgpID8/IHsgcmU6IDEsIGltOiAwIH07CgkgICAgfQoJICAgIC8qKiBSZXNldCBhbGwgYWNjdW11bGF0ZWQgcGhhc2UgKGUuZy4gb24gc3RvcCAvIHJlLXNlZWspLiAqLwoJICAgIHJlc2V0KCkgewoJICAgICAgICB0aGlzLnJvdGF0b3JzLmNsZWFyKCk7CgkgICAgICAgIHRoaXMuc2Vlbi5jbGVhcigpOwoJICAgIH0KCSAgICAvKiogTnVtYmVyIG9mIHBlYWtzIGN1cnJlbnRseSB0cmFja2VkICh0ZXN0L2ludHJvc3BlY3Rpb24gaGVscGVyKS4gKi8KCSAgICBnZXQgc2l6ZSgpIHsKCSAgICAgICAgcmV0dXJuIHRoaXMucm90YXRvcnMuc2l6ZTsKCSAgICB9Cgl9CgkvKioKCSAqIEhhbGYtd2F5IHJlZ2lvbi1vZi1pbmZsdWVuY2UgYm91bmRhcmllcyBmb3IgcGVhayBgaWAgKExhcm9jaGUtRG9sc29uIDE5OTkKCSAqIFNlY3Rpb24gMy4yIOKAlCBib3VuZGFyeSBzZXQgbWlkd2F5IGJldHdlZW4gYWRqYWNlbnQgcGVha3MpLiBSZXR1cm5zIHRoZQoJICogW3N0YXJ0SW5kZXgsIGVuZEluZGV4KSBiaW4gcmFuZ2Ugb3duZWQgYnkgdGhpcyBwZWFrLgoJICoKCSAqIFRoZSBMQVNUIHBlYWsncyByZWdpb24gbXVzdCBlbmQgYXQgdGhlIG5vbi1yZWR1bmRhbnQgaGFsZi1zcGVjdHJ1bSBsZW5ndGgKCSAqIGBtYWduaXR1ZGVzTGVuZ3RoYCAoPSBmZnRTaXplLzIgKyAxKSwgTk9UIGF0IGBmZnRTaXplYC4gVGhlIGFuYWx5c2VkIHNwZWN0cnVtCgkgKiBvbmx5IGNhcnJpZXMgYmlucyBbMCwgZmZ0U2l6ZS8yXTsgdGhlIHVwcGVyIGhhbGYgaXMgdGhlIGNvbmp1Z2F0ZS1zeW1tZXRyaWMKCSAqIG1pcnJvciBmaWxsZWQgbGF0ZXIgYnkgYGNvbXBsZXRlU3BlY3RydW1gLiBSZWFkaW5nIHNvdXJjZSBiaW5zIHBhc3QgTnlxdWlzdAoJICogZm9sZHMgc3RhbGUgLyBub3QteWV0LXBvcHVsYXRlZCBuZWdhdGl2ZS1mcmVxdWVuY3kgZGF0YSBpbnRvIHRoZSBvdXRwdXQsIHNvCgkgKiB0aGUgcmVnaW9uIGlzIGNsYW1wZWQgdG8gdGhlIGFuYWx5c2VkIHBvc2l0aXZlIHNwZWN0cnVtLgoJICovCglmdW5jdGlvbiByZWdpb25PZkluZmx1ZW5jZShwZWFrSW5kZXhlcywgaSwgbmJQZWFrcywgbWFnbml0dWRlc0xlbmd0aCkgewoJICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgIGNvbnN0IHN0YXJ0SW5kZXggPSBpID4gMCA/IHBlYWtJbmRleCAtIE1hdGguZmxvb3IoKHBlYWtJbmRleCAtIHBlYWtJbmRleGVzW2kgLSAxXSkgLyAyKSA6IDA7CgkgICAgY29uc3QgZW5kSW5kZXggPSBpIDwgbmJQZWFrcyAtIDEgPyBwZWFrSW5kZXggKyBNYXRoLmNlaWwoKHBlYWtJbmRleGVzW2kgKyAxXSAtIHBlYWtJbmRleCkgLyAyKSA6IG1hZ25pdHVkZXNMZW5ndGg7CgkgICAgcmV0dXJuIHsgc3RhcnRJbmRleCwgZW5kSW5kZXggfTsKCX0KCS8qKgoJICogUGVhay1zaGlmdCB3aXRoIElkZW50aXR5IFBoYXNlLUxvY2tpbmcgKExhcm9jaGUtRG9sc29uIDE5OTksIFNlY3Rpb25zIDMuNCAmCgkgKiAzLjUpLiBGb3IgZWFjaCBkZXRlY3RlZCBwZWFrOgoJICogIC0gY29tcHV0ZSB0aGUgc2hpZnRlZCBwZWFrIGJpbiAocGVha0luZGV4ICogcGl0Y2hGYWN0b3IsIHJvdW5kZWQg4oCUIHRoZQoJICogICAgaW50ZWdlci1iaW4gY2FzZSBvZiBTZWN0aW9uIDMuNCwgYSBsb3NzbGVzcyByZWdpb24gY29weSksCgkgKiAgLSBjb21wdXRlIE9ORSByb3RhdG9yIFpfdSA9IGV4cChqKkRlbHRhLW9tZWdhKnRpbWVDdXJzb3IpIGZvciB0aGUgcGVhawoJICogICAgKGBwZWFrUm90YXRvcmApLAoJICogIC0gcmlnaWRseSB0cmFuc2xhdGUgdGhlIHBlYWsncyByZWdpb24gb2YgaW5mbHVlbmNlIHRvIHRoZSBzaGlmdGVkIGxvY2F0aW9uLAoJICogICAgbXVsdGlwbHlpbmcgRVZFUlkgYmluIGluIHRoZSByZWdpb24gYnkgdGhhdCBTQU1FIFpfdS4KCSAqIE92ZXJsYXBwaW5nIHNoaWZ0ZWQgcmVnaW9ucyBhcmUgc3VtbWVkIChTZWN0aW9uIDMuNCkuCgkgKgoJICogYGNvbXBsZXhgIGlzIHRoZSBhbmFseXNpcyBzcGVjdHJ1bSAoaW50ZXJsZWF2ZWQgcmUvaW0pLiBgc2hpZnRlZGAgaXMgdGhlCgkgKiBvdXRwdXQgc3BlY3RydW0gKGludGVybGVhdmVkIHJlL2ltKSBhbmQgaXMgemVyby1maWxsZWQgaGVyZSBiZWZvcmUKCSAqIGFjY3VtdWxhdGlvbi4gYG1hZ25pdHVkZXNMZW5ndGhgID0gZmZ0U2l6ZS8yICsgMSAodGhlIG5vbi1yZWR1bmRhbnQgYmluCgkgKiBjb3VudCk7IGJpbnMgYmV5b25kIGl0IGFyZSBza2lwcGVkIC8gdGVybWluYXRlIHRoZSBwZWFrIGxvb3AsIG1hdGNoaW5nIHRoZQoJICogd29ya2xldCdzIHByZS1gY29tcGxldGVTcGVjdHJ1bWAgaGFsZi1zcGVjdHJ1bS4KCSAqLwoJZnVuY3Rpb24gc2hpZnRQZWFrcyhjb21wbGV4LCBzaGlmdGVkLCBwZWFrSW5kZXhlcywgbmJQZWFrcywgZmZ0U2l6ZSwgbWFnbml0dWRlc0xlbmd0aCwgcGl0Y2hGYWN0b3IsIHJvdGF0b3JzKSB7CgkgICAgc2hpZnRlZC5maWxsKDApOwoJICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJQZWFrczsgaSsrKSB7CgkgICAgICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgICAgICBjb25zdCBwZWFrSW5kZXhTaGlmdGVkID0gTWF0aC5yb3VuZChwZWFrSW5kZXggKiBwaXRjaEZhY3Rvcik7CgkgICAgICAgIGNvbnN0IHsgc3RhcnRJbmRleCwgZW5kSW5kZXggfSA9IHJlZ2lvbk9mSW5mbHVlbmNlKHBlYWtJbmRleGVzLCBpLCBuYlBlYWtzLCBtYWduaXR1ZGVzTGVuZ3RoKTsKCSAgICAgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBJZGVudGl0eSBQaGFzZS1Mb2NraW5nOiBPTkUgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdQoJICAgICAgICAvLyBwZXIgcGVhaywgYXBwbGllZCB1bmlmb3JtbHkgdG8gdGhlIHdob2xlIHJlZ2lvbiBvZiBpbmZsdWVuY2UgKG9uZSBjb21wbGV4CgkgICAgICAgIC8vIG11bHRpcGx5IHBlciBiaW4pLCBwcmVzZXJ2aW5nIHRoZSBpbnRyYS1yZWdpb24gcGhhc2UgcmVsYXRpb25zaGlwcy4gWl91CgkgICAgICAgIC8vIGlzIGFjY3VtdWxhdGVkIGZyYW1lLXRvLWZyYW1lIGJ5IFBlYWtSb3RhdG9yU3RhdGUg4oCUIG5vdCByZWNvbXB1dGVkIGZyb20KCSAgICAgICAgLy8gYWJzb2x1dGUgdGltZSDigJQgc28gYSBjaGFuZ2luZyBwaXRjaEZhY3RvciBkb2VzIG5vdCByZXRyb2FjdGl2ZWx5IHJlcGhhc2UuCgkgICAgICAgIGNvbnN0IHJvdCA9IHJvdGF0b3JzLmdldChwZWFrSW5kZXgpOwoJICAgICAgICBmb3IgKGxldCBqID0gc3RhcnRJbmRleCAtIHBlYWtJbmRleDsgaiA8IGVuZEluZGV4IC0gcGVha0luZGV4OyBqKyspIHsKCSAgICAgICAgICAgIGNvbnN0IGJpbkluZGV4ID0gcGVha0luZGV4ICsgajsKCSAgICAgICAgICAgIGNvbnN0IGJpbkluZGV4U2hpZnRlZCA9IHBlYWtJbmRleFNoaWZ0ZWQgKyBqOwoJICAgICAgICAgICAgaWYgKGJpbkluZGV4U2hpZnRlZCA+PSBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgYnJlYWs7CgkgICAgICAgICAgICB9CgkgICAgICAgICAgICAvLyBTb3VyY2UgYmlucyBhcmUgb25seSB2YWxpZCBpbnNpZGUgdGhlIGFuYWx5c2VkIHBvc2l0aXZlIHNwZWN0cnVtLgoJICAgICAgICAgICAgaWYgKGJpbkluZGV4IDwgMCB8fCBiaW5JbmRleCA+PSBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgICAgICB9CgkgICAgICAgICAgICBjb25zdCBpbmRleFJlYWwgPSBiaW5JbmRleCAqIDI7CgkgICAgICAgICAgICBjb25zdCBpbmRleEltYWcgPSBpbmRleFJlYWwgKyAxOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVSZWFsID0gY29tcGxleFtpbmRleFJlYWxdOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVJbWFnID0gY29tcGxleFtpbmRleEltYWddOwoJICAgICAgICAgICAgLy8gQ29tcGxleCBtdWx0aXBseSBieSB0aGUgc2luZ2xlIHBlci1wZWFrIGN1bXVsYXRpdmUgcm90YXRvciBaX3UuCgkgICAgICAgICAgICBjb25zdCB2YWx1ZVNoaWZ0ZWRSZWFsID0gdmFsdWVSZWFsICogcm90LnJlIC0gdmFsdWVJbWFnICogcm90LmltOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVTaGlmdGVkSW1hZyA9IHZhbHVlUmVhbCAqIHJvdC5pbSArIHZhbHVlSW1hZyAqIHJvdC5yZTsKCSAgICAgICAgICAgIGlmIChiaW5JbmRleFNoaWZ0ZWQgPCAwKSB7CgkgICAgICAgICAgICAgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNDogYSByZWdpb24gb2YgaW5mbHVlbmNlIHNwaWxsaW5nIG9udG8KCSAgICAgICAgICAgICAgICAvLyB0aGUgTkVHQVRJVkUtZnJlcXVlbmN5IGF4aXMgaXMgcmVmbGVjdGVkIGJhY2sgaW50byB0aGUgcG9zaXRpdmUKCSAgICAgICAgICAgICAgICAvLyBmcmVxdWVuY2llcyB3aXRoIENPTVBMRVggQ09OSlVHQVRJT04sIGJlY2F1c2UgdGhlIG9yaWdpbmFsIHNpZ25hbCBpcwoJICAgICAgICAgICAgICAgIC8vIHJlYWwgKEhlcm1pdGlhbiBzeW1tZXRyeSBYKC13KSA9IGNvbmooWCh3KSkpLiBXaXRob3V0IHRoaXMgdGhlIGVuZXJneQoJICAgICAgICAgICAgICAgIC8vIGJlbG93IERDIGlzIGRyb3BwZWQgYW5kIGRvd253YXJkIHNoaWZ0cyBsb3NlIHRoZWlyIGxvdyBlbmQuCgkgICAgICAgICAgICAgICAgY29uc3QgcmVmbGVjdGVkID0gLWJpbkluZGV4U2hpZnRlZDsKCSAgICAgICAgICAgICAgICBpZiAocmVmbGVjdGVkID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgICAgIGNvbnN0IHJlZmxSZWFsID0gcmVmbGVjdGVkICogMjsKCSAgICAgICAgICAgICAgICBjb25zdCByZWZsSW1hZyA9IHJlZmxSZWFsICsgMTsKCSAgICAgICAgICAgICAgICBzaGlmdGVkW3JlZmxSZWFsXSArPSB2YWx1ZVNoaWZ0ZWRSZWFsOwoJICAgICAgICAgICAgICAgIHNoaWZ0ZWRbcmVmbEltYWddICs9IC12YWx1ZVNoaWZ0ZWRJbWFnOyAvLyBjb25qdWdhdGUKCSAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIGNvbnN0IGluZGV4U2hpZnRlZFJlYWwgPSBiaW5JbmRleFNoaWZ0ZWQgKiAyOwoJICAgICAgICAgICAgY29uc3QgaW5kZXhTaGlmdGVkSW1hZyA9IGluZGV4U2hpZnRlZFJlYWwgKyAxOwoJICAgICAgICAgICAgc2hpZnRlZFtpbmRleFNoaWZ0ZWRSZWFsXSArPSB2YWx1ZVNoaWZ0ZWRSZWFsOwoJICAgICAgICAgICAgc2hpZnRlZFtpbmRleFNoaWZ0ZWRJbWFnXSArPSB2YWx1ZVNoaWZ0ZWRJbWFnOwoJICAgICAgICB9CgkgICAgfQoJfQoKCWNvbnN0IEJMT0NLX1NJWkUgPSAyMDQ4OwoJZnVuY3Rpb24gaGFubihsZW5ndGgpIHsKCSAgICBjb25zdCBvdXQgPSBuZXcgRmxvYXQzMkFycmF5KGxlbmd0aCk7CgkgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykKCSAgICAgICAgb3V0W2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIGxlbmd0aCkpOwoJICAgIHJldHVybiBvdXQ7Cgl9CgkvKiogVHdvIGFkZGVkIHZvaWNlcyBpbiBvbmUgTGFyb2NoZS1Eb2xzb24gU1RGVCBhbmFseXNpcyBwYXNzLiAqLwoJY2xhc3MgSGFybW9uaXplcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBPTEFQcm9jZXNzb3IgewoJICAgIGZmdDsKCSAgICB3aW5kb3c7CgkgICAgc291cmNlU3BlY3RydW07CgkgICAgdm9pY2VBU3BlY3RydW07CgkgICAgdm9pY2VCU3BlY3RydW07CgkgICAgb3V0cHV0U3BlY3RydW07CgkgICAgdGltZUNvbXBsZXg7CgkgICAgbWFnbml0dWRlczsKCSAgICBwZWFrczsKCSAgICB2b2ljZUFTdGF0ZXMgPSBbXTsKCSAgICB2b2ljZUJTdGF0ZXMgPSBbXTsKCSAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewoJICAgICAgICByZXR1cm4gWwoJICAgICAgICAgICAgeyBuYW1lOiAic2VtaXRvbmVzQSIsIGRlZmF1bHRWYWx1ZTogNywgbWluVmFsdWU6IC0zNiwgbWF4VmFsdWU6IDM2LCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogInNlbWl0b25lc0IiLCBkZWZhdWx0VmFsdWU6IDEyLCBtaW5WYWx1ZTogLTM2LCBtYXhWYWx1ZTogMzYsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICAgICAgeyBuYW1lOiAiZ2FpbkEiLCBkZWZhdWx0VmFsdWU6IDAuNiwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAyLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogImdhaW5CIiwgZGVmYXVsdFZhbHVlOiAwLjQ1LCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICAgICAgeyBuYW1lOiAiZHJ5IiwgZGVmYXVsdFZhbHVlOiAxLCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICBdOwoJICAgIH0KCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIGNvbnN0IHdvcmtsZXRPcHRpb25zID0gb3B0aW9ucyA/PyB7fTsKCSAgICAgICAgd29ya2xldE9wdGlvbnMucHJvY2Vzc29yT3B0aW9ucyA9IHsgYmxvY2tTaXplOiBCTE9DS19TSVpFLCAuLi4od29ya2xldE9wdGlvbnMucHJvY2Vzc29yT3B0aW9ucyA/PyB7fSkgfTsKCSAgICAgICAgc3VwZXIod29ya2xldE9wdGlvbnMpOwoJICAgICAgICB0aGlzLmZmdCA9IG5ldyBGRlQodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB0aGlzLndpbmRvdyA9IGhhbm4odGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB0aGlzLnNvdXJjZVNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudm9pY2VBU3BlY3RydW0gPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy52b2ljZUJTcGVjdHJ1bSA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudGltZUNvbXBsZXggPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy5tYWduaXR1ZGVzID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSAvIDIgKyAxKTsKCSAgICAgICAgdGhpcy5wZWFrcyA9IG5ldyBJbnQzMkFycmF5KHRoaXMubWFnbml0dWRlcy5sZW5ndGgpOwoJICAgIH0KCSAgICBwcm9jZXNzT0xBKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICBjb25zdCBmYWN0b3JBID0gMiAqKiAocGFyYW1ldGVycy5zZW1pdG9uZXNBW3BhcmFtZXRlcnMuc2VtaXRvbmVzQS5sZW5ndGggLSAxXSAvIDEyKTsKCSAgICAgICAgY29uc3QgZmFjdG9yQiA9IDIgKiogKHBhcmFtZXRlcnMuc2VtaXRvbmVzQltwYXJhbWV0ZXJzLnNlbWl0b25lc0IubGVuZ3RoIC0gMV0gLyAxMik7CgkgICAgICAgIGNvbnN0IGdhaW5BID0gcGFyYW1ldGVycy5nYWluQVtwYXJhbWV0ZXJzLmdhaW5BLmxlbmd0aCAtIDFdOwoJICAgICAgICBjb25zdCBnYWluQiA9IHBhcmFtZXRlcnMuZ2FpbkJbcGFyYW1ldGVycy5nYWluQi5sZW5ndGggLSAxXTsKCSAgICAgICAgY29uc3QgZHJ5ID0gcGFyYW1ldGVycy5kcnlbcGFyYW1ldGVycy5kcnkubGVuZ3RoIC0gMV07CgkgICAgICAgIGNvbnN0IG5vcm0gPSAxIC8gTWF0aC5tYXgoMSwgZHJ5ICsgZ2FpbkEgKyBnYWluQik7CgkgICAgICAgIGZvciAobGV0IGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgaW5wdXRzWzBdLmxlbmd0aDsgY2hhbm5lbCsrKSB7CgkgICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXVtjaGFubmVsXTsKCSAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF1bY2hhbm5lbF07CgkgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlucHV0Lmxlbmd0aDsgaSsrKQoJICAgICAgICAgICAgICAgIGlucHV0W2ldICo9IHRoaXMud2luZG93W2ldOwoJICAgICAgICAgICAgdGhpcy5mZnQucmVhbFRyYW5zZm9ybSh0aGlzLnNvdXJjZVNwZWN0cnVtLCBpbnB1dCk7CgkgICAgICAgICAgICBjb21wdXRlTWFnbml0dWRlcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLm1hZ25pdHVkZXMpOwoJICAgICAgICAgICAgY29uc3QgcGVha0NvdW50ID0gZmluZFBlYWtzKHRoaXMubWFnbml0dWRlcywgdGhpcy5wZWFrcyk7CgkgICAgICAgICAgICBjb25zdCBzdGF0ZUEgPSAodGhpcy52b2ljZUFTdGF0ZXNbY2hhbm5lbF0gPz89IG5ldyBQZWFrUm90YXRvclN0YXRlKCkpOwoJICAgICAgICAgICAgY29uc3Qgc3RhdGVCID0gKHRoaXMudm9pY2VCU3RhdGVzW2NoYW5uZWxdID8/PSBuZXcgUGVha1JvdGF0b3JTdGF0ZSgpKTsKCSAgICAgICAgICAgIHN0YXRlQS5hZHZhbmNlKHRoaXMucGVha3MsIHBlYWtDb3VudCwgdGhpcy5ibG9ja1NpemUsIGZhY3RvckEsIHRoaXMuaG9wU2l6ZSk7CgkgICAgICAgICAgICBzdGF0ZUIuYWR2YW5jZSh0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCBmYWN0b3JCLCB0aGlzLmhvcFNpemUpOwoJICAgICAgICAgICAgc2hpZnRQZWFrcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLnZvaWNlQVNwZWN0cnVtLCB0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCB0aGlzLm1hZ25pdHVkZXMubGVuZ3RoLCBmYWN0b3JBLCBzdGF0ZUEpOwoJICAgICAgICAgICAgc2hpZnRQZWFrcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLnZvaWNlQlNwZWN0cnVtLCB0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCB0aGlzLm1hZ25pdHVkZXMubGVuZ3RoLCBmYWN0b3JCLCBzdGF0ZUIpOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRTcGVjdHJ1bS5maWxsKDApOwoJICAgICAgICAgICAgZm9yIChsZXQgYmluID0gMDsgYmluIDwgdGhpcy5tYWduaXR1ZGVzLmxlbmd0aDsgYmluKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtW2ldID0KCSAgICAgICAgICAgICAgICAgICAgKGRyeSAqIHRoaXMuc291cmNlU3BlY3RydW1baV0gKyBnYWluQSAqIHRoaXMudm9pY2VBU3BlY3RydW1baV0gKyBnYWluQiAqIHRoaXMudm9pY2VCU3BlY3RydW1baV0pICogbm9ybTsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtW2kgKyAxXSA9CgkgICAgICAgICAgICAgICAgICAgIChkcnkgKiB0aGlzLnNvdXJjZVNwZWN0cnVtW2kgKyAxXSArIGdhaW5BICogdGhpcy52b2ljZUFTcGVjdHJ1bVtpICsgMV0gKyBnYWluQiAqIHRoaXMudm9pY2VCU3BlY3RydW1baSArIDFdKSAqCgkgICAgICAgICAgICAgICAgICAgICAgICBub3JtOwoJICAgICAgICAgICAgfQoJICAgICAgICAgICAgdGhpcy5mZnQuY29tcGxldGVTcGVjdHJ1bSh0aGlzLm91dHB1dFNwZWN0cnVtKTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LmludmVyc2VUcmFuc2Zvcm0odGhpcy50aW1lQ29tcGxleCwgdGhpcy5vdXRwdXRTcGVjdHJ1bSk7CgkgICAgICAgICAgICB0aGlzLmZmdC5mcm9tQ29tcGxleEFycmF5KHRoaXMudGltZUNvbXBsZXgsIG91dHB1dCk7CgkgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dC5sZW5ndGg7IGkrKykKCSAgICAgICAgICAgICAgICBvdXRwdXRbaV0gKj0gdGhpcy53aW5kb3dbaV07CgkgICAgICAgIH0KCSAgICB9Cgl9CglyZWdpc3RlclByb2Nlc3NvcigiaGFybW9uaXplciIsIEhhcm1vbml6ZXJXb3JrbGV0UHJvY2Vzc29yKTsKCglleHBvcnRzLkhhcm1vbml6ZXJXb3JrbGV0UHJvY2Vzc29yID0gSGFybW9uaXplcldvcmtsZXRQcm9jZXNzb3I7CgoJcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWhhcm1vbml6ZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,KGZ1bmN0aW9uICgpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIElUVS1SIEJTLjE3NzAtNSBsb3VkbmVzcyBtZXRlcmluZyDigJQgY29udGV4dC1mcmVlIERTUCBjb3JlLgogICAgICoKICAgICAqIEltcGxlbWVudHMgdGhlIGxvdWRuZXNzIG1lYXN1cmVtZW50IHBpcGVsaW5lIG9mIFJlY29tbWVuZGF0aW9uCiAgICAgKiBJVFUtUiBCUy4xNzcwLTUgKDExLzIwMjMpLCAiQWxnb3JpdGhtcyB0byBtZWFzdXJlIGF1ZGlvIHByb2dyYW1tZSBsb3VkbmVzcwogICAgICogYW5kIHRydWUtcGVhayBhdWRpbyBsZXZlbCIsIEFubmV4IDE6CiAgICAgKgogICAgICogICAxLiAiSyIgZnJlcXVlbmN5IHdlaWdodGluZyDigJQgYSB0d28tc3RhZ2UgcHJlLWZpbHRlciBwZXIgY2hhbm5lbAogICAgICogICAgICAoQW5uZXggMSwgwqcyIC8gRmlncyAyLTQ7IGNvZWZmaWNpZW50cyBUYWJsZXMgMSAmIDIsIHAuNC01KS4KICAgICAqICAgMi4gTWVhbi1zcXVhcmUgcGVyIGNoYW5uZWwgel9pIChBbm5leCAxLCBlcS4xLCBwLjUpLgogICAgICogICAzLiBDaGFubmVsLXdlaWdodGVkIHN1bW1hdGlvbiB3aXRoIHdlaWdodHMgR19pIChBbm5leCAxLCBUYWJsZSAzLCBwLjc7CiAgICAgKiAgICAgIEwvUi9DID0gMS4wLCBMcy9ScyA9IDEuNDEsIExGRSBleGNsdWRlZCkuCiAgICAgKiAgIDQuIExvdWRuZXNzICBMX0sgPSAtMC42OTEgKyAxMMK3bG9nMTAozqMgR19pwrd6X2kpICAoQW5uZXggMSwgZXEuMiwgcC42KSwKICAgICAqICAgICAgYW5kIGdhdGVkIGludGVncmF0ZWQgbG91ZG5lc3MgdmlhIHRoZSB0d28tc3RhZ2UgZ2F0ZQogICAgICogICAgICAozpNfYSA9IC03MCBMS0ZTIGFic29sdXRlLCDOk19yID0gLTEwIExVIHJlbGF0aXZlOyBBbm5leCAxLCBlcS4zLTcsIHAuNi03KS4KICAgICAqCiAgICAgKiBNb21lbnRhcnkgKDQwMCBtcykgYW5kIHNob3J0LXRlcm0gKDMgcykgd2luZG93cyBhcmUgdGhlIEVCVSBSMTI4IC8gVGVjaCAzMzQxCiAgICAgKiB1bmdhdGVkIGRlcml2YXRpdmVzIGJ1aWx0IGRpcmVjdGx5IG9uIHRoZSBzYW1lIEstd2VpZ2h0ZWQgbWVhbi1zcXVhcmU7IExSQQogICAgICogKGxvdWRuZXNzIHJhbmdlKSBmb2xsb3dzIEVCVSBUZWNoIDMzNDIgKDk1dGgg4oiSIDEwdGggcGVyY2VudGlsZSBvZiBzaG9ydC10ZXJtCiAgICAgKiBsb3VkbmVzcyBhYm92ZSBhIHJlbGF0aXZlIGdhdGUpLgogICAgICoKICAgICAqIFRoaXMgbW9kdWxlIGlzIFBVUkU6IGl0IG9wZXJhdGVzIG9uIGBGbG9hdDMyQXJyYXlgIGNoYW5uZWwgZGF0YSBhbmQgaGFzIG5vCiAgICAgKiBXZWIgQXVkaW8gLyB3b3JrbGV0IC8gZ2xvYmFsIGRlcGVuZGVuY2llcywgc28gdGhlIG1ldGVyaW5nIE1BVEggY2FuIGJlIHVuaXQKICAgICAqIHRlc3RlZCBkaXJlY3RseSAodGhlIHN0YW5kYXJkaXplZC1hdWRpby1jb250ZXh0IG1vY2sgY2FycmllcyBubyBzaWduYWwg4oCUIGl0cwogICAgICogQW5hbHlzZXJOb2RlIGlzIGFuIGVtcHR5IHN0dWIpLiBNaXJyb3JzIHRoZSBjb250ZXh0LWZyZWUtY29yZSB0ZXN0IHBhdHRlcm4gb2YKICAgICAqIGBzcmMvcHJvY2Vzc29ycy9zdGVyZW8tdG8tYmZvcm1hdC1jb3JlLnRzYC4KICAgICAqCiAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lml0dS5pbnQvcmVjL1ItUkVDLUJTLjE3NzAtNS0yMDIzMTEtSS9lbgogICAgICovCiAgICAvKioKICAgICAqIFBlci1jaGFubmVsIHdlaWdodGluZyBHX2kg4oCUIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBUYWJsZSAzIChwLjcpLgogICAgICogRnJvbnQvY2VudHJlIGNoYW5uZWxzIGFyZSB1bml0eTsgdGhlIHR3byBzdXJyb3VuZCBjaGFubmVscyBhcmUgYm9vc3RlZCBieQogICAgICogKzEuNSBkQiAoRyA9IDEuNDEpOyB0aGUgTEZFIGNoYW5uZWwgaXMgbm90IG1lYXN1cmVkICh3ZWlnaHQgMCAvIGV4Y2x1ZGVkKS4KICAgICAqLwogICAgY29uc3QgQ0hBTk5FTF9XRUlHSFRTID0gewogICAgICAgIEw6IDEuMCwKICAgICAgICBSOiAxLjAsCiAgICAgICAgQzogMS4wLAogICAgICAgIExzOiAxLjQxLAogICAgICAgIFJzOiAxLjQxLAogICAgICAgIExGRTogMCwgLy8gZXhjbHVkZWQg4oCUIHNlZSBUYWJsZSAzLCBwLjcKICAgIH07CiAgICAvKioKICAgICAqIFRoZSBLLXdlaWdodGluZyBjYWxpYnJhdGlvbiBjb25zdGFudCBvZiBJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSwgZXEuMiAocC42KS4KICAgICAqIENob3NlbiBzbyB0aGUgSy13ZWlnaHRpbmcgZmlsdGVyJ3MgZ2FpbiBhdCB0aGUgOTk3IEh6IHJlZmVyZW5jZSBmcmVxdWVuY3kgaXMKICAgICAqIGNhbmNlbGxlZCwgbWFraW5nIGEgMCBkQkZTIDk5NyBIeiBzaW5lIG9uIEwvQy9SIHJlYWQgZXhhY3RseSAtMy4wMSBMS0ZTIChwLjcpLgogICAgICovCiAgICBjb25zdCBMT1VETkVTU19PRkZTRVQgPSAtMC42OTE7CiAgICAvKiogQWJzb2x1dGUgZ2F0aW5nIHRocmVzaG9sZCDOk19hLCBJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSwgZXEuNiAocC42KS4gKi8KICAgIGNvbnN0IEFCU09MVVRFX0dBVEVfTEtGUyA9IC03MDsKICAgIC8qKiBSZWxhdGl2ZSBnYXRlIG9mZnNldCAoTFUgYmVsb3cgdGhlIGFic29sdXRlLWdhdGVkIG1lYW4pLCBBbm5leCAxIGVxLjYgKHAuNikuICovCiAgICBjb25zdCBSRUxBVElWRV9HQVRFX09GRlNFVF9MVSA9IC0xMDsKICAgIC8qKiBHYXRpbmcgYmxvY2sgbGVuZ3RoIFRfZywgSVRVLVIgQlMuMTc3MC01IEFubmV4IDEgKHAuNik6IDQwMCBtcy4gKi8KICAgIGNvbnN0IEdBVElOR19CTE9DS19TRUNPTkRTID0gMC40OwogICAgLyoqIEdhdGluZyBibG9jayBvdmVybGFwLCBBbm5leCAxIChwLjYpOiA3NSUg4oaSIHN0ZXAgPSAwLjI1IOKGkiAxMDAgbXMgaG9wLiAqLwogICAgY29uc3QgR0FUSU5HX09WRVJMQVAgPSAwLjc1OwogICAgLyoqIE1vbWVudGFyeSB3aW5kb3cgKEVCVSBUZWNoIDMzNDEpOiA0MDAgbXMsIHVuZ2F0ZWQuICovCiAgICBjb25zdCBNT01FTlRBUllfU0VDT05EUyA9IDAuNDsKICAgIC8qKiBTaG9ydC10ZXJtIHdpbmRvdyAoRUJVIFRlY2ggMzM0MSk6IDMgcywgdW5nYXRlZC4gKi8KICAgIGNvbnN0IFNIT1JUX1RFUk1fU0VDT05EUyA9IDMuMDsKICAgIC8qKgogICAgICogU3RhZ2UgMSBvZiB0aGUgSy13ZWlnaHRpbmcgZmlsdGVyIOKAlCBzZWNvbmQtb3JkZXIgaGlnaC1zaGVsZiBtb2RlbGxpbmcgdGhlCiAgICAgKiBhY291c3RpYyBlZmZlY3Qgb2YgdGhlIGhlYWQgYXMgYSByaWdpZCBzcGhlcmUuIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLAogICAgICogVGFibGUgMSAocC40KS4gVkVSQkFUSU0gY29lZmZpY2llbnRzLCBzcGVjaWZpZWQgYXQgYSA0OCBrSHogc2FtcGxlIHJhdGUuCiAgICAgKi8KICAgIGNvbnN0IEtfV0VJR0hUSU5HX1NUQUdFMV80OEsgPSB7CiAgICAgICAgYjA6IDEuNTM1MTI0ODU5NTg2OTcsCiAgICAgICAgYjE6IC0yLjY5MTY5NjE4OTQwNjM4LAogICAgICAgIGIyOiAxLjE5ODM5MjgxMDg1Mjg1LAogICAgICAgIGExOiAtMS42OTA2NTkyOTMxODI0MSwKICAgICAgICBhMjogMC43MzI0ODA3NzQyMTU4NSwKICAgIH07CiAgICAvKioKICAgICAqIFN0YWdlIDIgb2YgdGhlIEstd2VpZ2h0aW5nIGZpbHRlciDigJQgc2Vjb25kLW9yZGVyIFJMQiBoaWdoLXBhc3MuIElUVS1SCiAgICAgKiBCUy4xNzcwLTUgQW5uZXggMSwgVGFibGUgMiAocC41KS4gVkVSQkFUSU0gY29lZmZpY2llbnRzLCBhdCA0OCBrSHouCiAgICAgKi8KICAgIGNvbnN0IEtfV0VJR0hUSU5HX1NUQUdFMl80OEsgPSB7CiAgICAgICAgYjA6IDEuMCwKICAgICAgICBiMTogLTIsCiAgICAgICAgYjI6IDEuMCwKICAgICAgICBhMTogLTEuOTkwMDQ3NDU0ODMzOTgsCiAgICAgICAgYTI6IDAuOTkwMDcyMjUwMzY2MjEsCiAgICB9OwogICAgLyoqIFNhbXBsZSByYXRlIHRoZSB2ZXJiYXRpbSBUYWJsZSAxLzIgY29lZmZpY2llbnRzIGFyZSBzcGVjaWZpZWQgYXQgKHAuNSkuICovCiAgICBjb25zdCBSRUZFUkVOQ0VfU0FNUExFX1JBVEUgPSA0OF8wMDA7CiAgICAvKioKICAgICAqIERpcmVjdC1Gb3JtLUlJIHRyYW5zcG9zZWQgYmlxdWFkLiBUaGUgSy13ZWlnaHRpbmcgc3RhZ2VzIGFyZSBhcHBsaWVkIGFzIGEKICAgICAqIGNhc2NhZGUgb2YgdHdvIG9mIHRoZXNlIChBbm5leCAxLCBGaWdzIDMtNCkuIFN0YXRlZnVsIGFjcm9zcyBgcHJvY2Vzc2AgY2FsbHMKICAgICAqIHNvIGEgc3RyZWFtaW5nIGNhbGxlciBjYW4gZmVlZCBzdWNjZXNzaXZlIGJsb2NrcyB3aXRob3V0IHJlc2V0dGluZyBJSVIgbWVtb3J5LgogICAgICovCiAgICBjbGFzcyBCaXF1YWQgewogICAgICAgIGIwOwogICAgICAgIGIxOwogICAgICAgIGIyOwogICAgICAgIGExOwogICAgICAgIGEyOwogICAgICAgIC8vIFRyYW5zcG9zZWQgRGlyZWN0IEZvcm0gSUkgc3RhdGUuCiAgICAgICAgejEgPSAwOwogICAgICAgIHoyID0gMDsKICAgICAgICBjb25zdHJ1Y3Rvcihjb2VmZnMpIHsKICAgICAgICAgICAgdGhpcy5iMCA9IGNvZWZmcy5iMDsKICAgICAgICAgICAgdGhpcy5iMSA9IGNvZWZmcy5iMTsKICAgICAgICAgICAgdGhpcy5iMiA9IGNvZWZmcy5iMjsKICAgICAgICAgICAgdGhpcy5hMSA9IGNvZWZmcy5hMTsKICAgICAgICAgICAgdGhpcy5hMiA9IGNvZWZmcy5hMjsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSB0aGlzLmIwICogaW5wdXQgKyB0aGlzLnoxOwogICAgICAgICAgICB0aGlzLnoxID0gdGhpcy5iMSAqIGlucHV0IC0gdGhpcy5hMSAqIG91dHB1dCArIHRoaXMuejI7CiAgICAgICAgICAgIHRoaXMuejIgPSB0aGlzLmIyICogaW5wdXQgLSB0aGlzLmEyICogb3V0cHV0OwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy56MSA9IDA7CiAgICAgICAgICAgIHRoaXMuejIgPSAwOwogICAgICAgIH0KICAgIH0KICAgIC8qKgogICAgICogUmUtZGVyaXZlcyB0aGUgSy13ZWlnaHRpbmcgY29lZmZpY2llbnRzIGZvciBhIHNhbXBsZSByYXRlIG90aGVyIHRoYW4gdGhlCiAgICAgKiA0OCBrSHogdGhlIFRhYmxlIDEvMiB2YWx1ZXMgYXJlIHF1b3RlZCBhdCwgc28gdGhlIGRpZ2l0YWwgZmlsdGVyIGtlZXBzIHRoZQogICAgICogU0FNRSBmcmVxdWVuY3kgcmVzcG9uc2UgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBwLjU6ICJPdGhlciBzYW1wbGUgcmF0ZXMKICAgICAqIHJlcXVpcmUgcmVjb21wdXRlZCBjb2VmZmljaWVudHMgZ2l2aW5nIHRoZSBzYW1lIGZyZXF1ZW5jeSByZXNwb25zZSIpLgogICAgICoKICAgICAqIE1ldGhvZDogcmVhZCB0aGUgYW5hbG9nIHByb3RvdHlwZSBwb2xlcy96ZXJvcyBiYWNrIG91dCBvZiB0aGUgNDgga0h6IGRpZ2l0YWwKICAgICAqIGNvZWZmaWNpZW50cyB3aXRoIHRoZSBJTlZFUlNFIGJpbGluZWFyIHRyYW5zZm9ybSAobWF0Y2hlZCBhdCB0aGUgcmVmZXJlbmNlCiAgICAgKiByYXRlKSwgdGhlbiByZS1hcHBseSB0aGUgYmlsaW5lYXIgdHJhbnNmb3JtIGF0IHRoZSB0YXJnZXQgcmF0ZS4gQXQgNDgga0h6CiAgICAgKiB0aGlzIGlzIHRoZSBpZGVudGl0eSBhbmQgcmV0dXJucyB0aGUgdmVyYmF0aW0gY29lZmZpY2llbnRzIHVuY2hhbmdlZC4KICAgICAqLwogICAgZnVuY3Rpb24ga1dlaWdodGluZ0NvZWZmaWNpZW50cyhzYW1wbGVSYXRlLCByZWZlcmVuY2UpIHsKICAgICAgICBpZiAoc2FtcGxlUmF0ZSA9PT0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFKSB7CiAgICAgICAgICAgIHJldHVybiB7IC4uLnJlZmVyZW5jZSB9OwogICAgICAgIH0KICAgICAgICAvLyBCaWxpbmVhciB0cmFuc2Zvcm06IHMgPSBLwrcoMSAtIHpeLTEpLygxICsgel4tMSksIHdpdGggSyA9IDLCt2ZzICh0aGUgZmFjdG9yCiAgICAgICAgLy8gaXMgYWJzb3JiZWQgYnkgdGhlIGFuYWxvZyBjb2VmZmljaWVudHMsIHNvIGFueSBjb25zaXN0ZW50IEsgd29ya3MgYXMgbG9uZwogICAgICAgIC8vIGFzIHRoZSBzYW1lIEsgaXMgdXNlZCBmb3IgdGhlIGludmVyc2UgYW5kIGZvcndhcmQgdHJhbnNmb3JtKS4gUmVjb3ZlciB0aGUKICAgICAgICAvLyBjb250aW51b3VzLXRpbWUgYmlxdWFkIChhbmFsb2cgYi9hIGluIHBvd2VycyBvZiBzKSBmcm9tIHRoZSByZWZlcmVuY2UKICAgICAgICAvLyBkaWdpdGFsIGNvZWZmaWNpZW50cyBhdCBmc19yZWYsIHRoZW4gcmUtZGlzY3JldGlzZSBhdCB0aGUgdGFyZ2V0IGZzLgogICAgICAgIGNvbnN0IGZzUmVmID0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFOwogICAgICAgIGNvbnN0IGtSZWYgPSAyICogZnNSZWY7CiAgICAgICAgLy8gRGlnaXRhbCDihpIgYW5hbG9nIChpbnZlcnNlIGJpbGluZWFyKS4gRm9yCiAgICAgICAgLy8gICBIKHopID0gKGIwICsgYjEgel4tMSArIGIyIHpeLTIpLygxICsgYTEgel4tMSArIGEyIHpeLTIpCiAgICAgICAgLy8gc3Vic3RpdHV0ZSB6Xi0xID0gKHAgLSBzKS8ocCArIHMpIChwID0ga1JlZiA9IDLCt2ZzX3JlZikgYW5kIG11bHRpcGx5CiAgICAgICAgLy8gbnVtZXJhdG9yIGFuZCBkZW5vbWluYXRvciBieSAocCArIHMpXjIsIGNvbGxlY3RpbmcgcG93ZXJzIG9mIHMuIFRoZQogICAgICAgIC8vIHJlc3VsdGluZyBhbmFsb2cgbnVtL2Rlbm9tIGNvZWZmaWNpZW50cyAoQjIgc14yICsgQjEgcyArIEIwKSAvCiAgICAgICAgLy8gKEEyIHNeMiArIEExIHMgKyBBMCkga2VlcCBDT05TSVNURU5UIHJlbGF0aXZlIHNjYWxpbmcgYmV0d2VlbiBudW0gJiBkZW5vbSwKICAgICAgICAvLyB3aGljaCB0aGUgZm9yd2FyZCB0cmFuc2Zvcm0gYmVsb3cgdGhlbiByZS1kaXNjcmV0aXNlcyBhdCB0aGUgdGFyZ2V0IHJhdGUuCiAgICAgICAgY29uc3QgcCA9IGtSZWY7CiAgICAgICAgY29uc3QgeyBiMCwgYjEsIGIyLCBhMSwgYTIgfSA9IHJlZmVyZW5jZTsKICAgICAgICAvLyBOdW1lcmF0b3IgYW5hbG9nIGNvZWZmcyAoQjIgc14yICsgQjEgcyArIEIwKToKICAgICAgICBjb25zdCBCMiA9IGIwIC0gYjEgKyBiMjsKICAgICAgICBjb25zdCBCMSA9IDIgKiAoYjAgLSBiMikgKiBwOwogICAgICAgIGNvbnN0IEIwID0gKGIwICsgYjEgKyBiMikgKiBwICogcDsKICAgICAgICAvLyBEZW5vbWluYXRvciBhbmFsb2cgY29lZmZzIChBMiBzXjIgKyBBMSBzICsgQTApOgogICAgICAgIGNvbnN0IEEyID0gMSAtIGExICsgYTI7CiAgICAgICAgY29uc3QgQTEgPSAyICogKDEgLSBhMikgKiBwOwogICAgICAgIGNvbnN0IEEwID0gKDEgKyBhMSArIGEyKSAqIHAgKiBwOwogICAgICAgIC8vIEFuYWxvZyDihpIgZGlnaXRhbCAoZm9yd2FyZCBiaWxpbmVhcikgYXQgdGhlIHRhcmdldCByYXRlLiBzID0ga1TCtygxLXpeLTEpLygxK3peLTEpLgogICAgICAgIGNvbnN0IGtUID0gMiAqIHNhbXBsZVJhdGU7CiAgICAgICAgY29uc3Qga1QyID0ga1QgKiBrVDsKICAgICAgICAvLyBFdmFsdWF0ZSBudW1lcmF0b3IvZGVub21pbmF0b3IgcG9seW5vbWlhbHMgYXQgcyA9IGtUwrcoMS16Xi0xKS8oMSt6Xi0xKSBhbmQKICAgICAgICAvLyBtdWx0aXBseSB0aHJvdWdoIGJ5ICgxK3peLTEpXjI7IGNvbGxlY3QgZGlnaXRhbCBjb2VmZmljaWVudHMuCiAgICAgICAgY29uc3QgbmQwID0gQjIgKiBrVDIgKyBCMSAqIGtUICsgQjA7CiAgICAgICAgY29uc3QgbmQxID0gMiAqIChCMCAtIEIyICoga1QyKTsKICAgICAgICBjb25zdCBuZDIgPSBCMiAqIGtUMiAtIEIxICoga1QgKyBCMDsKICAgICAgICBjb25zdCBkZDAgPSBBMiAqIGtUMiArIEExICoga1QgKyBBMDsKICAgICAgICBjb25zdCBkZDEgPSAyICogKEEwIC0gQTIgKiBrVDIpOwogICAgICAgIGNvbnN0IGRkMiA9IEEyICoga1QyIC0gQTEgKiBrVCArIEEwOwogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIGIwOiBuZDAgLyBkZDAsCiAgICAgICAgICAgIGIxOiBuZDEgLyBkZDAsCiAgICAgICAgICAgIGIyOiBuZDIgLyBkZDAsCiAgICAgICAgICAgIGExOiBkZDEgLyBkZDAsCiAgICAgICAgICAgIGEyOiBkZDIgLyBkZDAsCiAgICAgICAgfTsKICAgIH0KICAgIC8qKgogICAgICogVGhlIHR3by1zdGFnZSBLLXdlaWdodGluZyBwcmUtZmlsdGVyIGZvciBPTkUgY2hhbm5lbCAoQW5uZXggMSwgwqcyKS4gSG9sZHMgdGhlCiAgICAgKiBjYXNjYWRlIG9mIHRoZSBUYWJsZSAxIGhpZ2gtc2hlbGYgZm9sbG93ZWQgYnkgdGhlIFRhYmxlIDIgUkxCIGhpZ2gtcGFzcyBhbmQKICAgICAqIHByZXNlcnZlcyBJSVIgc3RhdGUgYWNyb3NzIGJsb2Nrcy4KICAgICAqLwogICAgY2xhc3MgS1dlaWdodGluZ0ZpbHRlciB7CiAgICAgICAgc3RhZ2UxOwogICAgICAgIHN0YWdlMjsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlID0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFKSB7CiAgICAgICAgICAgIHRoaXMuc3RhZ2UxID0gbmV3IEJpcXVhZChrV2VpZ2h0aW5nQ29lZmZpY2llbnRzKHNhbXBsZVJhdGUsIEtfV0VJR0hUSU5HX1NUQUdFMV80OEspKTsKICAgICAgICAgICAgdGhpcy5zdGFnZTIgPSBuZXcgQmlxdWFkKGtXZWlnaHRpbmdDb2VmZmljaWVudHMoc2FtcGxlUmF0ZSwgS19XRUlHSFRJTkdfU1RBR0UyXzQ4SykpOwogICAgICAgIH0KICAgICAgICAvKiogQXBwbHkgYm90aCBLLXdlaWdodGluZyBzdGFnZXMgdG8gb25lIHNhbXBsZSAoQW5uZXggMSwgRmlncyAzLTQpLiAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RhZ2UyLnByb2Nlc3ModGhpcy5zdGFnZTEucHJvY2VzcyhpbnB1dCkpOwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy5zdGFnZTEucmVzZXQoKTsKICAgICAgICAgICAgdGhpcy5zdGFnZTIucmVzZXQoKTsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIENvbnZlcnRzIGEgY2hhbm5lbC13ZWlnaHRlZCBtZWFuLXNxdWFyZSBwb3dlciBzdW0gzqMgR19pwrd6X2kgdG8gbG91ZG5lc3MgaW4KICAgICAqIExLRlMgdmlhIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBlcS4yIChwLjYpOiBMX0sgPSAtMC42OTEgKyAxMMK3bG9nMTAozqMpLgogICAgICogUmV0dXJucyAtSW5maW5pdHkgZm9yIGEgbm9uLXBvc2l0aXZlIHN1bSAoZGlnaXRhbCBzaWxlbmNlKS4KICAgICAqLwogICAgZnVuY3Rpb24gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkUG93ZXJTdW0pIHsKICAgICAgICBpZiAod2VpZ2h0ZWRQb3dlclN1bSA8PSAwKSB7CiAgICAgICAgICAgIHJldHVybiAtSW5maW5pdHk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBMT1VETkVTU19PRkZTRVQgKyAxMCAqIE1hdGgubG9nMTAod2VpZ2h0ZWRQb3dlclN1bSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBJVFUtUiBCUy4xNzcwLTUgdHJ1ZS1wZWFrIG1ldGVyaW5nIOKAlCBjb250ZXh0LWZyZWUgRFNQIGNvcmUuCiAgICAgKgogICAgICogSW1wbGVtZW50cyB0aGUgaW50ZXItc2FtcGxlICh0cnVlLXBlYWspIGxldmVsIGVzdGltYXRvciBvZiBSZWNvbW1lbmRhdGlvbgogICAgICogSVRVLVIgQlMuMTc3MC01ICgxMS8yMDIzKSwgQW5uZXggMiAocC4xOC0xOSkuIFRoZSB0cnVlIHBlYWsgb2YgYSBzaWduYWwgaXMKICAgICAqIHRoZSBtYXhpbXVtIGFic29sdXRlIHZhbHVlIG9mIHRoZSByZWNvbnN0cnVjdGVkIENPTlRJTlVPVVMtdGltZSB3YXZlZm9ybSwKICAgICAqIHdoaWNoIGNhbiBleGNlZWQgdGhlIGxhcmdlc3QgZGlzY3JldGUgc2FtcGxlIOKAlCBhIHBlYWstc2FtcGxlIG1ldGVyIG1pc3NlcyBpdC4KICAgICAqCiAgICAgKiBQZXItY2hhbm5lbCBwcm9jZXNzaW5nIHN0YWdlcyAoQW5uZXggMiwgRmlnLiBwLjE4KToKICAgICAqICAgMS4gQXR0ZW51YXRlIGJ5IDEyLjA0IGRCIChhIDItYml0IHJpZ2h0IHNoaWZ0OyBpbnRlZ2VyIGhlYWRyb29tKS4gU0tJUFBFRAogICAgICogICAgICBoZXJlIOKAlCB0aGlzIGlzIGEgZmxvYXRpbmctcG9pbnQgaW1wbGVtZW50YXRpb24sIGFuZCB0aGUgcmVjb21tZW5kYXRpb24KICAgICAqICAgICAgc3RhdGVzIHRoZSBhdHRlbnVhdGUvcmVzdG9yZSBwYWlyIGlzIGZvciBpbnRlZ2VyIGFyaXRobWV0aWMgb25seSAocC4xOCkuCiAgICAgKiAgIDIuIOKJpTTDlyBvdmVyLXNhbXBsZSAoNDgga0h6IOKGkiAxOTIga0h6KS4gQSBjb21wbGlhbnQgbWV0ZXIgbXVzdCBvdmVyc2FtcGxlIHRvCiAgICAgKiAgICAgIGF0IGxlYXN0IDE5MiBrSHogKHAuMTgtMTkpLiBUaGlzIGNvcmUgZG9lcyA0w5cgdmlhIHRoZSBwb2x5cGhhc2UgRklSLgogICAgICogICAzLiBMb3ctcGFzcyBpbnRlcnBvbGF0aW9uIGZpbHRlciDigJQgdGhlIDQ4LXRhcCwgNC1waGFzZSBwb2x5cGhhc2UgRklSIHdob3NlCiAgICAgKiAgICAgIGNvZWZmaWNpZW50cyBhcmUgZ2l2ZW4gdmVyYmF0aW0gYmVsb3cgKEFubmV4IDIsIHAuMTgtMTkpLgogICAgICogICA0LiBUYWtlIHRoZSBhYnNvbHV0ZSB2YWx1ZSAocmVjdGlmeSkuCiAgICAgKiAgIDUuIENvbnZlcnQgdG8gZEIgVFA6IDIwwrdsb2cxMCh2YWx1ZSkgW3RoZW4gKzEyLjA0IGRCIHRvIHVuZG8gc3RhZ2UgMSwgd2hpY2gKICAgICAqICAgICAgaXMgYSBuby1vcCBoZXJlIHNpbmNlIHN0YWdlIDEgaXMgc2tpcHBlZF0uCiAgICAgKgogICAgICogUHVyZTogb3BlcmF0ZXMgb24gYEZsb2F0MzJBcnJheWAgYW5kIGhhcyBubyBXZWIgQXVkaW8gLyB3b3JrbGV0IGRlcGVuZGVuY2llcy4KICAgICAqCiAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lml0dS5pbnQvcmVjL1ItUkVDLUJTLjE3NzAtNS0yMDIzMTEtSS9lbgogICAgICovCiAgICAvKioKICAgICAqIFRoZSA0w5cgb3ZlcnNhbXBsaW5nIGludGVycG9sYXRpb24gRklSIG9mIElUVS1SIEJTLjE3NzAtNSBBbm5leCAyIChwLjE4LTE5KToKICAgICAqIG9yZGVyLTQ4LCBnaXZlbiBhcyBhIDQtcGhhc2UgcG9seXBoYXNlIGZpbHRlciAoZWFjaCBwaGFzZSBhIDEyLXRhcCBzdWJmaWx0ZXIsCiAgICAgKiA0IMOXIDEyID0gNDggdGFwcykuIFZFUkJBVElNIGNvZWZmaWNpZW50cy4gVGhlIHBoYXNlcyBhcmUgbWlycm9yLXN5bW1ldHJpYwogICAgICogKHBoYXNlIDMgPSBwaGFzZSAwIHJldmVyc2VkLCBwaGFzZSAyID0gcGhhc2UgMSByZXZlcnNlZCkg4oCUIHRoZSBzdGFuZGFyZAogICAgICogbGluZWFyLXBoYXNlIHBvbHlwaGFzZSBzdHJ1Y3R1cmUuCiAgICAgKgogICAgICogUGhhc2UgayBwcm9kdWNlcyB0aGUgb3ZlcnNhbXBsZWQgb3V0cHV0IHNhbXBsZSBhdCBmcmFjdGlvbmFsIHBvc2l0aW9uIGsvNAogICAgICogYmV0d2VlbiBpbnB1dCBzYW1wbGVzIChwaGFzZSAwIOKJiCB0aGUgaW5wdXQgc2FtcGxlIGl0c2VsZikuCiAgICAgKi8KICAgIGNvbnN0IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4SyA9IFsKICAgICAgICAvLyBQaGFzZSAwICh0YXAwLi50YXAxMSkKICAgICAgICBbCiAgICAgICAgICAgIDAuMDAxNzA4OTg0Mzc1LCAwLjAxMDk4NjMyODEyNSwgLTAuMDE5NjUzMzIwMzEyNSwgMC4wMzMyMDMxMjUsIC0wLjA1OTQ0ODI0MjE4NzUsIDAuMTM3MzI5MTAxNTYyNSwgMC45NzIxNjc5Njg3NSwKICAgICAgICAgICAgLTAuMTAyMjk0OTIxODc1LCAwLjA0NzYwNzQyMTg3NSwgLTAuMDI2NjExMzI4MTI1LCAwLjAxNDg5MjU3ODEyNSwgLTAuMDA4MzAwNzgxMjUsCiAgICAgICAgXSwKICAgICAgICAvLyBQaGFzZSAxICh0YXAwLi50YXAxMSkKICAgICAgICBbCiAgICAgICAgICAgIC0wLjAyOTE3NDgwNDY4NzUsIDAuMDI5Mjk2ODc1LCAtMC4wNTE3NTc4MTI1LCAwLjA4OTExMTMyODEyNSwgLTAuMTY2NTAzOTA2MjUsIDAuNDY1MDg3ODkwNjI1LCAwLjc3OTc4NTE1NjI1LAogICAgICAgICAgICAtMC4yMDAzMTczODI4MTI1LCAwLjEwMTU2MjUsIC0wLjA1ODIyNzUzOTA2MjUsIDAuMDMzMDgxMDU0Njg3NSwgLTAuMDE4OTIwODk4NDM3NSwKICAgICAgICBdLAogICAgICAgIC8vIFBoYXNlIDIgKHRhcDAuLnRhcDExKQogICAgICAgIFsKICAgICAgICAgICAgLTAuMDE4OTIwODk4NDM3NSwgMC4wMzMwODEwNTQ2ODc1LCAtMC4wNTgyMjc1MzkwNjI1LCAwLjEwMTU2MjUsIC0wLjIwMDMxNzM4MjgxMjUsIDAuNzc5Nzg1MTU2MjUsIDAuNDY1MDg3ODkwNjI1LAogICAgICAgICAgICAtMC4xNjY1MDM5MDYyNSwgMC4wODkxMTEzMjgxMjUsIC0wLjA1MTc1NzgxMjUsIDAuMDI5Mjk2ODc1LCAtMC4wMjkxNzQ4MDQ2ODc1LAogICAgICAgIF0sCiAgICAgICAgLy8gUGhhc2UgMyAodGFwMC4udGFwMTEpCiAgICAgICAgWwogICAgICAgICAgICAtMC4wMDgzMDA3ODEyNSwgMC4wMTQ4OTI1NzgxMjUsIC0wLjAyNjYxMTMyODEyNSwgMC4wNDc2MDc0MjE4NzUsIC0wLjEwMjI5NDkyMTg3NSwgMC45NzIxNjc5Njg3NSwgMC4xMzczMjkxMDE1NjI1LAogICAgICAgICAgICAtMC4wNTk0NDgyNDIxODc1LCAwLjAzMzIwMzEyNSwgLTAuMDE5NjUzMzIwMzEyNSwgMC4wMTA5ODYzMjgxMjUsIDAuMDAxNzA4OTg0Mzc1LAogICAgICAgIF0sCiAgICBdOwogICAgLyoqIE92ZXJzYW1wbGluZyByYXRpbyBvZiB0aGUgdmVyYmF0aW0gQW5uZXggMiBGSVIgKDTDlykuICovCiAgICBjb25zdCBUUlVFX1BFQUtfT1ZFUlNBTVBMRSA9IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4Sy5sZW5ndGg7CiAgICBjb25zdCBGSVJfVEFQUyA9IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4S1swXS5sZW5ndGg7CiAgICAvKioKICAgICAqIEJTLjE3NzAtNSBBbm5leCAyIHJlcXVpcmVzIHRoZSBvdmVyc2FtcGxlZCByYXRlIHRvIGJlICoqYXQgbGVhc3QgMTkyIGtIeioqCiAgICAgKiAocC4xODogIlRoZSA0w5cgb3Zlci1zYW1wbGluZyBmaWx0ZXIgaW5jcmVhc2VzIHRoZSBzYW1wbGluZyByYXRlIG9mIHRoZSBzaWduYWwKICAgICAqIGZyb20gNDgga0h6IHRvIDE5MiBrSHoiKS4gVGhlIHZlcmJhdGltIEZJUidzIDTDlyBvbmx5IHJlYWNoZXMgdGhhdCBhdCDiiaU0OCBrSHo7CiAgICAgKiBhdCA0NC4xIGtIeiwgNMOXID0gMTc2LjQga0h6LCBCRUxPVyB0aGUgcmVxdWlyZW1lbnQuCiAgICAgKi8KICAgIGNvbnN0IFRSVUVfUEVBS19NSU5fT1ZFUlNBTVBMRURfUkFURSA9IDE5Ml8wMDA7CiAgICAvKioKICAgICAqIENob29zZXMgdGhlIGludGVnZXIgb3ZlcnNhbXBsaW5nIGZhY3RvciBmb3IgYSBnaXZlbiBpbnB1dCBzYW1wbGUgcmF0ZSBzbyB0aGUKICAgICAqIG92ZXJzYW1wbGVkIHJhdGUgcmVhY2hlcyBCUy4xNzcwLTUncyDiiaUxOTIga0h6IHJlcXVpcmVtZW50IChBbm5leCAyLCBwLjE4KS4gVGhlCiAgICAgKiBmYWN0b3IgaXMgbmV2ZXIgYmVsb3cgNCAodGhlIHZlcmJhdGltIEZJUidzIGRlc2lnbiByYXRpbyksIGFuZCBhdCBzYW1wbGUgcmF0ZXMKICAgICAqIHRoZSB2ZXJiYXRpbSA0w5cgYWxyZWFkeSBjb3ZlcnMgKOKJpTQ4IGtIeikgaXQgc3RheXMgNC4gRXhhbXBsZXM6IDQ4IGtIeiDihpIgNMOXCiAgICAgKiAoMTkyIGtIeik7IDQ0LjEga0h6IOKGkiA1w5cgKDIyMC41IGtIeik7IDk2IGtIeiDihpIgNMOXICh0aGUgc3BlYyBub3RlcyAyw5cgd291bGQKICAgICAqIHN1ZmZpY2UsIGJ1dCB0aGUgdmVyYmF0aW0gRklSIGZsb29yIG9mIDTDlyBpcyBrZXB0IGFzIHRoZSBjb25zZXJ2YXRpdmUgZGVmYXVsdCkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIHRydWVQZWFrT3ZlcnNhbXBsZUZhY3RvcihzYW1wbGVSYXRlKSB7CiAgICAgICAgaWYgKCEoc2FtcGxlUmF0ZSA+IDApKSB7CiAgICAgICAgICAgIHJldHVybiBUUlVFX1BFQUtfT1ZFUlNBTVBMRTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIE1hdGgubWF4KFRSVUVfUEVBS19PVkVSU0FNUExFLCBNYXRoLmNlaWwoVFJVRV9QRUFLX01JTl9PVkVSU0FNUExFRF9SQVRFIC8gc2FtcGxlUmF0ZSkpOwogICAgfQogICAgLyoqCiAgICAgKiBCdWlsZHMgYW4gTi1waGFzZSBwb2x5cGhhc2UgaW50ZXJwb2xhdGluZyBGSVIgKGVhY2ggcGhhc2UgYSAxMi10YXAgc3ViZmlsdGVyLAogICAgICogbWF0Y2hpbmcgdGhlIHZlcmJhdGltIGZpbHRlcidzIG9yZGVyKSBmcm9tIGEgSGFubi13aW5kb3dlZC1zaW5jIHByb3RvdHlwZS4gVGhlCiAgICAgKiBCUy4xNzcwLTUgdmVyYmF0aW0gY29lZmZpY2llbnRzIGFyZSAib25lIHNldCAuLi4gdGhhdCB3b3VsZCBzYXRpc2Z5IHRoZQogICAgICogcmVxdWlyZW1lbnRzIiAoQW5uZXggMiwgcC4xOCkgZm9yIHRoZSA0w5cgY2FzZTsgZm9yIHNhbXBsZSByYXRlcyB0aGF0IG5lZWQgYQogICAgICogaGlnaGVyIGZhY3RvciB0byBjbGVhciAxOTIga0h6LCBhbiBlcXVpdmFsZW50IHdpbmRvd2VkLXNpbmMgbG93LXBhc3MKICAgICAqIGludGVycG9sYXRvciBvZiB0aGUgc2FtZSBvcmRlciBpcyBnZW5lcmF0ZWQgaGVyZS4gUGhhc2UgayBzYW1wbGVzIHRoZQogICAgICogY29udGludW91cy10aW1lIHJlY29uc3RydWN0aW9uIGF0IGZyYWN0aW9uYWwgb2Zmc2V0IGsvTiBiZXR3ZWVuIGlucHV0IHNhbXBsZXMuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGJ1aWxkUG9seXBoYXNlRmlyKG92ZXJzYW1wbGUpIHsKICAgICAgICBpZiAob3ZlcnNhbXBsZSA9PT0gVFJVRV9QRUFLX09WRVJTQU1QTEUpIHsKICAgICAgICAgICAgcmV0dXJuIFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4SzsKICAgICAgICB9CiAgICAgICAgY29uc3QgdGFwcyA9IEZJUl9UQVBTOwogICAgICAgIGNvbnN0IGNlbnRlciA9ICh0YXBzIC0gMSkgLyAyOyAvLyBzeW1tZXRyaWMgYWJvdXQgdGhlIG1pZGRsZSB0YXAKICAgICAgICBjb25zdCBwaGFzZXMgPSBbXTsKICAgICAgICBmb3IgKGxldCBwaGFzZSA9IDA7IHBoYXNlIDwgb3ZlcnNhbXBsZTsgcGhhc2UrKykgewogICAgICAgICAgICBjb25zdCBmcmFjID0gcGhhc2UgLyBvdmVyc2FtcGxlOyAvLyBmcmFjdGlvbmFsIHNhbXBsZSBwb3NpdGlvbiAwIOKJpCBmcmFjIDwgMQogICAgICAgICAgICBjb25zdCBjb2VmZnMgPSBuZXcgQXJyYXkodGFwcyk7CiAgICAgICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgdGFwczsgaysrKSB7CiAgICAgICAgICAgICAgICAvLyBJZGVhbCBpbnRlcnBvbGF0aW9uIGtlcm5lbCBzYW1wbGVkIGF0IGRpc3RhbmNlIChrIC0gY2VudGVyIC0gZnJhYyk6CiAgICAgICAgICAgICAgICBjb25zdCB0ID0gayAtIGNlbnRlciAtIGZyYWM7CiAgICAgICAgICAgICAgICBjb25zdCBzaW5jQXJnID0gTWF0aC5QSSAqIHQ7CiAgICAgICAgICAgICAgICBjb25zdCBzaW5jID0gdCA9PT0gMCA/IDEgOiBNYXRoLnNpbihzaW5jQXJnKSAvIHNpbmNBcmc7CiAgICAgICAgICAgICAgICAvLyBIYW5uIHdpbmRvdyBhY3Jvc3MgdGhlIHRhcCBzcGFuIGtlZXBzIHRoZSBGSVIgZmluaXRlICYgbG93LXJpcHBsZS4KICAgICAgICAgICAgICAgIGNvbnN0IHcgPSAwLjUgLSAwLjUgKiBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBrKSAvICh0YXBzIC0gMSkpOwogICAgICAgICAgICAgICAgY29lZmZzW2tdID0gc2luYyAqIHc7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gTm9ybWFsaXplIGVhY2ggcGhhc2UgdG8gdW5pdHkgREMgZ2FpbiBzbyBhIGNvbnN0YW50IHJlY29uc3RydWN0cyBleGFjdGx5LgogICAgICAgICAgICBsZXQgc3VtID0gMDsKICAgICAgICAgICAgZm9yIChjb25zdCBjIG9mIGNvZWZmcykgewogICAgICAgICAgICAgICAgc3VtICs9IGM7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKHN1bSAhPT0gMCkgewogICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCB0YXBzOyBrKyspIHsKICAgICAgICAgICAgICAgICAgICBjb2VmZnNba10gLz0gc3VtOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHBoYXNlcy5wdXNoKGNvZWZmcyk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBwaGFzZXM7CiAgICB9CiAgICAvKioKICAgICAqIFN0cmVhbWluZyA0w5cgcG9seXBoYXNlIHRydWUtcGVhayBkZXRlY3RvciBmb3IgT05FIGNoYW5uZWwgKElUVS1SIEJTLjE3NzAtNQogICAgICogQW5uZXggMikuIE1haW50YWlucyBhIDEyLXNhbXBsZSBpbnB1dCBoaXN0b3J5IHNvIHN1Y2Nlc3NpdmUgYmxvY2tzIGpvaW4KICAgICAqIHNlYW1sZXNzbHksIHJ1bnMgZXZlcnkgcGhhc2Ugb2YgdGhlIHBvbHlwaGFzZSBGSVIgcGVyIGlucHV0IHNhbXBsZSAoZ2l2aW5nCiAgICAgKiB0aGUgNMOXIG92ZXJzYW1wbGVkIHN0cmVhbSksIHJlY3RpZmllcywgYW5kIHRyYWNrcyB0aGUgcnVubmluZyBtYXguCiAgICAgKgogICAgICogVGhlIHZlcmJhdGltIGNvZWZmaWNpZW50cyBhcmUgcXVvdGVkIGF0IDQ4IGtIei4gQXQgaGlnaGVyIGlucHV0IHJhdGVzIGZld2VyCiAgICAgKiBvdmVyc2FtcGxpbmcgcGhhc2VzIGFyZSBuZWVkZWQgZm9yIHRoZSBzYW1lIGFjY3VyYWN5IChBbm5leCAyOiA5NiBrSHog4oaSIDLDlwogICAgICogc3VmZmljZXMpOyB0aGlzIDTDlyBkZXNpZ24gaXMgdGhlIGNvbnNlcnZhdGl2ZSBkZWZhdWx0IGF0IGFueSByYXRlLgogICAgICovCiAgICBjbGFzcyBUcnVlUGVha0RldGVjdG9yIHsKICAgICAgICBoaXN0b3J5OwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIHBlYWsgPSAwOwogICAgICAgIC8qKiBUaGUgcG9seXBoYXNlIEZJUiBhY3R1YWxseSB1c2VkICh2ZXJiYXRpbSBhdCA0w5csIGdlbmVyYXRlZCBmb3IgPjTDlykuICovCiAgICAgICAgZmlyOwogICAgICAgIC8qKiBPdmVyc2FtcGxpbmcgZmFjdG9yIGNob3NlbiBmb3IgdGhlIGlucHV0IHNhbXBsZSByYXRlICjiiaU0OyDiiaUxOTIga0h6KS4gKi8KICAgICAgICBvdmVyc2FtcGxlOwogICAgICAgIC8qKgogICAgICAgICAqIEBwYXJhbSBzYW1wbGVSYXRlIElucHV0IHNhbXBsZSByYXRlIGluIEh6LiBTZWxlY3RzIHRoZSBvdmVyc2FtcGxpbmcgZmFjdG9yCiAgICAgICAgICogICBzbyB0aGUgb3ZlcnNhbXBsZWQgcmF0ZSByZWFjaGVzIEJTLjE3NzAtNSdzIOKJpTE5MiBrSHogcmVxdWlyZW1lbnQgKEFubmV4IDIsCiAgICAgICAgICogICBwLjE4KS4gRGVmYXVsdHMgdG8gNDgga0h6ICh0aGUgcmF0ZSB0aGUgdmVyYmF0aW0gRklSIGlzIHF1b3RlZCBhdCDihpIgNMOXKS4KICAgICAgICAgKi8KICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlID0gNDhfMDAwKSB7CiAgICAgICAgICAgIHRoaXMub3ZlcnNhbXBsZSA9IHRydWVQZWFrT3ZlcnNhbXBsZUZhY3RvcihzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5maXIgPSBidWlsZFBvbHlwaGFzZUZpcih0aGlzLm92ZXJzYW1wbGUpOwogICAgICAgICAgICB0aGlzLmhpc3RvcnkgPSBuZXcgRmxvYXQzMkFycmF5KEZJUl9UQVBTKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHVzaGVzIGEgYmxvY2sgb2YgaW5wdXQgc2FtcGxlcyB0aHJvdWdoIHRoZSBwb2x5cGhhc2UgaW50ZXJwb2xhdG9yIGFuZAogICAgICAgICAqIHVwZGF0ZXMgdGhlIHJ1bm5pbmcgdHJ1ZSBwZWFrIChtYXggYWJzb2x1dGUgb3ZlcnNhbXBsZWQgdmFsdWUpLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3Moc2FtcGxlcykgewogICAgICAgICAgICBmb3IgKGxldCBuID0gMDsgbiA8IHNhbXBsZXMubGVuZ3RoOyBuKyspIHsKICAgICAgICAgICAgICAgIC8vIFNoaWZ0IHRoZSBuZXdlc3Qgc2FtcGxlIGludG8gdGhlIGNpcmN1bGFyIGhpc3RvcnkuCiAgICAgICAgICAgICAgICB0aGlzLmhpc3RvcnlbdGhpcy53cml0ZUluZGV4XSA9IHNhbXBsZXNbbl07CiAgICAgICAgICAgICAgICB0aGlzLndyaXRlSW5kZXggPSAodGhpcy53cml0ZUluZGV4ICsgMSkgJSBGSVJfVEFQUzsKICAgICAgICAgICAgICAgIC8vIEZvciBlYWNoIHBvbHlwaGFzZSBicmFuY2gsIGNvbnZvbHZlIHRoZSAxMi10YXAgaGlzdG9yeS4gdGFwIDAgaXMgdGhlCiAgICAgICAgICAgICAgICAvLyBvbGRlc3Qgc2FtcGxlOyB0aGUgbmV3ZXN0IHNhbXBsZSBpcyBhdCAod3JpdGVJbmRleCAtIDEpLgogICAgICAgICAgICAgICAgZm9yIChsZXQgcGhhc2UgPSAwOyBwaGFzZSA8IHRoaXMuZmlyLmxlbmd0aDsgcGhhc2UrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGNvZWZmcyA9IHRoaXMuZmlyW3BoYXNlXTsKICAgICAgICAgICAgICAgICAgICBsZXQgYWNjID0gMDsKICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IEZJUl9UQVBTOyBrKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgaWR4ID0gKHRoaXMud3JpdGVJbmRleCArIGspICUgRklSX1RBUFM7CiAgICAgICAgICAgICAgICAgICAgICAgIGFjYyArPSBjb2VmZnNba10gKiB0aGlzLmhpc3RvcnlbaWR4XTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgY29uc3QgbWFnID0gTWF0aC5hYnMoYWNjKTsKICAgICAgICAgICAgICAgICAgICBpZiAobWFnID4gdGhpcy5wZWFrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucGVhayA9IG1hZzsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIExpbmVhciAobm90IGRCKSB0cnVlLXBlYWsgbWFnbml0dWRlIG9ic2VydmVkIHNvIGZhci4gKi8KICAgICAgICB0cnVlUGVhaygpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMucGVhazsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogVHJ1ZS1wZWFrIGxldmVsIGluIGRCVFAgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAyIHN0YWdlIDU6IDIwwrdsb2cxMCBvZiB0aGUKICAgICAgICAgKiByZWN0aWZpZWQgb3ZlcnNhbXBsZWQgcGVhaywgcmVsYXRpdmUgdG8gMTAwJSBmdWxsIHNjYWxlKS4gUmV0dXJucyAtSW5maW5pdHkKICAgICAgICAgKiBmb3IgcHVyZSBzaWxlbmNlLgogICAgICAgICAqLwogICAgICAgIHRydWVQZWFrRGIoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnBlYWsgPiAwID8gMjAgKiBNYXRoLmxvZzEwKHRoaXMucGVhaykgOiAtSW5maW5pdHk7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmhpc3RvcnkuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5wZWFrID0gMDsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBMb3VkbmVzcyBtZXRlcmluZyBBdWRpb1dvcmtsZXRQcm9jZXNzb3Ig4oCUIElUVS1SIEJTLjE3NzAtNS4KICAgICAqCiAgICAgKiBBIFBBU1MtVEhST1VHSCBtZXRlcmluZyB0YXA6IGl0IGNvcGllcyBpdHMgaW5wdXQgc3RyYWlnaHQgdG8gaXRzIG91dHB1dAogICAgICogKHNvIGluc2VydGluZyBpdCBuZXZlciBhbHRlcnMgdGhlIGF1ZGlibGUgc2lnbmFsKSB3aGlsZSBtZWFzdXJpbmcgbG91ZG5lc3MKICAgICAqIGluIHJlYWwgdGltZSBhbmQgcG9zdGluZyB0aGUgbGF0ZXN0IHJlYWRpbmdzIGJhY2sgdG8gdGhlIG1haW4gdGhyZWFkIHZpYSB0aGUKICAgICAqIE1lc3NhZ2VQb3J0LgogICAgICoKICAgICAqIFRoZSBtZXRlcmluZyBNQVRIIGxpdmVzIGVudGlyZWx5IGluIHRoZSBjb250ZXh0LWZyZWUgY29yZXMKICAgICAqIGAuLi9tZXRlcnMvbG91ZG5lc3MtY29yZWAgYW5kIGAuLi9tZXRlcnMvdHJ1ZXBlYWstY29yZWAgKHZlcmJhdGltIElUVS1SCiAgICAgKiBCUy4xNzcwLTUgY29lZmZpY2llbnRzIGFuZCBmb3JtdWxhZSksIHdoaWNoIGFyZSB1bml0LXRlc3RlZCBkaXJlY3RseSBvbgogICAgICogYEZsb2F0MzJBcnJheWBzIOKAlCB0aGUgc3RhbmRhcmRpemVkLWF1ZGlvLWNvbnRleHQgbW9jayBjYW5ub3QgY2Fycnkgc2lnbmFsLCBzbwogICAgICogdGhlIHdvcmtsZXQgc2hlbGwgaGVyZSBpcyBpbnRlbnRpb25hbGx5IHRoaW4uIFJvbGx1cCBpbmxpbmVzIHRoZSBpbXBvcnRlZAogICAgICogY29yZXMgaW50byB0aGUgSUlGRSB3b3JrbGV0IGJ1bmRsZS4KICAgICAqCiAgICAgKiBSZXBvcnRlZCB2YWx1ZXM6CiAgICAgKiAgIC0gbW9tZW50YXJ5ICAoTSk6IEstd2VpZ2h0ZWQgbG91ZG5lc3Mgb3ZlciB0aGUgbGFzdCA0MDAgbXMgKEVCVSBUZWNoIDMzNDEpLgogICAgICogICAtIHNob3J0VGVybSAgKFMpOiBLLXdlaWdodGVkIGxvdWRuZXNzIG92ZXIgdGhlIGxhc3QgMyBzIChFQlUgVGVjaCAzMzQxKS4KICAgICAqICAgLSBpbnRlZ3JhdGVkIChJKTogZ2F0ZWQgaW50ZWdyYXRlZCBsb3VkbmVzcyBzaW5jZSB0aGUgbGFzdCByZXNldAogICAgICogICAgIChJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSB0d28tc3RhZ2UgZ2F0ZSwgLTcwIGFicyAvIC0xMCByZWwpLgogICAgICogICAtIHRydWVQZWFrICAoVFApOiBydW5uaW5nIG1heCB0cnVlLXBlYWsgaW4gZEJUUCAoSVRVLVIgQlMuMTc3MC01IEFubmV4IDIsCiAgICAgKiAgICAgNMOXIHBvbHlwaGFzZSBvdmVyc2FtcGxpbmcpLgogICAgICoKICAgICAqIEBzZWUgaHR0cHM6Ly93d3cuaXR1LmludC9yZWMvUi1SRUMtQlMuMTc3MC01LTIwMjMxMS1JL2VuCiAgICAgKi8KICAgIC8qKiBEZWZhdWx0IGNoYW5uZWwgbGFiZWwgYXNzaWdubWVudCBieSBjaGFubmVsIGluZGV4IGZvciDiiaQ1LWNoYW5uZWwgaW5wdXQuICovCiAgICBjb25zdCBERUZBVUxUX0NIQU5ORUxfT1JERVIgPSBbIkwiLCAiUiIsICJDIiwgIkxzIiwgIlJzIl07CiAgICAvKioKICAgICAqIENoYW5uZWwgbGFiZWwgbGF5b3V0IGJ5IGNoYW5uZWwgQ09VTlQuIFR3byBkaXN0aW5jdCBhdXRob3JpdGllcyBhcHBseToKICAgICAqICAtIFRoZSBjaGFubmVsIElOREVYIG9yZGVyIGZvciBlYWNoIGNvdW50IGlzIHRoZSBXM0MgV2ViIEF1ZGlvIEFQSSAxLjAKICAgICAqICAgICJzcGVha2VycyIgbGF5b3V0OiA2LWNoYW5uZWwgNS4xIGlzIFtGTCwgRlIsIEZDLCBMRkUsIFNMLCBTUl0gPSBpbmRleCAzIGlzCiAgICAgKiAgICB0aGUgTEZFIChodHRwczovL3d3dy53My5vcmcvVFIvd2ViYXVkaW8vI0NoYW5uZWxPcmRlcmluZykuCiAgICAgKiAgLSBUaGUgbG91ZG5lc3MgVFJFQVRNRU5UIG9mIHRob3NlIGNoYW5uZWxzIGlzIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxCiAgICAgKiAgICAoVGFibGUgMyAvIEZpZy4xLCBwLjMsNyk6IHRoZSBMRkUgaXMgRVhDTFVERUQgZnJvbSB0aGUgbG91ZG5lc3Mgc3VtCiAgICAgKiAgICAoQ0hBTk5FTF9XRUlHSFRTLkxGRSA9IDApIGFuZCBvbmx5IEwvUi9DL0xzL1JzIGFyZSBtZWFzdXJlZCAoTHMvUnMgYXQgMS40MSkuCiAgICAgKiBUaGUgZmxhdCBERUZBVUxUX0NIQU5ORUxfT1JERVIgYWJvdmUgY2Fubm90IGV4cHJlc3MgdGhlIExGRSBzbG90IOKAlCBpdCB3b3VsZAogICAgICogbWlzbGFiZWwgYSA1LjEgc3RyZWFtJ3MgaW5kZXgtMyBMRkUgYXMgYExzYCBhbmQgY291bnQgaXQgYXMgcHJvZ3JhbW1lCiAgICAgKiBsb3VkbmVzcyDigJQgc28gYSBsYXlvdXQgaXMgY2hvc2VuIGJ5IGNoYW5uZWwgY291bnQgaGVyZS4gQ291bnRzIG5vdCBsaXN0ZWQgZmFsbAogICAgICogYmFjayB0byBERUZBVUxUX0NIQU5ORUxfT1JERVIuCiAgICAgKi8KICAgIGNvbnN0IENIQU5ORUxfTEFZT1VUUyA9IHsKICAgICAgICAxOiBbIkMiXSwgLy8gbW9ubzogb25lIG1lYXN1cmVkIGNoYW5uZWwsIHdlaWdodCAxLjAKICAgICAgICAyOiBbIkwiLCAiUiJdLCAvLyBzdGVyZW8KICAgICAgICAzOiBbIkwiLCAiUiIsICJDIl0sCiAgICAgICAgNTogWyJMIiwgIlIiLCAiQyIsICJMcyIsICJScyJdLCAvLyA1LjAKICAgICAgICA2OiBbIkwiLCAiUiIsICJDIiwgIkxGRSIsICJMcyIsICJScyJdLCAvLyA1LjEg4oCUIExGRSAoaW5kZXggMykgZXhjbHVkZWQKICAgIH07CiAgICAvKioKICAgICAqIE1haW50YWlucyB0aGUgSy13ZWlnaHRlZCBtZWFuLXNxdWFyZSBvZiBvbmUgY2hhbm5lbCBvdmVyIGEgc2xpZGluZyB3aW5kb3csCiAgICAgKiBwbHVzIGEgMTAwIG1zLWhvcCBibG9jayBhY2N1bXVsYXRvciBmb3IgaW50ZWdyYXRlZCBnYXRpbmcuIFB1cmUgbnVtYmVyCiAgICAgKiBjcnVuY2hpbmc7IG9uZSBpbnN0YW5jZSBwZXIgaW5wdXQgY2hhbm5lbC4KICAgICAqLwogICAgY2xhc3MgQ2hhbm5lbE1ldGVyIHsKICAgICAgICBrRmlsdGVyOwogICAgICAgIHRydWVQZWFrOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5rRmlsdGVyID0gbmV3IEtXZWlnaHRpbmdGaWx0ZXIoc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIC8vIFNhbXBsZS1yYXRlLWF3YXJlOiB0aGUgZGV0ZWN0b3IgcGlja3MgYW4gb3ZlcnNhbXBsaW5nIGZhY3RvciByZWFjaGluZwogICAgICAgICAgICAvLyBCUy4xNzcwLTUncyDiiaUxOTIga0h6IHJlcXVpcmVtZW50ICg0w5cgYXQgNDgga0h6LCDiiaU1w5cgYXQgNDQuMSBrSHopLgogICAgICAgICAgICB0aGlzLnRydWVQZWFrID0gbmV3IFRydWVQZWFrRGV0ZWN0b3Ioc2FtcGxlUmF0ZSk7CiAgICAgICAgfQogICAgfQogICAgLyoqCiAgICAgKiBSaW5nIG9mIHJlY2VudCBLLXdlaWdodGVkIHNxdWFyZWQgc2FtcGxlcyBmb3Igb25lIGNoYW5uZWwsIHN1cHBvcnRpbmcgYQogICAgICogc2xpZGluZy13aW5kb3cgbWVhbi1zcXVhcmUgb3ZlciB0aGUgbW9zdCByZWNlbnQgYGNhcGFjaXR5YCBzYW1wbGVzLgogICAgICovCiAgICBjbGFzcyBTbGlkaW5nUG93ZXIgewogICAgICAgIHJpbmc7CiAgICAgICAgaGVhZCA9IDA7CiAgICAgICAgZmlsbGVkID0gMDsKICAgICAgICBzdW0gPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKGNhcGFjaXR5KSB7CiAgICAgICAgICAgIHRoaXMucmluZyA9IG5ldyBGbG9hdDMyQXJyYXkoTWF0aC5tYXgoMSwgY2FwYWNpdHkpKTsKICAgICAgICB9CiAgICAgICAgcHVzaChzcXVhcmVkKSB7CiAgICAgICAgICAgIGlmICh0aGlzLmZpbGxlZCA9PT0gdGhpcy5yaW5nLmxlbmd0aCkgewogICAgICAgICAgICAgICAgdGhpcy5zdW0gLT0gdGhpcy5yaW5nW3RoaXMuaGVhZF07CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICB0aGlzLmZpbGxlZCsrOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMucmluZ1t0aGlzLmhlYWRdID0gc3F1YXJlZDsKICAgICAgICAgICAgdGhpcy5zdW0gKz0gc3F1YXJlZDsKICAgICAgICAgICAgdGhpcy5oZWFkID0gKHRoaXMuaGVhZCArIDEpICUgdGhpcy5yaW5nLmxlbmd0aDsKICAgICAgICB9CiAgICAgICAgbWVhblNxdWFyZSgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuZmlsbGVkID4gMCA/IHRoaXMuc3VtIC8gdGhpcy5maWxsZWQgOiAwOwogICAgICAgIH0KICAgIH0KICAgIGNsYXNzIExvdWRuZXNzTWV0ZXJQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIGNoYW5uZWxNZXRlcnMgPSBbXTsKICAgICAgICBtb21lbnRhcnlXaW5kb3dzID0gW107CiAgICAgICAgc2hvcnRUZXJtV2luZG93cyA9IFtdOwogICAgICAgIC8vIEludGVncmF0ZWQgZ2F0aW5nIHN0YXRlOiBwZXItY2hhbm5lbCBtZWFuLXNxdWFyZSBhY2N1bXVsYXRlZCBwZXIgMTAwIG1zCiAgICAgICAgLy8gYmxvY2sgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAxOyA0MDAgbXMgYmxvY2tzIGF0IDc1JSBvdmVybGFwID09IHN1bSBvZiB0aGUKICAgICAgICAvLyBtb3N0IHJlY2VudCBmb3VyIDEwMCBtcyBzdWItYmxvY2tzKS4gV2UgYWNjdW11bGF0ZSAxMDAgbXMgc3ViLWJsb2NrIHBvd2VyCiAgICAgICAgLy8gYW5kIGZvcm0gNDAwIG1zIGdhdGluZyBibG9ja3MgZnJvbSBhIHNsaWRpbmcgc3VtIG9mIGZvdXIgc3ViLWJsb2Nrcy4KICAgICAgICBzdWJCbG9ja1NhbXBsZXMgPSAwOwogICAgICAgIHNhbXBsZXNJbnRvU3ViQmxvY2sgPSAwOwogICAgICAgIHN1YkJsb2NrU3VtUGVyQ2hhbm5lbCA9IFtdOwogICAgICAgIHJlY2VudFN1YkJsb2NrcyA9IFtdOyAvLyB1cCB0byA0IHN1Yi1ibG9ja3Mgb2YgcGVyLWNoYW5uZWwgc3VtcwogICAgICAgIGdhdGVkQmxvY2tzUGVyQ2hhbm5lbCA9IFtdOyAvLyBzdXJ2aXZpbmctYWJzb2x1dGUtZ2F0ZSBibG9jayBwZXItY2hhbm5lbCBtZWFuLXNxdWFyZXMKICAgICAgICBnYXRlZEJsb2NrTG91ZG5lc3MgPSBbXTsKICAgICAgICBjaGFubmVsQ291bnQgPSAwOwogICAgICAgIHBvc3RDb3VudGVyID0gMDsKICAgICAgICBwb3N0RXZlcnlTYW1wbGVzID0gMDsKICAgICAgICBzYW1wbGVzU2luY2VQb3N0ID0gMDsKICAgICAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CiAgICAgICAgICAgIHN1cGVyKG9wdGlvbnMpOwogICAgICAgICAgICAvLyBQb3N0IHJlYWRpbmdzIH4xMMOXIHBlciBzZWNvbmQuCiAgICAgICAgICAgIHRoaXMucG9zdEV2ZXJ5U2FtcGxlcyA9IE1hdGgucm91bmQoc2FtcGxlUmF0ZSAvIDEwKTsKICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1NhbXBsZXMgPSBNYXRoLnJvdW5kKEdBVElOR19CTE9DS19TRUNPTkRTICogKDEgLSBHQVRJTkdfT1ZFUkxBUCkgKiBzYW1wbGVSYXRlKTsgLy8gMTAwIG1zCiAgICAgICAgICAgIHRoaXMucG9ydC5vbm1lc3NhZ2UgPSAoZXZlbnQpID0+IHsKICAgICAgICAgICAgICAgIGlmIChldmVudC5kYXRhICYmIGV2ZW50LmRhdGEuY29tbWFuZCA9PT0gInJlc2V0IikgewogICAgICAgICAgICAgICAgICAgIHRoaXMucmVzZXQoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgZW5zdXJlQ2hhbm5lbHMoY291bnQpIHsKICAgICAgICAgICAgaWYgKGNvdW50ID09PSB0aGlzLmNoYW5uZWxDb3VudCkgewogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbENvdW50ID0gY291bnQ7CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbE1ldGVycyA9IFtdOwogICAgICAgICAgICB0aGlzLm1vbWVudGFyeVdpbmRvd3MgPSBbXTsKICAgICAgICAgICAgdGhpcy5zaG9ydFRlcm1XaW5kb3dzID0gW107CiAgICAgICAgICAgIGNvbnN0IG1vbWVudGFyeUNhcGFjaXR5ID0gTWF0aC5yb3VuZChNT01FTlRBUllfU0VDT05EUyAqIHNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBzaG9ydFRlcm1DYXBhY2l0eSA9IE1hdGgucm91bmQoU0hPUlRfVEVSTV9TRUNPTkRTICogc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgY291bnQ7IGMrKykgewogICAgICAgICAgICAgICAgdGhpcy5jaGFubmVsTWV0ZXJzLnB1c2gobmV3IENoYW5uZWxNZXRlcihzYW1wbGVSYXRlKSk7CiAgICAgICAgICAgICAgICB0aGlzLm1vbWVudGFyeVdpbmRvd3MucHVzaChuZXcgU2xpZGluZ1Bvd2VyKG1vbWVudGFyeUNhcGFjaXR5KSk7CiAgICAgICAgICAgICAgICB0aGlzLnNob3J0VGVybVdpbmRvd3MucHVzaChuZXcgU2xpZGluZ1Bvd2VyKHNob3J0VGVybUNhcGFjaXR5KSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwgPSBuZXcgQXJyYXkoY291bnQpLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMucmVjZW50U3ViQmxvY2tzID0gW107CiAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsID0gW107CiAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja0xvdWRuZXNzID0gW107CiAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayA9IDA7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICBjb25zdCBjb3VudCA9IHRoaXMuY2hhbm5lbENvdW50OwogICAgICAgICAgICB0aGlzLmNoYW5uZWxDb3VudCA9IDA7CiAgICAgICAgICAgIHRoaXMuZW5zdXJlQ2hhbm5lbHMoY291bnQpOwogICAgICAgICAgICBmb3IgKGNvbnN0IG1ldGVyIG9mIHRoaXMuY2hhbm5lbE1ldGVycykgewogICAgICAgICAgICAgICAgbWV0ZXIua0ZpbHRlci5yZXNldCgpOwogICAgICAgICAgICAgICAgbWV0ZXIudHJ1ZVBlYWsucmVzZXQoKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBjaGFubmVsTGFiZWwoaW5kZXgpIHsKICAgICAgICAgICAgLy8gUGljayB0aGUgbGF5b3V0IGZvciB0aGUgbGl2ZSBjaGFubmVsIGNvdW50IHNvIGEgNS4xIHN0cmVhbSdzIGluZGV4LTMgTEZFCiAgICAgICAgICAgIC8vIGlzIGxhYmVsbGVkIExGRSAod2VpZ2h0IDAsIGV4Y2x1ZGVkKSByYXRoZXIgdGhhbiBtaXNsYWJlbGxlZCBMcy4gQ291bnRzCiAgICAgICAgICAgIC8vIHdpdGhvdXQgYSBkZWZpbmVkIGxheW91dCBmYWxsIGJhY2sgdG8gdGhlIGZsYXQgZGVmYXVsdCBvcmRlci4KICAgICAgICAgICAgY29uc3QgbGF5b3V0ID0gQ0hBTk5FTF9MQVlPVVRTW3RoaXMuY2hhbm5lbENvdW50XSA/PyBERUZBVUxUX0NIQU5ORUxfT1JERVI7CiAgICAgICAgICAgIHJldHVybiBsYXlvdXRbaW5kZXhdID8/ICJDIjsKICAgICAgICB9CiAgICAgICAgLyoqIExvdWRuZXNzIG9mIGEgcGVyLWNoYW5uZWwgbWVhbi1zcXVhcmUgdmVjdG9yIChBbm5leCAxLCBlcXMuMS0yKS4gKi8KICAgICAgICBsb3VkbmVzc09mKG1lYW5TcXVhcmVzUGVyQ2hhbm5lbCkgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG1lYW5TcXVhcmVzUGVyQ2hhbm5lbC5sZW5ndGg7IGMrKykgewogICAgICAgICAgICAgICAgd2VpZ2h0ZWRTdW0gKz0gQ0hBTk5FTF9XRUlHSFRTW3RoaXMuY2hhbm5lbExhYmVsKGMpXSAqIG1lYW5TcXVhcmVzUGVyQ2hhbm5lbFtjXTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkU3VtKTsKICAgICAgICB9CiAgICAgICAgd2luZG93TG91ZG5lc3Mod2luZG93cykgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHdpbmRvd3MubGVuZ3RoOyBjKyspIHsKICAgICAgICAgICAgICAgIHdlaWdodGVkU3VtICs9IENIQU5ORUxfV0VJR0hUU1t0aGlzLmNoYW5uZWxMYWJlbChjKV0gKiB3aW5kb3dzW2NdLm1lYW5TcXVhcmUoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkU3VtKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogQ2xvc2VzIHRoZSBjdXJyZW50IDEwMCBtcyBzdWItYmxvY2sgZXhhY3RseSBhdCBpdHMgYm91bmRhcnk6IGNvbnZlcnRzIHRoZQogICAgICAgICAqIGFjY3VtdWxhdGVkIHBlci1jaGFubmVsIHBvd2VyIHN1bSB0byBhIG1lYW4sIHB1c2hlcyBpdCBvbnRvIHRoZSByaW5nIG9mIHRoZQogICAgICAgICAqIGZvdXIgbW9zdC1yZWNlbnQgc3ViLWJsb2NrcywgZm9ybXMgYSA0MDAgbXMgZ2F0aW5nIGJsb2NrIChlcS4zKSB3aGVuIGZvdXIKICAgICAgICAgKiBzdWItYmxvY2tzIGFyZSBhdmFpbGFibGUsIGFwcGxpZXMgdGhlIGFic29sdXRlIGdhdGUgKGVxLjYpLCB0aGVuIHJlc2V0cyB0aGUKICAgICAgICAgKiBzdWItYmxvY2sgYWNjdW11bGF0b3IuIENhbGxlZCB0aGUgaW5zdGFudCBgc2FtcGxlc0ludG9TdWJCbG9ja2AgcmVhY2hlcyB0aGUKICAgICAgICAgKiBib3VuZGFyeSBzbyBubyBzYW1wbGUgbGFuZHMgaW4gdGhlIHdyb25nIHN1Yi1ibG9jay4KICAgICAgICAgKi8KICAgICAgICBjbG9zZVN1YkJsb2NrKCkgewogICAgICAgICAgICBjb25zdCBzdWJNZWFuID0gdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwubWFwKChzKSA9PiBzIC8gdGhpcy5zdWJCbG9ja1NhbXBsZXMpOwogICAgICAgICAgICB0aGlzLnJlY2VudFN1YkJsb2Nrcy5wdXNoKHN1Yk1lYW4pOwogICAgICAgICAgICBpZiAodGhpcy5yZWNlbnRTdWJCbG9ja3MubGVuZ3RoID4gNCkgewogICAgICAgICAgICAgICAgdGhpcy5yZWNlbnRTdWJCbG9ja3Muc2hpZnQoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBBIGNvbXBsZXRlIDQwMCBtcyBnYXRpbmcgYmxvY2sgZXhpc3RzIG9uY2UgZm91ciBzdWItYmxvY2tzIGFjY3VtdWxhdGUuCiAgICAgICAgICAgIGlmICh0aGlzLnJlY2VudFN1YkJsb2Nrcy5sZW5ndGggPT09IDQpIHsKICAgICAgICAgICAgICAgIGNvbnN0IGJsb2NrTWVhbiA9IG5ldyBBcnJheSh0aGlzLmNoYW5uZWxDb3VudCkuZmlsbCgwKTsKICAgICAgICAgICAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgdGhpcy5jaGFubmVsQ291bnQ7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGxldCBzID0gMDsKICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IHN1YiBvZiB0aGlzLnJlY2VudFN1YkJsb2NrcykgewogICAgICAgICAgICAgICAgICAgICAgICBzICs9IHN1YltjXTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgYmxvY2tNZWFuW2NdID0gcyAvIDQ7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBjb25zdCBsID0gdGhpcy5sb3VkbmVzc09mKGJsb2NrTWVhbik7CiAgICAgICAgICAgICAgICAvLyBBYnNvbHV0ZSBnYXRlIM6TX2EgKC03MCBMS0ZTKTogb25seSBzdG9yZSBwYXNzaW5nIGJsb2NrcyAoQW5uZXggMSBlcS42KS4KICAgICAgICAgICAgICAgIGlmIChsID4gQUJTT0xVVEVfR0FURV9MS0ZTKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5nYXRlZEJsb2Nrc1BlckNoYW5uZWwucHVzaChibG9ja01lYW4pOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja0xvdWRuZXNzLnB1c2gobCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwgPSBuZXcgQXJyYXkodGhpcy5jaGFubmVsQ291bnQpLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayAtPSB0aGlzLnN1YkJsb2NrU2FtcGxlczsKICAgICAgICB9CiAgICAgICAgLyoqIEdhdGVkIGludGVncmF0ZWQgbG91ZG5lc3Mgb3ZlciBzdXJ2aXZpbmcgYmxvY2tzIChBbm5leCAxLCBlcXMuNS03KS4gKi8KICAgICAgICBjb21wdXRlSW50ZWdyYXRlZCgpIHsKICAgICAgICAgICAgaWYgKHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsLmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIC1JbmZpbml0eTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBNZWFuIG9mIGFic29sdXRlLWdhdGVkIHNldCBhbHJlYWR5IGZpbHRlcmVkIG9uIHRoZSBhYnNvbHV0ZSBnYXRlIHdoZW4gdGhlCiAgICAgICAgICAgIC8vIGJsb2NrIHdhcyBzdG9yZWQuIENvbXB1dGUgcmVsYXRpdmUgdGhyZXNob2xkIHRoZW4gcmUtZmlsdGVyLgogICAgICAgICAgICBjb25zdCByZWxhdGl2ZVRocmVzaG9sZCA9IHRoaXMuZ2F0ZWRMb3VkbmVzc09mU2V0KHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsKSArIFJFTEFUSVZFX0dBVEVfT0ZGU0VUX0xVOwogICAgICAgICAgICBjb25zdCBmaW5hbFNldCA9IFtdOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5nYXRlZEJsb2NrTG91ZG5lc3NbaV0gPiByZWxhdGl2ZVRocmVzaG9sZCkgewogICAgICAgICAgICAgICAgICAgIGZpbmFsU2V0LnB1c2godGhpcy5nYXRlZEJsb2Nrc1BlckNoYW5uZWxbaV0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChmaW5hbFNldC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiAtSW5maW5pdHk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2F0ZWRMb3VkbmVzc09mU2V0KGZpbmFsU2V0KTsKICAgICAgICB9CiAgICAgICAgZ2F0ZWRMb3VkbmVzc09mU2V0KHNldCkgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHRoaXMuY2hhbm5lbENvdW50OyBjKyspIHsKICAgICAgICAgICAgICAgIGxldCBtZWFuID0gMDsKICAgICAgICAgICAgICAgIGZvciAoY29uc3QgYmxvY2sgb2Ygc2V0KSB7CiAgICAgICAgICAgICAgICAgICAgbWVhbiArPSBibG9ja1tjXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIG1lYW4gLz0gc2V0Lmxlbmd0aDsKICAgICAgICAgICAgICAgIHdlaWdodGVkU3VtICs9IENIQU5ORUxfV0VJR0hUU1t0aGlzLmNoYW5uZWxMYWJlbChjKV0gKiBtZWFuOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBwb3dlclN1bVRvTG91ZG5lc3Mod2VpZ2h0ZWRTdW0pOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgLy8gTm8gaW5wdXQgY29ubmVjdGVkIHRoaXMgcXVhbnR1bSDigJQga2VlcCB0aGUgcHJvY2Vzc29yIGFsaXZlLgogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5lbnN1cmVDaGFubmVscyhpbnB1dC5sZW5ndGgpOwogICAgICAgICAgICBjb25zdCBmcmFtZUNvdW50ID0gaW5wdXRbMF0/Lmxlbmd0aCA/PyAwOwogICAgICAgICAgICAvLyBUcnVlLXBlYWsgcnVucyBvbiB0aGUgUkFXICh1bi13ZWlnaHRlZCkgY2hhbm5lbCBzaWduYWwgKEFubmV4IDIpOyBpdCBoYXMKICAgICAgICAgICAgLy8gbm8gc3ViLWJsb2NrIGJvdW5kYXJ5IGRlcGVuZGVuY3ksIHNvIHByb2Nlc3Mgd2hvbGUgcXVhbnRhIHBlciBjaGFubmVsLgogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGlucHV0Lmxlbmd0aDsgYysrKSB7CiAgICAgICAgICAgICAgICB0aGlzLmNoYW5uZWxNZXRlcnNbY10udHJ1ZVBlYWsucHJvY2VzcyhpbnB1dFtjXSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gUGFzcy10aHJvdWdoIGNvcHk6IG1ldGVyaW5nIG11c3Qgbm90IGFsdGVyIHRoZSBhdWRpYmxlIHBhdGguIERvbmUgaW4gb25lCiAgICAgICAgICAgIC8vIHBhc3Mgc28gdGhlIGJvdW5kYXJ5LXNwbGl0IGxvdWRuZXNzIGxvb3AgYmVsb3cgbmVlZCBub3QgYWxzbyBjb3B5LgogICAgICAgICAgICBpZiAob3V0cHV0KSB7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGlucHV0Lmxlbmd0aDsgYysrKSB7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5DaCA9IGlucHV0W2NdOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG91dENoID0gb3V0cHV0W2NdOwogICAgICAgICAgICAgICAgICAgIGlmIChvdXRDaCkgewogICAgICAgICAgICAgICAgICAgICAgICBvdXRDaC5zZXQoaW5DaCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIEstd2VpZ2h0LCBzcXVhcmUsIGFuZCBhY2N1bXVsYXRlIHRoZSBzbGlkaW5nIHdpbmRvd3MgKyAxMDAgbXMgc3ViLWJsb2NrLgogICAgICAgICAgICAvLyBDUklUSUNBTCAoSVRVLVIgQlMuMTc3MC01IEFubmV4IDEsIGVxLjMgLyBwLjYg4oCUIGEgZ2F0aW5nIGJsb2NrIGlzIGEgc2V0IG9mCiAgICAgICAgICAgIC8vIENPTlRJR1VPVVMgc2FtcGxlcyBvZiBleGFjdGx5IHRoZSBibG9jayBkdXJhdGlvbik6IGEgMTI4LXNhbXBsZSByZW5kZXIKICAgICAgICAgICAgLy8gcXVhbnR1bSBjYW4gU1RSQURETEUgYSBzdWItYmxvY2sgYm91bmRhcnkgKDQ4MDAgc2FtcGxlcyBhdCA0OCBrSHopLiBXZSBtdXN0CiAgICAgICAgICAgIC8vIGNsb3NlIHRoZSBzdWItYmxvY2sgZXhhY3RseSBBVCB0aGUgYm91bmRhcnkgc28gbm8gc2FtcGxlIGxhbmRzIGluIHRoZSB3cm9uZwogICAgICAgICAgICAvLyAxMDAgbXMgc3ViLWJsb2NrLiBXYWxrIHRoZSBxdWFudHVtIGluIHNlZ21lbnRzIGJvdW5kZWQgYnkgdGhlIG5leHQgc3ViLWJsb2NrCiAgICAgICAgICAgIC8vIGVkZ2U7IGNsb3NlIChhbmQgZm9ybSBnYXRpbmcgYmxvY2tzIGZyb20pIHRoZSBzdWItYmxvY2sgd2hlbiBpdCBmaWxscy4KICAgICAgICAgICAgbGV0IG9mZnNldCA9IDA7CiAgICAgICAgICAgIHdoaWxlIChvZmZzZXQgPCBmcmFtZUNvdW50KSB7CiAgICAgICAgICAgICAgICBjb25zdCByZW1haW5pbmdJblN1YkJsb2NrID0gdGhpcy5zdWJCbG9ja1NhbXBsZXMgPiAwID8gdGhpcy5zdWJCbG9ja1NhbXBsZXMgLSB0aGlzLnNhbXBsZXNJbnRvU3ViQmxvY2sgOiBmcmFtZUNvdW50IC0gb2Zmc2V0OwogICAgICAgICAgICAgICAgY29uc3Qgc2VnbWVudCA9IE1hdGgubWluKHJlbWFpbmluZ0luU3ViQmxvY2ssIGZyYW1lQ291bnQgLSBvZmZzZXQpOwogICAgICAgICAgICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBpbnB1dC5sZW5ndGg7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGluQ2ggPSBpbnB1dFtjXTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBtZXRlciA9IHRoaXMuY2hhbm5lbE1ldGVyc1tjXTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBtb21lbnRhcnkgPSB0aGlzLm1vbWVudGFyeVdpbmRvd3NbY107CiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2hvcnRUZXJtID0gdGhpcy5zaG9ydFRlcm1XaW5kb3dzW2NdOwogICAgICAgICAgICAgICAgICAgIGxldCBzdWJTdW0gPSB0aGlzLnN1YkJsb2NrU3VtUGVyQ2hhbm5lbFtjXTsKICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gb2Zmc2V0OyBpIDwgb2Zmc2V0ICsgc2VnbWVudDsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEstd2VpZ2h0IHRoZW4gc3F1YXJlIGZvciB0aGUgbG91ZG5lc3Mgd2luZG93cyAvIGJsb2Nrcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgeSA9IG1ldGVyLmtGaWx0ZXIucHJvY2VzcyhpbkNoW2ldKTsKICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3EgPSB5ICogeTsKICAgICAgICAgICAgICAgICAgICAgICAgbW9tZW50YXJ5LnB1c2goc3EpOwogICAgICAgICAgICAgICAgICAgICAgICBzaG9ydFRlcm0ucHVzaChzcSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1YlN1bSArPSBzcTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWxbY10gPSBzdWJTdW07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBvZmZzZXQgKz0gc2VnbWVudDsKICAgICAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayArPSBzZWdtZW50OwogICAgICAgICAgICAgICAgLy8gQ2xvc2UgYSAxMDAgbXMgc3ViLWJsb2NrIGV4YWN0bHkgYXQgdGhlIGJvdW5kYXJ5LgogICAgICAgICAgICAgICAgaWYgKHRoaXMuc3ViQmxvY2tTYW1wbGVzID4gMCAmJiB0aGlzLnNhbXBsZXNJbnRvU3ViQmxvY2sgPj0gdGhpcy5zdWJCbG9ja1NhbXBsZXMpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNsb3NlU3ViQmxvY2soKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLnNhbXBsZXNTaW5jZVBvc3QgKz0gZnJhbWVDb3VudDsKICAgICAgICAgICAgaWYgKHRoaXMuc2FtcGxlc1NpbmNlUG9zdCA+PSB0aGlzLnBvc3RFdmVyeVNhbXBsZXMpIHsKICAgICAgICAgICAgICAgIHRoaXMuc2FtcGxlc1NpbmNlUG9zdCA9IDA7CiAgICAgICAgICAgICAgICBsZXQgbWF4VHJ1ZVBlYWsgPSAtSW5maW5pdHk7CiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IG1ldGVyIG9mIHRoaXMuY2hhbm5lbE1ldGVycykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRwID0gbWV0ZXIudHJ1ZVBlYWsudHJ1ZVBlYWtEYigpOwogICAgICAgICAgICAgICAgICAgIGlmICh0cCA+IG1heFRydWVQZWFrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIG1heFRydWVQZWFrID0gdHA7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgcmVwb3J0ID0gewogICAgICAgICAgICAgICAgICAgIHR5cGU6ICJsb3VkbmVzcyIsCiAgICAgICAgICAgICAgICAgICAgbW9tZW50YXJ5OiB0aGlzLndpbmRvd0xvdWRuZXNzKHRoaXMubW9tZW50YXJ5V2luZG93cyksCiAgICAgICAgICAgICAgICAgICAgc2hvcnRUZXJtOiB0aGlzLndpbmRvd0xvdWRuZXNzKHRoaXMuc2hvcnRUZXJtV2luZG93cyksCiAgICAgICAgICAgICAgICAgICAgaW50ZWdyYXRlZDogdGhpcy5jb21wdXRlSW50ZWdyYXRlZCgpLAogICAgICAgICAgICAgICAgICAgIHRydWVQZWFrOiBtYXhUcnVlUGVhaywKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2UocmVwb3J0KTsKICAgICAgICAgICAgICAgIHRoaXMucG9zdENvdW50ZXIrKzsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3NvcigibG91ZG5lc3MtbWV0ZXIiLCBMb3VkbmVzc01ldGVyUHJvY2Vzc29yKTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWxvdWRuZXNzLW1ldGVyLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIG1vZHVsYXRlZERlbGF5ID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgICAqIE1vZHVsYXRlZC1kZWxheSBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciB0aGUgdW5pZmllZAogICAgICogZGVsYXktbGluZSBtb2R1bGF0aW9uIGVmZmVjdCBiZWhpbmQgZGVsYXkvZWNobywgY2hvcnVzLCBmbGFuZ2VyLCB2aWJyYXRvIGFuZAogICAgICogZG91YmxpbmcuCiAgICAgKgogICAgICogSW1wbGVtZW50cyB0aGUgY2Fub25pY2FsIHVuaWZpZWQgY2lyY3VpdCBmcm9tOgogICAgICogICBKLiBEYXR0b3JybywgIkVmZmVjdCBEZXNpZ24sIFBhcnQgMjogRGVsYXktTGluZSBNb2R1bGF0aW9uIGFuZCBDaG9ydXMiLAogICAgICogICBKLiBBdWRpbyBFbmcuIFNvYy4sIFZvbC4gNDUsIE5vLiAxMCwgcHAuIDc2NC03ODggKDE5OTcpLgogICAgICoKICAgICAqIFRvcG9sb2d5IChEYXR0b3JybyAxOTk3LCBGaWcuIDM2LCBwLjc3NSkg4oCUIE9ORSBtb2R1bGF0ZWQgZGVsYXkgbGluZSB3cmFwcGVkCiAgICAgKiBpbiBkcnkvd2V0L2ZlZWRiYWNrIHBhdGhzOgogICAgICoKICAgICAqICAgICBIKHopID0gKGJsZW5kICsgZmVlZGZvcndhcmQgKiB6Xi1pLmZyYWMpIC8gKDEgKyBmZWVkYmFjayAqIHpeLWNlbnRlcikKICAgICAqCiAgICAgKiAgIC0gYmxlbmQgICAgICAg4oCUIGRyeSBwYXRoIGdhaW4gb24geFtuXS4KICAgICAqICAgLSBmZWVkZm9yd2FyZCDigJQgZ2FpbiBvbiB0aGUgTU9EVUxBVEVEIHdldCB0YXAgel4taS5mcmFjLCB3aGVyZSB0aGUgcmVhZAogICAgICogICAgIHBvaW50ZXIgaS5mcmFjID0gTk9NSU5BTF9ERUxBWSArIENIT1JVU19XSURUSCAqIHlbbl0gc3dlZXBzIHdpdGggdGhlIExGTy4KICAgICAqICAgLSBmZWVkYmFjayAgICDigJQgZ2FpbiBvbiBhIEZJWEVEIGNlbnRlciB0YXAgel4tY2VudGVyLiBEYXR0b3JybyBwLjc3NTogdGhlCiAgICAgKiAgICAgZmVlZGJhY2sgaXMgdGFrZW4gZnJvbSBhIFNFUEFSQVRFIGZpeGVkIHRhcCBhdCB0aGUgbm9taW5hbCBjZW50ZXIsIE5FVkVSCiAgICAgKiAgICAgdGhlIG1vZHVsYXRlZCB0YXAg4oCUIGZlZWRpbmcgYmFjayBhIHBpdGNoLW1vZHVsYXRlZCBzaWduYWwgaXMgb2JqZWN0aW9uYWJsZS4KICAgICAqCiAgICAgKiBLbm9iIHByZXNldHMgKFRhYmxlIDYsIHAuNzc1KSB5aWVsZCBlYWNoIGVmZmVjdCBmcm9tIHRoaXMgb25lIGNpcmN1aXQ6CiAgICAgKiAgIHZpYnJhdG8gIGJsZW5kPTAsICAgICAgZmY9MSwgICAgICBmYj0wICAgICAgICAoMTAwJSB3ZXQpCiAgICAgKiAgIGZsYW5nZXIgIGJsZW5kPTAuNzA3MSwgZmY9MC43MDcxLCBmYj0tMC43MDcxICAoYmxlbmQ9ZmYgZm9yIGRlZXBlc3QgdHJvdWdoKQogICAgICogICBjaG9ydXMgICBibGVuZD0wLjcwNzEsIGZmPTEsICAgICAgZmI9MC43MDcxICAgKHdoaXRlLWNob3J1cyBrbm9iczogYmxlbmQ9ZmIsIGZmPTEpCiAgICAgKiAgIGRvdWJsaW5nIGJsZW5kPTAuNzA3MSwgZmY9MC43MDcxLCBmYj0wCiAgICAgKiAgIGVjaG8gICAgIGJsZW5kPTEsICAgICAgZmY8PTEsICAgICBmYjwxICAgICAgICAobW9kdWxhdGlvbiBvZmYpCiAgICAgKiBTdGFiaWxpdHkgcmVxdWlyZXMgfGZlZWRiYWNrfCA8PSAwLjk5OTk5OTkgKFRhYmxlIDYsIHAuNzc2KS4KICAgICAqCiAgICAgKiBMRk86IHNpbnVzb2lkYWwsIGJpcG9sYXIgWy0xLCAxXSAoRGF0dG9ycm8gcC43Njcg4oCUIGEgdHJpYW5ndWxhciBMRk8gZ2l2ZXMgYW4KICAgICAqIHVubmF0dXJhbCBwaWVjZXdpc2UtY29uc3RhbnQgaW5zdGFudGFuZW91cyBwaXRjaCkuIE1vZHVsYXRlZCBkZWxheSBpbiBzYW1wbGVzCiAgICAgKiBpcyBkZWxheVRpbWUgKyBkZXB0aCpzaW4ocGhhc2UpOyBwaGFzZSBhZHZhbmNlcyBhdCBgcmF0ZWAgSHouIFN0ZXJlbyB1c2VzIGEKICAgICAqIHF1YWRyYXR1cmUgTEZPICg5MCBkZWcgcGVyIGNoYW5uZWwsIHAuNzc2KSwgcmVhbGl6ZWQgYnkgc2VlZGluZyBlYWNoIGNoYW5uZWwncwogICAgICogY29yZSB3aXRoIGxmb1BoYXNlMCA9IGNoICogcGkvMiBhdCBjb25zdHJ1Y3Rpb24gKHNlZSBtb2R1bGF0ZWQtZGVsYXkudHMpLgogICAgICoKICAgICAqIEludGVycG9sYXRpb246IHRoZSByZWFkIHBvaW50ZXIgbGFuZHMgYmV0d2VlbiBzYW1wbGVzLCBzbyB0aGUgZnJhY3Rpb25hbCBkZWxheQogICAgICogaXMgaW50ZXJwb2xhdGVkIHdpdGggYSBMYWdyYW5nZSBGSVIgZmlsdGVyIChMYWFrc28sIFZhbGltYWtpLCBLYXJqYWxhaW5lbiAmCiAgICAgKiBMYWluZSwgIlNwbGl0dGluZyB0aGUgVW5pdCBEZWxheSIsIElFRUUgU1AgTWFnIDE5OTYsIEVxLiA0MiAvIE49MSwzIHRhYmxlKS4KICAgICAqIExhZ3JhbmdlIGlzIEZJUiAoc3RhdGVsZXNzIGNvZWZmaWNpZW50cyk6IHVwZGF0aW5nIHRoZSBjb2VmZmljaWVudHMgZXZlcnkKICAgICAqIHNhbXBsZSB1bmRlciBtb2R1bGF0aW9uIGRvZXMgTk9UIGV4Y2l0ZSB0aGUgcmVjdXJzaXZlIHRyYW5zaWVudCB0aGF0IHBsYWd1ZXMKICAgICAqIGFuIGFsbHBhc3MgZnJhY3Rpb25hbC1kZWxheSBmaWx0ZXIgKExhYWtzbyBwLjUyKSwgYW5kIGl0cyBtYWduaXR1ZGUgc3RheXMgPD0gMQogICAgICogZm9yIGZlZWRiYWNrLWxvb3Agc2FmZXR5IChMYWFrc28gcC40MikuIFR3byBvcmRlcnMgYXJlIGV4cG9zZWQ6CiAgICAgKiAgIC0gImxpbmVhciIgKE49MSwgTGFha3NvIEVxLjQzKTogaDAgPSAxLWQsIGgxID0gZC4KICAgICAqICAgLSAiY3ViaWMiICAoTj0zLCBMYWFrc28gdGFibGUpOiB0aGUgNC10YXAgbWF4aW1hbGx5LWZsYXQgaW50ZXJwb2xhdG9yLCB3aXRoCiAgICAgKiAgICAgRCA9IDEgKyBkIHNvIHRoZSBmcmFjdGlvbiBzaXRzIGJldHdlZW4gdGFwcyAxIGFuZCAyIChNX29wdCBjZW50ZXJpbmcpLgogICAgICogQWxscGFzcyBpbnRlcnBvbGF0aW9uIGlzIGRlbGliZXJhdGVseSBkZWZlcnJlZDogdW5kZXIgbW9kdWxhdGlvbiBpdCBwcm9kdWNlcwogICAgICogdHJhbnNpZW50cyBhbmQgaXMgY2xlYW4gb25seSBvdmVyIH4rLy0xIHNlbWl0b25lIChEYXR0b3JybyBwLjc3MC03NzQpLgogICAgICoKICAgICAqIE5PVEUgb24gIndoaXRlIGNob3J1cyI6IERhdHRvcnJvIChwLjc3NikgZGVmaW5lcyB3aGl0ZSBjaG9ydXMgYXMgbmVnYXRpdmUKICAgICAqIGZlZWRiYWNrIEFORCBhbGxwYXNzIGZyYWN0aW9uYWwtZGVsYXkgaW50ZXJwb2xhdGlvbi4gVGhpcyBjb3JlIHVzZXMgdGhlCiAgICAgKiB3aGl0ZS1jaG9ydXMgS05PQiBzZXR0aW5ncyAodGhlIG5lZ2F0aXZlLWZlZWRiYWNrIHBhdGgpIGJ1dCBzdWJzdGl0dXRlcwogICAgICogY3ViaWMtTGFncmFuZ2UgaW50ZXJwb2xhdGlvbiAoTGFha3NvIDE5OTYpIGZvciBEYXR0b3JybydzIGFsbHBhc3Mg4oCUIGl0IGlzCiAgICAgKiB0cmFuc2llbnQtc2FmZSB1bmRlciBtb2R1bGF0aW9uIChMYWFrc28gcC41MikgYXQgdGhlIGNvc3Qgb2Ygc29tZQogICAgICogaGlnaC1mcmVxdWVuY3kgdHJvdWdoIGRlcHRoIHZlcnN1cyBhIHRydWUgYWxscGFzcy1pbnRlcnBvbGF0ZWQgd2hpdGUgY2hvcnVzLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzICsgRmxvYXQzMkFycmF5KS4gSXQgaGFzCiAgICAgKiBubyBBdWRpb1dvcmtsZXQgLyBnbG9iYWwgZGVwZW5kZW5jaWVzLCBzbyBpdCBpcyB1bml0LXRlc3RhYmxlIGRpcmVjdGx5OyB0aGUKICAgICAqIHdvcmtsZXQgc2hlbGwgaW4gbW9kdWxhdGVkLWRlbGF5LnRzIGRlbGVnYXRlcyB0byBpdC4KICAgICAqLwogICAgLyoqCiAgICAgKiBUaGUgRGF0dG9ycm8gVGFibGUgNiBrbm9iIGNvbnN0YW50IDEvc3FydCgyKSwgcHJpbnRlZCBWRVJCQVRJTSBhcyAwLjcwNzEgaW4KICAgICAqIHRoZSBwYXBlciAocC43NzUg4oCUIERhdHRvcnJvIHF1YW50aXplZCBpdCB0byA0IGRlY2ltYWxzIGZvciBoaXMgcTIzIGZpeGVkLXBvaW50CiAgICAgKiBoYXJkd2FyZSkuIFRoZSBmYWN0b3J5IHByZXNldHMgKGNhY29waG9ueS50cykgYW5kIHRoZSB3aGl0ZS1jaG9ydXMgY29uZGl0aW9uCiAgICAgKiBibGVuZCA9IGZlZWRiYWNrIHVzZSB0aGlzIGV4YWN0IHByaW50ZWQgdmFsdWUsIE5PVCB0aGUgZnVsbC1wcmVjaXNpb24KICAgICAqIE1hdGguU1FSVDFfMiwgc28gdGhlIHNoaXBwZWQga25vYnMgbWF0Y2ggdGhlIHBhcGVyJ3MgdGFibGUgZGlnaXQtZm9yLWRpZ2l0LgogICAgICovCiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQXBwcm94aW1hdGl2ZU51bWVyaWNDb25zdGFudDogRGF0dG9ycm8gVGFibGUgNiBwcmludHMgMC43MDcxIHZlcmJhdGltOyBtYXRjaGluZyB0aGUgcGFwZXIgZGlnaXQtZm9yLWRpZ2l0IGlzIGludGVudGlvbmFsLCBub3QgYW4gaW5leGFjdCBNYXRoLlNRUlQxXzIuCiAgICBjb25zdCBEQVRUT1JST19JTlZfU1FSVDIgPSAwLjcwNzE7CiAgICAvKioKICAgICAqIERlZmF1bHQgQXVkaW9QYXJhbSB2YWx1ZXMgZm9yIHRoZSBtb2R1bGF0ZWQtZGVsYXkgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncwogICAgICogb3duIHVuaXRzLiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AKICAgICAqIChtb2R1bGF0ZWQtZGVsYXkudHMpIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdAogICAgICogbmVlZCB0aGUgc2hpcHBlZCBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgc2hvcnQsIGRyeSwgc2xvdyBzZXR0aW5nIChhIHRyYW5zcGFyZW50IG5lYXItYnlwYXNzKSDigJQgdGhlCiAgICAgKiBjcmVhdGVEZWxheS9jcmVhdGVDaG9ydXMvY3JlYXRlRmxhbmdlci9jcmVhdGVWaWJyYXRvL2NyZWF0ZURvdWJsaW5nIGZhY3RvcmllcwogICAgICogKGNhY29waG9ueS50cykgc3ByZWFkIERhdHRvcnJvIFRhYmxlIDYga25vYiBwcmVzZXRzIG92ZXIgdGhlc2UuCiAgICAgKi8KICAgIGNvbnN0IE1PRFVMQVRFRF9ERUxBWV9ERUZBVUxUUyA9IHsKICAgICAgICBkZWxheVRpbWU6IDUsCiAgICAgICAgZGVwdGg6IDAsCiAgICAgICAgcmF0ZTogMC41LAogICAgICAgIGZlZWRiYWNrOiAwLAogICAgICAgIGJsZW5kOiAxLAogICAgICAgIGZlZWRmb3J3YXJkOiBEQVRUT1JST19JTlZfU1FSVDJ9OwogICAgLyoqIE1heGltdW0gbm9taW5hbCBkZWxheSAobXMpIHRoZSBidWZmZXIgaXMgc2l6ZWQgZm9yIChkZWxheVRpbWUgQXVkaW9QYXJhbSBtYXgpLiAqLwogICAgY29uc3QgTUFYX0RFTEFZX01TID0gMTAwMDsKICAgIC8qKiBNYXhpbXVtIExGTyBleGN1cnNpb24gKG1zKSB0aGUgYnVmZmVyIGlzIHNpemVkIGZvciAoZGVwdGggQXVkaW9QYXJhbSBtYXgpLiAqLwogICAgY29uc3QgTUFYX0RFUFRIX01TID0gNTA7CiAgICAvKiogU3RhYmlsaXR5IGJvdW5kIG9uIHxmZWVkYmFja3wgKERhdHRvcnJvIFRhYmxlIDYsIHEyMyBtYXgsIHAuNzc2KS4gKi8KICAgIGNvbnN0IE1BWF9GRUVEQkFDSyA9IDAuOTk5OTk5OTsKICAgIC8qKgogICAgICogTGFncmFuZ2UgbGluZWFyIChOPTEpIGZyYWN0aW9uYWwtZGVsYXkgY29lZmZpY2llbnRzIChMYWFrc28gMTk5NiBFcS40Myk6CiAgICAgKiAgIGgwID0gMSAtIGQsICBoMSA9IGQgICAgICAgIGZvciBmcmFjdGlvbmFsIGRlbGF5IGQgaW4gWzAsIDEpLgogICAgICogVHdvLXRhcCBGSVI7IGgwIHdlaWdodHMgdGhlIGludGVnZXIgdGFwLCBoMSB0aGUgbmV4dCBzYW1wbGUuIGgwICsgaDEgPSAxCiAgICAgKiAocGFydGl0aW9uIG9mIHVuaXR5LCBEQyBnYWluIDEpLiBFeHBvc2VkIHB1cmUgZm9yIGlzb2xhdGVkIHRlc3RpbmcuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGxhZ3JhbmdlTGluZWFyKGQpIHsKICAgICAgICByZXR1cm4gWzEgLSBkLCBkXTsKICAgIH0KICAgIC8qKgogICAgICogTGFncmFuZ2UgY3ViaWMgKE49MykgZnJhY3Rpb25hbC1kZWxheSBjb2VmZmljaWVudHMgKExhYWtzbyAxOTk2LCBOPTMgdGFibGUsCiAgICAgKiBwLjQxKS4gVGhlIDQtdGFwIG1heGltYWxseS1mbGF0IGludGVycG9sYXRvciBldmFsdWF0ZWQgYXQgdG90YWwgZGVsYXkKICAgICAqIEQgPSAxICsgZCwgc28gdGhlIGRlc2lyZWQgZnJhY3Rpb24gZCBpbiBbMCwgMSkgc2l0cyBiZXR3ZWVuIHRhcHMgMSBhbmQgMgogICAgICogKExhYWtzbyBNX29wdCBjZW50ZXJpbmcsIEVxLjIxKSDigJQgdGhlIHJlZ2ltZSB3aGVyZSB8SHwgPD0gMToKICAgICAqCiAgICAgKiAgIGgwID0gLShELTEpKEQtMikoRC0zKS82CiAgICAgKiAgIGgxID0gIEQoRC0yKShELTMpLzIKICAgICAqICAgaDIgPSAtRChELTEpKEQtMykvMgogICAgICogICBoMyA9ICBEKEQtMSkoRC0yKS82CiAgICAgKgogICAgICogVGhlIHJldHVybmVkIHRhcHMgd2VpZ2h0IGRlbGF5LWxpbmUgc2FtcGxlcyBhdCBpbnRlZ2VyIG9mZnNldHMge2ktMSwgaSwgaSsxLAogICAgICogaSsyfSByZWxhdGl2ZSB0byB0aGUgaW50ZWdlciBwYXJ0IG9mIHRoZSByZWFkIHBvaW50ZXIuIFN1bSA9IDEgKERDIGdhaW4gMSkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGxhZ3JhbmdlQ3ViaWMoZCkgewogICAgICAgIGNvbnN0IEQgPSAxICsgZDsKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAoLShEIC0gMSkgKiAoRCAtIDIpICogKEQgLSAzKSkgLyA2LAogICAgICAgICAgICAoRCAqIChEIC0gMikgKiAoRCAtIDMpKSAvIDIsCiAgICAgICAgICAgICgtRCAqIChEIC0gMSkgKiAoRCAtIDMpKSAvIDIsCiAgICAgICAgICAgIChEICogKEQgLSAxKSAqIChEIC0gMikpIC8gNiwKICAgICAgICBdOwogICAgfQogICAgLyoqIENvbnZlcnQgbWlsbGlzZWNvbmRzIHRvIGEgKGZyYWN0aW9uYWwpIHNhbXBsZSBjb3VudCBhdCB0aGUgZ2l2ZW4gcmF0ZS4gKi8KICAgIGZ1bmN0aW9uIG1zVG9TYW1wbGVzKG1zLCBzYW1wbGVSYXRlKSB7CiAgICAgICAgcmV0dXJuIChtcyAvIDEwMDApICogc2FtcGxlUmF0ZTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgbW9kdWxhdGVkLWRlbGF5IHByb2Nlc3NvciBmb3IgT05FIGNoYW5uZWwuIE93bnMgdGhlIGNpcmN1bGFyIGRlbGF5CiAgICAgKiBidWZmZXIsIHRoZSB3cml0ZSBwb2ludGVyIGFuZCB0aGUgTEZPIHBoYXNlLCBhbmQgaXMgcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvCiAgICAgKiB0aGUgZGVsYXktbGluZSBjb250ZW50cywgZmVlZGJhY2sgcmVjaXJjdWxhdGlvbiBhbmQgTEZPIGNvbnRpbnVpdHkgYWxsIHN1cnZpdmUKICAgICAqIGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMg4oCUIGNvbnN0cnVjdGluZyBhIGZyZXNoIGluc3RhbmNlIHBlciBibG9jayB3b3VsZCBjbGVhcgogICAgICogdGhlIGJ1ZmZlciBhbmQgcmVzZXQgdGhlIExGTywgcHJvZHVjaW5nIGF1ZGlibGUgY2xpY2tzIChzYW1lIGhhemFyZCBkb2N1bWVudGVkCiAgICAgKiBmb3IgdGhlIGR5bmFtaWNzIGJhbGxpc3RpY3MgYW5kIEFEQUEgaGlzdG9yeSBjb3JlcykuCiAgICAgKgogICAgICogVGhlIGJ1ZmZlciBpcyBzaXplZCBPTkNFIGluIHRoZSBjb25zdHJ1Y3RvciBmb3IgTUFYX0RFTEFZX01TICsgTUFYX0RFUFRIX01TCiAgICAgKiBwbHVzIGEgbWFyZ2luIGZvciB0aGUgY3ViaWMgaW50ZXJwb2xhdG9yJ3MgNC10YXAgd2luZG93LCBzbyB0aGUgcmVhZCBwb2ludGVyCiAgICAgKiBjYW4gbmV2ZXIgcnVuIHBhc3QgdGhlIGFsbG9jYXRpb24gcmVnYXJkbGVzcyBvZiB0aGUgKGNsYW1wZWQpIHBhcmFtcy4KICAgICAqLwogICAgY2xhc3MgTW9kdWxhdGVkRGVsYXlQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgLyoqIENpcmN1bGFyIGRlbGF5IGxpbmUuICovCiAgICAgICAgYnVmZmVyOwogICAgICAgIC8qKiBMZW5ndGggb2YgdGhlIGNpcmN1bGFyIGJ1ZmZlciAoc2FtcGxlcykuICovCiAgICAgICAgc2l6ZTsKICAgICAgICAvKiogV3JpdGUgaGVhZCBpbmRleCBpbnRvIGBidWZmZXJgLiAqLwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIC8qKiBDdXJyZW50IExGTyBwaGFzZSAocmFkaWFucykuICovCiAgICAgICAgbGZvUGhhc2U7CiAgICAgICAgY29uc3RydWN0b3Ioc2FtcGxlUmF0ZSwgbGZvUGhhc2UwID0gMCkgewogICAgICAgICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlOwogICAgICAgICAgICAvLyBTaXplIGZvciB0aGUgd29yc3QgY2FzZTogbWF4IG5vbWluYWwgZGVsYXkgKyBtYXggZXhjdXJzaW9uICsgY3ViaWMgbWFyZ2luCiAgICAgICAgICAgIC8vICh0aGUgY3ViaWMgaW50ZXJwb2xhdG9yIHJlYWRzIG9uZSBzYW1wbGUgYmVmb3JlIGFuZCB0d28gYWZ0ZXIgdGhlIGludGVnZXIKICAgICAgICAgICAgLy8gdGFwKS4gKzQgc2FtcGxlcyBvZiBzbGFjayBrZWVwcyBldmVyeSByZWFkIGluZGV4IGluc2lkZSB0aGUgYWxsb2NhdGlvbi4KICAgICAgICAgICAgY29uc3QgbWF4RGVsYXlTYW1wbGVzID0gbXNUb1NhbXBsZXMoTUFYX0RFTEFZX01TICsgTUFYX0RFUFRIX01TLCBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5zaXplID0gTWF0aC5jZWlsKG1heERlbGF5U2FtcGxlcykgKyA0OwogICAgICAgICAgICB0aGlzLmJ1ZmZlciA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5zaXplKTsKICAgICAgICAgICAgdGhpcy5sZm9QaGFzZSA9IGxmb1BoYXNlMDsKICAgICAgICB9CiAgICAgICAgLyoqIENsZWFyIHRoZSBkZWxheSBsaW5lLCB3cml0ZSBoZWFkIGFuZCBMRk8gcGhhc2UuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKiogUmVhZCB0aGUgZGVsYXkgbGluZSBhdCBpbnRlZ2VyIG9mZnNldCBgZGVsYXlTYW1wbGVzYCBiZWhpbmQgdGhlIHdyaXRlIGhlYWQuICovCiAgICAgICAgcmVhZEludGVnZXIoZGVsYXlTYW1wbGVzKSB7CiAgICAgICAgICAgIGxldCBpZHggPSB0aGlzLndyaXRlSW5kZXggLSBkZWxheVNhbXBsZXM7CiAgICAgICAgICAgIGlkeCAlPSB0aGlzLnNpemU7CiAgICAgICAgICAgIGlmIChpZHggPCAwKQogICAgICAgICAgICAgICAgaWR4ICs9IHRoaXMuc2l6ZTsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyW2lkeF07CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFJlYWQgdGhlIGRlbGF5IGxpbmUgYXQgYSBmcmFjdGlvbmFsIGRlbGF5IGBkZWxheVNhbXBsZXNgIGJlaGluZCB0aGUgd3JpdGUKICAgICAgICAgKiBoZWFkLCBpbnRlcnBvbGF0aW5nIHdpdGggdGhlIHNlbGVjdGVkIExhZ3JhbmdlIEZJUiAoTGFha3NvIDE5OTYpLgogICAgICAgICAqLwogICAgICAgIHJlYWRGcmFjdGlvbmFsKGRlbGF5U2FtcGxlcywgbW9kZSkgewogICAgICAgICAgICBjb25zdCBpbnREZWxheSA9IE1hdGguZmxvb3IoZGVsYXlTYW1wbGVzKTsKICAgICAgICAgICAgY29uc3QgZnJhYyA9IGRlbGF5U2FtcGxlcyAtIGludERlbGF5OwogICAgICAgICAgICAvLyBMaW5lYXIgKE49MSksIG9yIGN1YmljIGRlZ3JhZGVkIHRvIGxpbmVhciBuZWFyIHRoZSBoZWFkOiB3aGVuIGludERlbGF5IDwgMQogICAgICAgICAgICAvLyB0aGUgY3ViaWMncyBuZXdlc3QgdGFwIChpbnREZWxheS0xKSB3b3VsZCBiZSBhIEZVVFVSRSBzYW1wbGUsIHNvIGZhbGwgYmFjawogICAgICAgICAgICAvLyB0byB0aGUgMi10YXAgbGluZWFyIGludGVycG9sYXRvci4gTGluZWFyIHJlYWNoZXMgZGVsYXkgMCBleGFjdGx5IChvZmZzZXQgMAogICAgICAgICAgICAvLyA9IHRoZSBqdXN0LXdyaXR0ZW4gY3VycmVudCBzYW1wbGUpIGZvciB0cnVlIHRocm91Z2gtemVybyBmbGFuZ2UvdmlicmF0by4KICAgICAgICAgICAgaWYgKG1vZGUgPT09ICJsaW5lYXIiIHx8IGludERlbGF5IDwgMSkgewogICAgICAgICAgICAgICAgY29uc3QgW2gwLCBoMV0gPSBsYWdyYW5nZUxpbmVhcihmcmFjKTsKICAgICAgICAgICAgICAgIC8vIFRoZSBkZXNpcmVkIGRlbGF5IGludERlbGF5K2ZyYWMgc2l0cyBiZXR3ZWVuIG9mZnNldCBpbnREZWxheSAobmV3ZXIpIGFuZAogICAgICAgICAgICAgICAgLy8gb2Zmc2V0IGludERlbGF5KzEgKG9sZGVyKS4gaDAgd2VpZ2h0cyB0aGUgaW50ZWdlciB0YXAsIGgxIHRoZSBuZXh0LU9MREVSCiAgICAgICAgICAgICAgICAvLyBzYW1wbGUsIHNvIGEgbGFyZ2VyIGZyYWMgcHVsbHMgdG93YXJkIHRoZSBsYXJnZXIgKG9sZGVyKSBkZWxheS4KICAgICAgICAgICAgICAgIHJldHVybiBoMCAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkpICsgaDEgKiB0aGlzLnJlYWRJbnRlZ2VyKGludERlbGF5ICsgMSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gQ3ViaWM6IEQgPSAxICsgZnJhYyBjZW50ZXJzIHRoZSBmcmFjdGlvbiBiZXR3ZWVuIHRhcHMgMSBhbmQgMiwgc28gdGhlIGZvdXIKICAgICAgICAgICAgLy8gdGFwcyBydW4gbmV3ZXN0LT5vbGRlc3QgYXQgb2Zmc2V0cyB7aW50RGVsYXktMSwgaW50RGVsYXksIGludERlbGF5KzEsCiAgICAgICAgICAgIC8vIGludERlbGF5KzJ9LCB3ZWlnaHRlZCBieSBoMC4uaDMgZnJvbSB0aGUgTGFha3NvIE49MyB0YWJsZSBpbiB0aGF0IG9yZGVyLgogICAgICAgICAgICBjb25zdCBbaDAsIGgxLCBoMiwgaDNdID0gbGFncmFuZ2VDdWJpYyhmcmFjKTsKICAgICAgICAgICAgcmV0dXJuIChoMCAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkgLSAxKSArCiAgICAgICAgICAgICAgICBoMSAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkpICsKICAgICAgICAgICAgICAgIGgyICogdGhpcy5yZWFkSW50ZWdlcihpbnREZWxheSArIDEpICsKICAgICAgICAgICAgICAgIGgzICogdGhpcy5yZWFkSW50ZWdlcihpbnREZWxheSArIDIpKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhIGJsb2NrLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtYXkgYWxpYXMgKGluLXBsYWNlIGlzIGZpbmUpLgogICAgICAgICAqCiAgICAgICAgICogUGVyIHNhbXBsZSAoRGF0dG9ycm8gMTk5NyBGaWcuIDM2KToKICAgICAgICAgKiAgIDEuIGNlbnRlciAgPSBmaXhlZCBub21pbmFsIGRlbGF5IHRhcCAoc2FtcGxlcykgICAgICAgICAgICAgICAgICAoZmVlZGJhY2sgdGFwKQogICAgICAgICAqICAgMi4gZmIgICAgICA9IGZlZWRiYWNrICogZGVsYXlMaW5lW2NlbnRlcl0gICAgICAgICAgICAgICAgICAgICAgIChmaXhlZCB0YXApCiAgICAgICAgICogICAzLiB3W25dICAgID0geFtuXSAtIGZlZWRiYWNrKmRlbGF5TGluZVtjZW50ZXJdIC0+IHdyaXR0ZW4gICAgICAgKHJlY2lyY3VsYXRpb24pCiAgICAgICAgICogICAgICBUaGUgc3VtbWVyIFNVQlRSQUNUUyB0aGUgZmVlZGJhY2sgKEZpZy4gMzYncyBuZWdhdGl2ZS1mZWVkYmFjayBzdW1tZXIsCiAgICAgICAgICogICAgICBwLjc3NSksIHJlYWxpemluZyB0aGUgcGFwZXIncyBkZW5vbWluYXRvciAxICsgZmVlZGJhY2sqel4tY2VudGVyIChwLjc3NikuCiAgICAgICAgICogICA0LiBpLmZyYWMgID0gY2VudGVyICsgZGVwdGggKiBzaW4obGZvUGhhc2UpICAgICAgICAgICAgICAgICAgICAgKG1vZHVsYXRlZCB0YXApCiAgICAgICAgICogICA1LiB3ZXQgICAgID0gaW50ZXJwb2xhdGUgZGVsYXlMaW5lIGF0IGkuZnJhYyAgICAgICAgICAgICAgICAgICAgKExhZ3JhbmdlIEZJUikKICAgICAgICAgKiAgIDYuIHlbbl0gICAgPSBibGVuZCAqIHdbbl0gKyBmZWVkZm9yd2FyZCAqIHdldCAgICAgICAgICAgICAgICAgICAoZHJ5ICsgd2V0IG1peCkKICAgICAgICAgKiAgICAgIFRoZSBkcnkgKGJsZW5kKSB0YXAgcmVhZHMgdGhlIHJlY2lyY3VsYXRpb24gbm9kZSB3W25dLCBOT1QgdGhlIHJhdyBpbnB1dAogICAgICAgICAqICAgICAgeFtuXSwgc28gYm90aCBudW1lcmF0b3IgdGFwcyBzaGFyZSB0aGUgZmVlZGJhY2sgZGVub21pbmF0b3IgYW5kIHRoZQogICAgICAgICAqICAgICAgdHJhbnNmZXIgZnVuY3Rpb24gaXMgZXhhY3RseSBIKHopID0gKGJsZW5kICsgZmVlZGZvcndhcmQqel4taSkvKDEgKwogICAgICAgICAqICAgICAgZmVlZGJhY2sqel4tY2VudGVyKSAocC43NzYpLiBUaGlzIGlzIHRoZSBjYW5vbmljYWwgYWxscGFzcy1jb21iOiB3aXRoCiAgICAgICAgICogICAgICBibGVuZCA9IGZlZWRiYWNrIGFuZCBmZWVkZm9yd2FyZCA9IDEgaXQgY29sbGFwc2VzIHRvIGEgdW5pdHktbWFnbml0dWRlCiAgICAgICAgICogICAgICBhbGxwYXNzICh0aGUgd2hpdGUtY2hvcnVzIGNvbmRpdGlvbiwgcC43NzYpLiBXaGVuIGZlZWRiYWNrID0gMCwgd1tuXSA9CiAgICAgICAgICogICAgICB4W25dLCBzbyB0aGUgZHJ5IHBhdGggaXMgdGhlIHBsYWluIGlucHV0IGFzIGV4cGVjdGVkIGZvciB0aGUgbm8tZmVlZGJhY2sKICAgICAgICAgKiAgICAgIHByZXNldHMgKHZpYnJhdG8vaW5kdXN0cnkgY2hvcnVzL2RvdWJsaW5nL2VjaG8pLgogICAgICAgICAqIFRoZSBMRk8gcGhhc2UgYWR2YW5jZXMgYnkgMipwaSpyYXRlL3NhbXBsZVJhdGUgZWFjaCBzYW1wbGUuCiAgICAgICAgICovCiAgICAgICAgcHJvY2VzcyhpbnB1dCwgb3V0cHV0LCBwYXJhbXMpIHsKICAgICAgICAgICAgLy8gay1yYXRlLXN0eWxlIGNvZWZmaWNpZW50cyBjb21wdXRlZCBvbmNlIHBlciBibG9jay4KICAgICAgICAgICAgY29uc3QgY2VudGVyU2FtcGxlcyA9IG1zVG9TYW1wbGVzKHBhcmFtcy5kZWxheVRpbWUsIHRoaXMuc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIGNvbnN0IGRlcHRoU2FtcGxlcyA9IG1zVG9TYW1wbGVzKHBhcmFtcy5kZXB0aCwgdGhpcy5zYW1wbGVSYXRlKTsKICAgICAgICAgICAgY29uc3QgZmVlZGJhY2sgPSBNYXRoLm1heCgtTUFYX0ZFRURCQUNLLCBNYXRoLm1pbihNQVhfRkVFREJBQ0ssIHBhcmFtcy5mZWVkYmFjaykpOwogICAgICAgICAgICBjb25zdCBibGVuZCA9IHBhcmFtcy5ibGVuZDsKICAgICAgICAgICAgY29uc3QgZmVlZGZvcndhcmQgPSBwYXJhbXMuZmVlZGZvcndhcmQ7CiAgICAgICAgICAgIGNvbnN0IG1vZGUgPSBwYXJhbXMuaW50ZXJwb2xhdGlvbjsKICAgICAgICAgICAgY29uc3QgcGhhc2VJbmMgPSAoMiAqIE1hdGguUEkgKiBwYXJhbXMucmF0ZSkgLyB0aGlzLnNhbXBsZVJhdGU7CiAgICAgICAgICAgIC8vIFRoZSBmaXhlZCBmZWVkYmFjayB0YXAgcmVhZHMgdGhlIGludGVnZXIgY2VudGVyOyBjbGFtcCB0byBhIHZhbGlkIG9mZnNldC4KICAgICAgICAgICAgY29uc3QgY2VudGVyVGFwID0gTWF0aC5tYXgoMCwgTWF0aC5taW4odGhpcy5zaXplIC0gMSwgTWF0aC5yb3VuZChjZW50ZXJTYW1wbGVzKSkpOwogICAgICAgICAgICBjb25zdCBuID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICAgICAgICAgIGNvbnN0IHggPSBpbnB1dFtpXTsKICAgICAgICAgICAgICAgIC8vIEZlZWRiYWNrIGZyb20gdGhlIEZJWEVEIGNlbnRlciB0YXAgKERhdHRvcnJvIHAuNzc1OiBuZXZlciB0aGUgbW9kdWxhdGVkCiAgICAgICAgICAgICAgICAvLyB0YXApLiBSZWFkIEJFRk9SRSB3cml0aW5nIHRoaXMgc2FtcGxlIHNvIHRoZSBsb29wIGRlbGF5IGlzIGBjZW50ZXJgLgogICAgICAgICAgICAgICAgY29uc3QgZmIgPSBmZWVkYmFjayAqIHRoaXMucmVhZEludGVnZXIoY2VudGVyVGFwKTsKICAgICAgICAgICAgICAgIC8vIFdyaXRlIHRoZSByZWNpcmN1bGF0ZWQgc2FtcGxlIGludG8gdGhlIGRlbGF5IGxpbmUgYXQgdGhlIHdyaXRlIGhlYWQuCiAgICAgICAgICAgICAgICAvLyBUaGUgZmVlZGJhY2sgaXMgU1VCVFJBQ1RFRCAoRGF0dG9ycm8gRmlnLiAzNidzIG5lZ2F0aXZlLWZlZWRiYWNrIHN1bW1lciwKICAgICAgICAgICAgICAgIC8vIHAuNzc1KSwgc28gdGhlIHRyYW5zZmVyIGZ1bmN0aW9uIGRlbm9taW5hdG9yIGlzIDEgKyBmZWVkYmFjayp6Xi1jZW50ZXIuCiAgICAgICAgICAgICAgICBjb25zdCB3ID0geCAtIGZiOwogICAgICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZUluZGV4XSA9IHc7CiAgICAgICAgICAgICAgICAvLyBNb2R1bGF0ZWQgcmVhZCBwb2ludGVyIGkuZnJhYyA9IGNlbnRlciArIGRlcHRoICogTEZPIChEYXR0b3JybyBlcSwgcC43NjUpLgogICAgICAgICAgICAgICAgbGV0IHJlYWREZWxheSA9IGNlbnRlclNhbXBsZXMgKyBkZXB0aFNhbXBsZXMgKiBNYXRoLnNpbih0aGlzLmxmb1BoYXNlKTsKICAgICAgICAgICAgICAgIC8vIEFsbG93IHRoZSBkZWxheSBkb3duIHRvIDAgc28gZmxhbmdlcnMvdmlicmF0byBjYW4gc3dlZXAgdGhyb3VnaCBhYnNvbHV0ZQogICAgICAgICAgICAgICAgLy8gemVybyAoRGF0dG9ycm8gcC43NzU7IFRhYmxlIDcgdmlicmF0by9mbGFuZ2Ugb25zZXQgMCBtcykuIE9mZnNldCAwIGlzIHRoZQogICAgICAgICAgICAgICAgLy8gbGVnaXRpbWF0ZSBqdXN0LXdyaXR0ZW4gY3VycmVudCBzYW1wbGUgKHdlIHdyaXRlIGJlZm9yZSByZWFkaW5nIHRoZSB3ZXQKICAgICAgICAgICAgICAgIC8vIHRhcCkuIHJlYWRGcmFjdGlvbmFsIGRlZ3JhZGVzIGN1YmljIC0+IGxpbmVhciBuZWFyIHRoZSBoZWFkIHNvIG5vIGZ1dHVyZQogICAgICAgICAgICAgICAgLy8gc2FtcGxlIGlzIGV2ZXIgcmVhZC4gVGhlIHVwcGVyIGNsYW1wIGtlZXBzIHRoZSBjdWJpYydzIG9sZGVzdCB0YXAKICAgICAgICAgICAgICAgIC8vIChpbnREZWxheSsyKSBpbnNpZGUgdGhlIGFsbG9jYXRpb24uCiAgICAgICAgICAgICAgICBpZiAocmVhZERlbGF5IDwgMCkKICAgICAgICAgICAgICAgICAgICByZWFkRGVsYXkgPSAwOwogICAgICAgICAgICAgICAgaWYgKHJlYWREZWxheSA+IHRoaXMuc2l6ZSAtIDMpCiAgICAgICAgICAgICAgICAgICAgcmVhZERlbGF5ID0gdGhpcy5zaXplIC0gMzsKICAgICAgICAgICAgICAgIGNvbnN0IHdldCA9IHRoaXMucmVhZEZyYWN0aW9uYWwocmVhZERlbGF5LCBtb2RlKTsKICAgICAgICAgICAgICAgIC8vIEZpZy4gMzYgb3V0cHV0OiBkcnkgKGJsZW5kLCBvZmYgdGhlIHJlY2lyY3VsYXRpb24gbm9kZSB3W25dKSArIG1vZHVsYXRlZAogICAgICAgICAgICAgICAgLy8gd2V0IChmZWVkZm9yd2FyZCkuIEJvdGggdGFwcyBzaGFyZSB0aGUgZmVlZGJhY2sgZGVub21pbmF0b3IsIGdpdmluZwogICAgICAgICAgICAgICAgLy8gSCh6KSA9IChibGVuZCArIGZlZWRmb3J3YXJkKnpeLWkpLygxICsgZmVlZGJhY2sqel4tY2VudGVyKSAocC43NzYpLgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0gYmxlbmQgKiB3ICsgZmVlZGZvcndhcmQgKiB3ZXQ7CiAgICAgICAgICAgICAgICAvLyBBZHZhbmNlIHdyaXRlIGhlYWQgYW5kIExGTyBwaGFzZS4KICAgICAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9ICh0aGlzLndyaXRlSW5kZXggKyAxKSAlIHRoaXMuc2l6ZTsKICAgICAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgKz0gcGhhc2VJbmM7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5sZm9QaGFzZSA+PSAyICogTWF0aC5QSSkKICAgICAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlIC09IDIgKiBNYXRoLlBJOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBNb2R1bGF0ZWQtZGVsYXkgQXVkaW9Xb3JrbGV0IHNoZWxsIOKAlCB0aGluIEF1ZGlvV29ya2xldFByb2Nlc3NvciB0aGF0IGRlbGVnYXRlcwogICAgICogQUxMIERTUCBtYXRoIHRvIHRoZSBjb250ZXh0LWZyZWUgTW9kdWxhdGVkRGVsYXlQcm9jZXNzb3IgaW4KICAgICAqIG1vZHVsYXRlZC1kZWxheS1jb3JlLnRzLiBNaXJyb3JzIHRoZSBkeW5hbWljcy50cyAvIHdhdmVzaGFwZXIudHMgY29yZS9zaGVsbAogICAgICogc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKHBhcmFtZXRlckRlc2NyaXB0b3JzLAogICAgICogcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IgY2FsbCk7IHRoZSBhbGdvcml0aG0gbGl2ZXMgaW4gdGhlIHVuaXQtdGVzdGVkCiAgICAgKiBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogRGF0dG9ycm8ncyB1bmlmaWVkIG1vZHVsYXRlZC1kZWxheSBjaXJjdWl0IChGaWcuIDM2LCBKQUVTIDE5OTcpCiAgICAgKiBkcml2aW5nIGRlbGF5L2Nob3J1cy9mbGFuZ2VyL3ZpYnJhdG8vZG91YmxpbmcgZnJvbSBibGVuZC9mZWVkZm9yd2FyZC9mZWVkYmFjawogICAgICoga25vYnMsIHdpdGggTGFncmFuZ2UgRklSIGZyYWN0aW9uYWwtZGVsYXkgaW50ZXJwb2xhdGlvbiAoTGFha3NvIDE5OTYpLiBTZWUKICAgICAqIG1vZHVsYXRlZC1kZWxheS1jb3JlLnRzIGhlYWRlciBmb3IgdGhlIHRyYW5zZmVyIGZ1bmN0aW9uLCBUYWJsZSA2IHByZXNldHMsIHRoZQogICAgICogZml4ZWQtZmVlZGJhY2stdGFwIHJhdGlvbmFsZSwgdGhlIHNpbnVzb2lkYWwgTEZPIGFuZCB0aGUgaW50ZXJwb2xhdGlvbiBjaG9pY2UuCiAgICAgKi8KICAgIGNvbnN0IFdPUktMRVRfTE9HX1BSRUZJWCA9ICJbY2Fjb3Bob255L3dvcmtsZXQ6bW9kdWxhdGVkLWRlbGF5XSI7CiAgICAvKioKICAgICAqIEludGVycG9sYXRpb24gc2VsZWN0aW9uIHJpZGVzIG9uIGFuIEF1ZGlvUGFyYW0gKHdoaWNoIGNhbiBvbmx5IGNhcnJ5IG51bWJlcnMpLAogICAgICogc28gdGhlICJpbnRlcnBvbGF0aW9uIiBwYXJhbSBpcyBhbiBlbnVtIGluZGV4OiAwID0gY3ViaWMgKDQtdGFwIExhZ3JhbmdlIE49MywKICAgICAqIHRoZSBkZWZhdWx0KSwgMSA9IGxpbmVhciAoMi10YXAgTGFncmFuZ2UgTj0xKS4gQm90aCBhcmUgRklSIChMYWFrc28gMTk5NikuCiAgICAgKi8KICAgIGNvbnN0IElOVEVSUE9MQVRJT05fQllfSU5ERVggPSBbImN1YmljIiwgImxpbmVhciJdOwogICAgZnVuY3Rpb24gaW50ZXJwb2xhdGlvbkZyb21JbmRleChpbmRleCkgewogICAgICAgIGNvbnN0IGkgPSBNYXRoLnJvdW5kKGluZGV4KTsKICAgICAgICByZXR1cm4gSU5URVJQT0xBVElPTl9CWV9JTkRFWFtpXSA/PyAiY3ViaWMiOwogICAgfQogICAgY2xhc3MgTW9kdWxhdGVkRGVsYXlXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBkZWxheSBsaW5lLAogICAgICAgIC8vIGZlZWRiYWNrIHJlY2lyY3VsYXRpb24gYW5kIExGTyBwaGFzZSBhY3Jvc3MgcHJvY2VzcygpIGJsb2Nrcy4gRWFjaCBjaGFubmVsJ3MKICAgICAgICAvLyBMRk8gaXMgc2VlZGVkIGluIHF1YWRyYXR1cmUgKGNoICogcGkvMikgc28gYSBzdGVyZW8gcGFpciBnZXRzIHRoZSBkeW5hbWljCiAgICAgICAgLy8gc3RlcmVvIGZpZWxkIERhdHRvcnJvIGRlc2NyaWJlcyAocC43NzYpLgogICAgICAgIGNvcmVzID0gW107CiAgICAgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKICAgICAgICAgICAgLy8gRGVmYXVsdCBWQUxVRVMgY29tZSBmcm9tIE1PRFVMQVRFRF9ERUxBWV9ERUZBVUxUUyAobW9kdWxhdGVkLWRlbGF5LWNvcmUudHMpCiAgICAgICAgICAgIC8vIOKAlCB0aGUgc2luZ2xlIHNvdXJjZSBvZiB0cnV0aCBzaGFyZWQgd2l0aCB0aGUgY29yZSB0ZXN0cy4gUmFuZ2VzIGxpdmUgaGVyZQogICAgICAgICAgICAvLyBhbmQgYm91bmQgdGhlIGJ1ZmZlciBzaXppbmcgKGRlbGF5VGltZSA8PSBNQVhfREVMQVlfTVMsIGRlcHRoIDw9CiAgICAgICAgICAgIC8vIE1BWF9ERVBUSF9NUykgYW5kIHN0YWJpbGl0eSAofGZlZWRiYWNrfCA8PSAwLjk5OTk5OTksIERhdHRvcnJvIFRhYmxlIDYpLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJkZWxheVRpbWUiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuZGVsYXlUaW1lLCAwLCAxMDAwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImRlcHRoIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLmRlcHRoLCAwLCA1MCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJyYXRlIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLnJhdGUsIDAsIDIwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImZlZWRiYWNrIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLmZlZWRiYWNrLCAtMC45OTk5OTk5LCAwLjk5OTk5OTksICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiYmxlbmQiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuYmxlbmQsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZmVlZGZvcndhcmQiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuZmVlZGZvcndhcmQsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiaW50ZXJwb2xhdGlvbiIsIDAsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCB8fCAhb3V0cHV0IHx8IG91dHB1dC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIGstcmF0ZSBwYXJhbXM6IHRha2UgdGhlIGZpcnN0IChjb25zdGFudC1vdmVyLWJsb2NrKSB2YWx1ZS4KICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgZGVsYXlUaW1lOiBwYXJhbWV0ZXJzLmRlbGF5VGltZVswXSwKICAgICAgICAgICAgICAgIGRlcHRoOiBwYXJhbWV0ZXJzLmRlcHRoWzBdLAogICAgICAgICAgICAgICAgcmF0ZTogcGFyYW1ldGVycy5yYXRlWzBdLAogICAgICAgICAgICAgICAgZmVlZGJhY2s6IHBhcmFtZXRlcnMuZmVlZGJhY2tbMF0sCiAgICAgICAgICAgICAgICBibGVuZDogcGFyYW1ldGVycy5ibGVuZFswXSwKICAgICAgICAgICAgICAgIGZlZWRmb3J3YXJkOiBwYXJhbWV0ZXJzLmZlZWRmb3J3YXJkWzBdLAogICAgICAgICAgICAgICAgaW50ZXJwb2xhdGlvbjogaW50ZXJwb2xhdGlvbkZyb21JbmRleChwYXJhbWV0ZXJzLmludGVycG9sYXRpb25bMF0pLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gU2VlZCB0aGUgTEZPIDkwIGRlZyBhcGFydCBwZXIgY2hhbm5lbCBmb3IgdGhlIHF1YWRyYXR1cmUgc3RlcmVvIGZpZWxkCiAgICAgICAgICAgICAgICAgICAgLy8gKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBNb2R1bGF0ZWREZWxheVByb2Nlc3NvcihzYW1wbGVSYXRlLCAoY2ggKiBNYXRoLlBJKSAvIDIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoIm1vZHVsYXRlZC1kZWxheSIsIE1vZHVsYXRlZERlbGF5V29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5Nb2R1bGF0ZWREZWxheVdvcmtsZXRQcm9jZXNzb3IgPSBNb2R1bGF0ZWREZWxheVdvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPW1vZHVsYXRlZC1kZWxheS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHBjbVN0cmVhbSA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNsYXNzIFBjbVJpbmdCdWZmZXIgewogICAgICAgIGNhcGFjaXR5RnJhbWVzOwogICAgICAgIGNoYW5uZWxDb3VudDsKICAgICAgICBjaGFubmVsczsKICAgICAgICByZWFkSW5kZXggPSAwOwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIHNpemUgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKGNhcGFjaXR5RnJhbWVzLCBjaGFubmVsQ291bnQpIHsKICAgICAgICAgICAgdGhpcy5jYXBhY2l0eUZyYW1lcyA9IGNhcGFjaXR5RnJhbWVzOwogICAgICAgICAgICB0aGlzLmNoYW5uZWxDb3VudCA9IGNoYW5uZWxDb3VudDsKICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGNhcGFjaXR5RnJhbWVzKSB8fCBjYXBhY2l0eUZyYW1lcyA8PSAwKSB7CiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcigiUENNIHJpbmctYnVmZmVyIGNhcGFjaXR5IG11c3QgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGNoYW5uZWxDb3VudCkgfHwgY2hhbm5lbENvdW50IDw9IDApIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCJQQ00gY2hhbm5lbCBjb3VudCBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlciIpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbHMgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjaGFubmVsQ291bnQgfSwgKCkgPT4gbmV3IEZsb2F0MzJBcnJheShjYXBhY2l0eUZyYW1lcykpOwogICAgICAgIH0KICAgICAgICBnZXQgYnVmZmVyZWRGcmFtZXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnNpemU7CiAgICAgICAgfQogICAgICAgIHdyaXRlSW50ZXJsZWF2ZWQoc2FtcGxlcykgewogICAgICAgICAgICBpZiAoc2FtcGxlcy5sZW5ndGggJSB0aGlzLmNoYW5uZWxDb3VudCAhPT0gMCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYEludGVybGVhdmVkIFBDTSBsZW5ndGggbXVzdCBiZSBkaXZpc2libGUgYnkgY2hhbm5lbENvdW50ICgke3RoaXMuY2hhbm5lbENvdW50fSlgKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBmcmFtZUNvdW50ID0gc2FtcGxlcy5sZW5ndGggLyB0aGlzLmNoYW5uZWxDb3VudDsKICAgICAgICAgICAgaWYgKGZyYW1lQ291bnQgPiB0aGlzLmNhcGFjaXR5RnJhbWVzIC0gdGhpcy5zaXplKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZm9yIChsZXQgZnJhbWUgPSAwOyBmcmFtZSA8IGZyYW1lQ291bnQ7IGZyYW1lKyspIHsKICAgICAgICAgICAgICAgIGZvciAobGV0IGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbm5lbHNbY2hhbm5lbF1bdGhpcy53cml0ZUluZGV4XSA9IHNhbXBsZXNbZnJhbWUgKiB0aGlzLmNoYW5uZWxDb3VudCArIGNoYW5uZWxdOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICUgdGhpcy5jYXBhY2l0eUZyYW1lczsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLnNpemUgKz0gZnJhbWVDb3VudDsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgICAgIHJlYWQob3V0cHV0KSB7CiAgICAgICAgICAgIGNvbnN0IGZyYW1lQ291bnQgPSBNYXRoLm1pbih0aGlzLnNpemUsIG91dHB1dFswXT8ubGVuZ3RoID8/IDApOwogICAgICAgICAgICBmb3IgKGxldCBmcmFtZSA9IDA7IGZyYW1lIDwgZnJhbWVDb3VudDsgZnJhbWUrKykgewogICAgICAgICAgICAgICAgZm9yIChsZXQgY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCBNYXRoLm1pbih0aGlzLmNoYW5uZWxDb3VudCwgb3V0cHV0Lmxlbmd0aCk7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgICAgIG91dHB1dFtjaGFubmVsXVtmcmFtZV0gPSB0aGlzLmNoYW5uZWxzW2NoYW5uZWxdW3RoaXMucmVhZEluZGV4XTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMucmVhZEluZGV4ID0gKHRoaXMucmVhZEluZGV4ICsgMSkgJSB0aGlzLmNhcGFjaXR5RnJhbWVzOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuc2l6ZSAtPSBmcmFtZUNvdW50OwogICAgICAgICAgICByZXR1cm4gZnJhbWVDb3VudDsKICAgICAgICB9CiAgICAgICAgY2xlYXIoKSB7CiAgICAgICAgICAgIHRoaXMucmVhZEluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5zaXplID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIENvbnRleHQtZnJlZSBzdGF0ZSBtYWNoaW5lIGZvciB0aGUgUENNIHN0cmVhbSB3b3JrbGV0LgogICAgICoKICAgICAqIFRoZSB3b3JrbGV0IHNoZWxsIG93bnMgTWVzc2FnZVBvcnQgcGx1bWJpbmc7IHRoaXMgY2xhc3Mgb3ducyB0aGUgZml4ZWQtc2l6ZQogICAgICogcmluZyBidWZmZXIsIGxhdGVuY3kgZ2F0ZSwgcGF1c2UvcmVzdW1lIHBvc2l0aW9uLCB1bmRlcnJ1biBlcGlzb2RlcywgYW5kCiAgICAgKiB0ZXJtaW5hbCBlbmQtb2YtaW5wdXQgYmVoYXZpb3IuCiAgICAgKi8KICAgIGNsYXNzIFBjbVN0cmVhbUVuZ2luZSB7CiAgICAgICAgcmluZzsKICAgICAgICBsYXRlbmN5RnJhbWVzOwogICAgICAgIHBsYXlpbmcgPSBmYWxzZTsKICAgICAgICBzdGFydGVkID0gZmFsc2U7CiAgICAgICAgaW5wdXRFbmRlZCA9IGZhbHNlOwogICAgICAgIHVuZGVycnVuQWN0aXZlID0gZmFsc2U7CiAgICAgICAgZW5kZWRSZXBvcnRlZCA9IGZhbHNlOwogICAgICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKG9wdGlvbnMubGF0ZW5jeUZyYW1lcykgfHwgb3B0aW9ucy5sYXRlbmN5RnJhbWVzIDwgMCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIlBDTSBsYXRlbmN5IG11c3QgYmUgYSBub24tbmVnYXRpdmUgaW50ZWdlciBudW1iZXIgb2YgZnJhbWVzIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKG9wdGlvbnMubGF0ZW5jeUZyYW1lcyA+IG9wdGlvbnMuY2FwYWNpdHlGcmFtZXMpIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCJQQ00gbGF0ZW5jeSBjYW5ub3QgZXhjZWVkIHRoZSByaW5nLWJ1ZmZlciBjYXBhY2l0eSIpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMucmluZyA9IG5ldyBQY21SaW5nQnVmZmVyKG9wdGlvbnMuY2FwYWNpdHlGcmFtZXMsIG9wdGlvbnMuY2hhbm5lbENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5RnJhbWVzID0gb3B0aW9ucy5sYXRlbmN5RnJhbWVzOwogICAgICAgIH0KICAgICAgICBnZXQgYnVmZmVyZWRGcmFtZXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnJpbmcuYnVmZmVyZWRGcmFtZXM7CiAgICAgICAgfQogICAgICAgIHdyaXRlSW50ZXJsZWF2ZWQoc2FtcGxlcykgewogICAgICAgICAgICBpZiAodGhpcy5pbnB1dEVuZGVkKSB7CiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCB3cml0ZSBQQ00gYWZ0ZXIgZW5kKCkiKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBhY2NlcHRlZCA9IHRoaXMucmluZy53cml0ZUludGVybGVhdmVkKHNhbXBsZXMpOwogICAgICAgICAgICBpZiAoYWNjZXB0ZWQpIHsKICAgICAgICAgICAgICAgIHRoaXMudW5kZXJydW5BY3RpdmUgPSBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gYWNjZXB0ZWQ7CiAgICAgICAgfQogICAgICAgIHBsYXkoKSB7CiAgICAgICAgICAgIGlmICghdGhpcy5lbmRlZFJlcG9ydGVkKSB7CiAgICAgICAgICAgICAgICB0aGlzLnBsYXlpbmcgPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHBhdXNlKCkgewogICAgICAgICAgICB0aGlzLnBsYXlpbmcgPSBmYWxzZTsKICAgICAgICB9CiAgICAgICAgc3RvcCgpIHsKICAgICAgICAgICAgdGhpcy5yaW5nLmNsZWFyKCk7CiAgICAgICAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlOwogICAgICAgICAgICB0aGlzLnN0YXJ0ZWQgPSBmYWxzZTsKICAgICAgICAgICAgdGhpcy5pbnB1dEVuZGVkID0gZmFsc2U7CiAgICAgICAgICAgIHRoaXMudW5kZXJydW5BY3RpdmUgPSBmYWxzZTsKICAgICAgICAgICAgdGhpcy5lbmRlZFJlcG9ydGVkID0gZmFsc2U7CiAgICAgICAgfQogICAgICAgIGVuZCgpIHsKICAgICAgICAgICAgdGhpcy5pbnB1dEVuZGVkID0gdHJ1ZTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhvdXRwdXQpIHsKICAgICAgICAgICAgZm9yIChjb25zdCBjaGFubmVsIG9mIG91dHB1dCkgewogICAgICAgICAgICAgICAgY2hhbm5lbC5maWxsKDApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHsKICAgICAgICAgICAgICAgIGNvbnN1bWVkRnJhbWVzOiAwLAogICAgICAgICAgICAgICAgZW5kZWQ6IGZhbHNlLAogICAgICAgICAgICAgICAgdW5kZXJydW46IGZhbHNlLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBxdWFudHVtRnJhbWVzID0gb3V0cHV0WzBdPy5sZW5ndGggPz8gMDsKICAgICAgICAgICAgaWYgKCF0aGlzLnBsYXlpbmcgfHwgcXVhbnR1bUZyYW1lcyA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoIXRoaXMuc3RhcnRlZCkgewogICAgICAgICAgICAgICAgaWYgKHRoaXMucmluZy5idWZmZXJlZEZyYW1lcyA8IHRoaXMubGF0ZW5jeUZyYW1lcyAmJiAhdGhpcy5pbnB1dEVuZGVkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRlZCA9IHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmVzdWx0LmNvbnN1bWVkRnJhbWVzID0gdGhpcy5yaW5nLnJlYWQob3V0cHV0KTsKICAgICAgICAgICAgaWYgKHRoaXMuaW5wdXRFbmRlZCAmJiB0aGlzLnJpbmcuYnVmZmVyZWRGcmFtZXMgPT09IDApIHsKICAgICAgICAgICAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlOwogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmVuZGVkUmVwb3J0ZWQpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZGVkUmVwb3J0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHJlc3VsdC5lbmRlZCA9IHRydWU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChyZXN1bHQuY29uc3VtZWRGcmFtZXMgPCBxdWFudHVtRnJhbWVzICYmIHRoaXMucmluZy5idWZmZXJlZEZyYW1lcyA9PT0gMCAmJiAhdGhpcy51bmRlcnJ1bkFjdGl2ZSkgewogICAgICAgICAgICAgICAgdGhpcy51bmRlcnJ1bkFjdGl2ZSA9IHRydWU7CiAgICAgICAgICAgICAgICByZXN1bHQudW5kZXJydW4gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQXVkaW9Xb3JrbGV0IHNoZWxsIGZvciBwdXNoLWJhc2VkIFBDTSBwbGF5YmFjay4KICAgICAqCiAgICAgKiBBbGwgYnVmZmVyaW5nIGFuZCBwbGF5YmFjayBzdGF0ZSBsaXZlcyBpbiB0aGUgY29udGV4dC1mcmVlCiAgICAgKiB7QGxpbmsgUGNtU3RyZWFtRW5naW5lfTsgdGhpcyBjbGFzcyBvbmx5IHRyYW5zbGF0ZXMgTWVzc2FnZVBvcnQgY29tbWFuZHMsCiAgICAgKiByZW5kZXJzIG9uZSBXZWIgQXVkaW8gcXVhbnR1bSwgYW5kIHJlcG9ydHMgY29uc3VtcHRpb24vc3RhdGUgdXBzdHJlYW0uCiAgICAgKi8KICAgIGNsYXNzIFBjbVN0cmVhbVdvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIGVuZ2luZTsKICAgICAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CiAgICAgICAgICAgIHN1cGVyKG9wdGlvbnMpOwogICAgICAgICAgICBjb25zdCBwcm9jZXNzb3JPcHRpb25zID0gKG9wdGlvbnM/LnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwogICAgICAgICAgICB0aGlzLmVuZ2luZSA9IG5ldyBQY21TdHJlYW1FbmdpbmUoewogICAgICAgICAgICAgICAgY2FwYWNpdHlGcmFtZXM6IHByb2Nlc3Nvck9wdGlvbnMuY2FwYWNpdHlGcmFtZXMgPz8gc2FtcGxlUmF0ZSwKICAgICAgICAgICAgICAgIGNoYW5uZWxDb3VudDogcHJvY2Vzc29yT3B0aW9ucy5jaGFubmVsQ291bnQgPz8gMSwKICAgICAgICAgICAgICAgIGxhdGVuY3lGcmFtZXM6IHByb2Nlc3Nvck9wdGlvbnMubGF0ZW5jeUZyYW1lcyA/PyAwLAogICAgICAgICAgICB9KTsKICAgICAgICAgICAgdGhpcy5wb3J0Lm9ubWVzc2FnZSA9IChldmVudCkgPT4gewogICAgICAgICAgICAgICAgY29uc3QgY29tbWFuZCA9IGV2ZW50LmRhdGE7CiAgICAgICAgICAgICAgICBzd2l0Y2ggKGNvbW1hbmQudHlwZSkgewogICAgICAgICAgICAgICAgICAgIGNhc2UgIndyaXRlIjoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLmVuZ2luZS53cml0ZUludGVybGVhdmVkKGNvbW1hbmQuc2FtcGxlcykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZSh7IHR5cGU6ICJvdmVyZmxvdyIgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSAicGxheSI6CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZW5naW5lLnBsYXkoKTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSAicGF1c2UiOgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5wYXVzZSgpOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlICJzdG9wIjoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbmdpbmUuc3RvcCgpOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlICJlbmQiOgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5lbmQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoX2lucHV0cywgb3V0cHV0cykgewogICAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLmVuZ2luZS5wcm9jZXNzKG91dHB1dHNbMF0gPz8gW10pOwogICAgICAgICAgICBpZiAocmVzdWx0LmNvbnN1bWVkRnJhbWVzID4gMCkgewogICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHsgdHlwZTogImNvbnN1bWVkIiwgZnJhbWVzOiByZXN1bHQuY29uc3VtZWRGcmFtZXMgfSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKHJlc3VsdC51bmRlcnJ1bikgewogICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHsgdHlwZTogInVuZGVycnVuIiB9KTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzdWx0LmVuZGVkKSB7CiAgICAgICAgICAgICAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2UoeyB0eXBlOiAiZW5kZWQiIH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJwY20tc3RyZWFtIiwgUGNtU3RyZWFtV29ya2xldFByb2Nlc3Nvcik7CgogICAgZXhwb3J0cy5QY21TdHJlYW1Xb3JrbGV0UHJvY2Vzc29yID0gUGNtU3RyZWFtV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9cGNtLXN0cmVhbS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHBoYXNlVm9jb2RlciA9IChmdW5jdGlvbiAoZXhwb3J0cykgewoJJ3VzZSBzdHJpY3QnOwoKCWZ1bmN0aW9uIGdldERlZmF1bHRFeHBvcnRGcm9tQ2pzICh4KSB7CgkJcmV0dXJuIHggJiYgeC5fX2VzTW9kdWxlICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnZGVmYXVsdCcpID8geFsnZGVmYXVsdCddIDogeDsKCX0KCgl2YXIgZmZ0OwoJdmFyIGhhc1JlcXVpcmVkRmZ0OwoKCWZ1bmN0aW9uIHJlcXVpcmVGZnQgKCkgewoJCWlmIChoYXNSZXF1aXJlZEZmdCkgcmV0dXJuIGZmdDsKCQloYXNSZXF1aXJlZEZmdCA9IDE7CgoJCWZ1bmN0aW9uIEZGVChzaXplKSB7CgkJICB0aGlzLnNpemUgPSBzaXplIHwgMDsKCQkgIGlmICh0aGlzLnNpemUgPD0gMSB8fCAodGhpcy5zaXplICYgKHRoaXMuc2l6ZSAtIDEpKSAhPT0gMCkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdGRlQgc2l6ZSBtdXN0IGJlIGEgcG93ZXIgb2YgdHdvIGFuZCBiaWdnZXIgdGhhbiAxJyk7CgoJCSAgdGhpcy5fY3NpemUgPSBzaXplIDw8IDE7CgoJCSAgLy8gTk9URTogVXNlIG9mIGB2YXJgIGlzIGludGVudGlvbmFsIGZvciBvbGQgVjggdmVyc2lvbnMKCQkgIHZhciB0YWJsZSA9IG5ldyBBcnJheSh0aGlzLnNpemUgKiAyKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgdGFibGUubGVuZ3RoOyBpICs9IDIpIHsKCQkgICAgY29uc3QgYW5nbGUgPSBNYXRoLlBJICogaSAvIHRoaXMuc2l6ZTsKCQkgICAgdGFibGVbaV0gPSBNYXRoLmNvcyhhbmdsZSk7CgkJICAgIHRhYmxlW2kgKyAxXSA9IC1NYXRoLnNpbihhbmdsZSk7CgkJICB9CgkJICB0aGlzLnRhYmxlID0gdGFibGU7CgoJCSAgLy8gRmluZCBzaXplJ3MgcG93ZXIgb2YgdHdvCgkJICB2YXIgcG93ZXIgPSAwOwoJCSAgZm9yICh2YXIgdCA9IDE7IHRoaXMuc2l6ZSA+IHQ7IHQgPDw9IDEpCgkJICAgIHBvd2VyKys7CgoJCSAgLy8gQ2FsY3VsYXRlIGluaXRpYWwgc3RlcCdzIHdpZHRoOgoJCSAgLy8gICAqIElmIHdlIGFyZSBmdWxsIHJhZGl4LTQgLSBpdCBpcyAyeCBzbWFsbGVyIHRvIGdpdmUgaW5pdGFsIGxlbj04CgkJICAvLyAgICogT3RoZXJ3aXNlIGl0IGlzIHRoZSBzYW1lIGFzIGBwb3dlcmAgdG8gZ2l2ZSBsZW49NAoJCSAgdGhpcy5fd2lkdGggPSBwb3dlciAlIDIgPT09IDAgPyBwb3dlciAtIDEgOiBwb3dlcjsKCgkJICAvLyBQcmUtY29tcHV0ZSBiaXQtcmV2ZXJzYWwgcGF0dGVybnMKCQkgIHRoaXMuX2JpdHJldiA9IG5ldyBBcnJheSgxIDw8IHRoaXMuX3dpZHRoKTsKCQkgIGZvciAodmFyIGogPSAwOyBqIDwgdGhpcy5fYml0cmV2Lmxlbmd0aDsgaisrKSB7CgkJICAgIHRoaXMuX2JpdHJldltqXSA9IDA7CgkJICAgIGZvciAodmFyIHNoaWZ0ID0gMDsgc2hpZnQgPCB0aGlzLl93aWR0aDsgc2hpZnQgKz0gMikgewoJCSAgICAgIHZhciByZXZTaGlmdCA9IHRoaXMuX3dpZHRoIC0gc2hpZnQgLSAyOwoJCSAgICAgIHRoaXMuX2JpdHJldltqXSB8PSAoKGogPj4+IHNoaWZ0KSAmIDMpIDw8IHJldlNoaWZ0OwoJCSAgICB9CgkJICB9CgoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCSAgdGhpcy5faW52ID0gMDsKCQl9CgkJZmZ0ID0gRkZUOwoKCQlGRlQucHJvdG90eXBlLmZyb21Db21wbGV4QXJyYXkgPSBmdW5jdGlvbiBmcm9tQ29tcGxleEFycmF5KGNvbXBsZXgsIHN0b3JhZ2UpIHsKCQkgIHZhciByZXMgPSBzdG9yYWdlIHx8IG5ldyBBcnJheShjb21wbGV4Lmxlbmd0aCA+Pj4gMSk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbXBsZXgubGVuZ3RoOyBpICs9IDIpCgkJICAgIHJlc1tpID4+PiAxXSA9IGNvbXBsZXhbaV07CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuY3JlYXRlQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gY3JlYXRlQ29tcGxleEFycmF5KCkgewoJCSAgY29uc3QgcmVzID0gbmV3IEFycmF5KHRoaXMuX2NzaXplKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzLmxlbmd0aDsgaSsrKQoJCSAgICByZXNbaV0gPSAwOwoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLnRvQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gdG9Db21wbGV4QXJyYXkoaW5wdXQsIHN0b3JhZ2UpIHsKCQkgIHZhciByZXMgPSBzdG9yYWdlIHx8IHRoaXMuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHJlcy5sZW5ndGg7IGkgKz0gMikgewoJCSAgICByZXNbaV0gPSBpbnB1dFtpID4+PiAxXTsKCQkgICAgcmVzW2kgKyAxXSA9IDA7CgkJICB9CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuY29tcGxldGVTcGVjdHJ1bSA9IGZ1bmN0aW9uIGNvbXBsZXRlU3BlY3RydW0oc3BlY3RydW0pIHsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgkJICB2YXIgaGFsZiA9IHNpemUgPj4+IDE7CgkJICBmb3IgKHZhciBpID0gMjsgaSA8IGhhbGY7IGkgKz0gMikgewoJCSAgICBzcGVjdHJ1bVtzaXplIC0gaV0gPSBzcGVjdHJ1bVtpXTsKCQkgICAgc3BlY3RydW1bc2l6ZSAtIGkgKyAxXSA9IC1zcGVjdHJ1bVtpICsgMV07CgkJICB9CgkJfTsKCgkJRkZULnByb3RvdHlwZS50cmFuc2Zvcm0gPSBmdW5jdGlvbiB0cmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMDsKCQkgIHRoaXMuX3RyYW5zZm9ybTQoKTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQlGRlQucHJvdG90eXBlLnJlYWxUcmFuc2Zvcm0gPSBmdW5jdGlvbiByZWFsVHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDA7CgkJICB0aGlzLl9yZWFsVHJhbnNmb3JtNCgpOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuaW52ZXJzZVRyYW5zZm9ybSA9IGZ1bmN0aW9uIGludmVyc2VUcmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMTsKCQkgIHRoaXMuX3RyYW5zZm9ybTQoKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgb3V0Lmxlbmd0aDsgaSsrKQoJCSAgICBvdXRbaV0gLz0gdGhpcy5zaXplOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCS8vIHJhZGl4LTQgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IFVzZXMgb2YgYHZhcmAgYXJlIGludGVudGlvbmFsIGZvciBvbGRlciBWOCB2ZXJzaW9uIHRoYXQgZG8gbm90CgkJLy8gc3VwcG9ydCBib3RoIGBsZXQgY29tcG91bmQgYXNzaWdubWVudHNgIGFuZCBgY29uc3QgcGhpYAoJCUZGVC5wcm90b3R5cGUuX3RyYW5zZm9ybTQgPSBmdW5jdGlvbiBfdHJhbnNmb3JtNCgpIHsKCQkgIHZhciBvdXQgPSB0aGlzLl9vdXQ7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoKCQkgIC8vIEluaXRpYWwgc3RlcCAocGVybXV0ZSBhbmQgdHJhbnNmb3JtKQoJCSAgdmFyIHdpZHRoID0gdGhpcy5fd2lkdGg7CgkJICB2YXIgc3RlcCA9IDEgPDwgd2lkdGg7CgkJICB2YXIgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoKCQkgIHZhciBvdXRPZmY7CgkJICB2YXIgdDsKCQkgIHZhciBiaXRyZXYgPSB0aGlzLl9iaXRyZXY7CgkJICBpZiAobGVuID09PSA0KSB7CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmLCBzdGVwKTsKCQkgICAgfQoJCSAgfSBlbHNlIHsKCQkgICAgLy8gbGVuID09PSA4CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVUcmFuc2Zvcm00KG91dE9mZiwgb2ZmLCBzdGVwKTsKCQkgICAgfQoJCSAgfQoKCQkgIC8vIExvb3AgdGhyb3VnaCBzdGVwcyBpbiBkZWNyZWFzaW5nIG9yZGVyCgkJICB2YXIgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgdmFyIHRhYmxlID0gdGhpcy50YWJsZTsKCQkgIGZvciAoc3RlcCA+Pj0gMjsgc3RlcCA+PSAyOyBzdGVwID4+PSAyKSB7CgkJICAgIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCQkgICAgdmFyIHF1YXJ0ZXJMZW4gPSBsZW4gPj4+IDI7CgoJCSAgICAvLyBMb29wIHRocm91Z2ggb2Zmc2V0cyBpbiB0aGUgZGF0YQoJCSAgICBmb3IgKG91dE9mZiA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4pIHsKCQkgICAgICAvLyBGdWxsIGNhc2UKCQkgICAgICB2YXIgbGltaXQgPSBvdXRPZmYgKyBxdWFydGVyTGVuOwoJCSAgICAgIGZvciAodmFyIGkgPSBvdXRPZmYsIGsgPSAwOyBpIDwgbGltaXQ7IGkgKz0gMiwgayArPSBzdGVwKSB7CgkJICAgICAgICBjb25zdCBBID0gaTsKCQkgICAgICAgIGNvbnN0IEIgPSBBICsgcXVhcnRlckxlbjsKCQkgICAgICAgIGNvbnN0IEMgPSBCICsgcXVhcnRlckxlbjsKCQkgICAgICAgIGNvbnN0IEQgPSBDICsgcXVhcnRlckxlbjsKCgkJICAgICAgICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IEFyID0gb3V0W0FdOwoJCSAgICAgICAgY29uc3QgQWkgPSBvdXRbQSArIDFdOwoJCSAgICAgICAgY29uc3QgQnIgPSBvdXRbQl07CgkJICAgICAgICBjb25zdCBCaSA9IG91dFtCICsgMV07CgkJICAgICAgICBjb25zdCBDciA9IG91dFtDXTsKCQkgICAgICAgIGNvbnN0IENpID0gb3V0W0MgKyAxXTsKCQkgICAgICAgIGNvbnN0IERyID0gb3V0W0RdOwoJCSAgICAgICAgY29uc3QgRGkgPSBvdXRbRCArIDFdOwoKCQkgICAgICAgIC8vIE1pZGRsZSB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IE1BciA9IEFyOwoJCSAgICAgICAgY29uc3QgTUFpID0gQWk7CgoJCSAgICAgICAgY29uc3QgdGFibGVCciA9IHRhYmxlW2tdOwoJCSAgICAgICAgY29uc3QgdGFibGVCaSA9IGludiAqIHRhYmxlW2sgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1CciA9IEJyICogdGFibGVCciAtIEJpICogdGFibGVCaTsKCQkgICAgICAgIGNvbnN0IE1CaSA9IEJyICogdGFibGVCaSArIEJpICogdGFibGVCcjsKCgkJICAgICAgICBjb25zdCB0YWJsZUNyID0gdGFibGVbMiAqIGtdOwoJCSAgICAgICAgY29uc3QgdGFibGVDaSA9IGludiAqIHRhYmxlWzIgKiBrICsgMV07CgkJICAgICAgICBjb25zdCBNQ3IgPSBDciAqIHRhYmxlQ3IgLSBDaSAqIHRhYmxlQ2k7CgkJICAgICAgICBjb25zdCBNQ2kgPSBDciAqIHRhYmxlQ2kgKyBDaSAqIHRhYmxlQ3I7CgoJCSAgICAgICAgY29uc3QgdGFibGVEciA9IHRhYmxlWzMgKiBrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlRGkgPSBpbnYgKiB0YWJsZVszICogayArIDFdOwoJCSAgICAgICAgY29uc3QgTURyID0gRHIgKiB0YWJsZURyIC0gRGkgKiB0YWJsZURpOwoJCSAgICAgICAgY29uc3QgTURpID0gRHIgKiB0YWJsZURpICsgRGkgKiB0YWJsZURyOwoKCQkgICAgICAgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IFQwciA9IE1BciArIE1DcjsKCQkgICAgICAgIGNvbnN0IFQwaSA9IE1BaSArIE1DaTsKCQkgICAgICAgIGNvbnN0IFQxciA9IE1BciAtIE1DcjsKCQkgICAgICAgIGNvbnN0IFQxaSA9IE1BaSAtIE1DaTsKCQkgICAgICAgIGNvbnN0IFQyciA9IE1CciArIE1EcjsKCQkgICAgICAgIGNvbnN0IFQyaSA9IE1CaSArIE1EaTsKCQkgICAgICAgIGNvbnN0IFQzciA9IGludiAqIChNQnIgLSBNRHIpOwoJCSAgICAgICAgY29uc3QgVDNpID0gaW52ICogKE1CaSAtIE1EaSk7CgoJCSAgICAgICAgLy8gRmluYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgkJICAgICAgICBjb25zdCBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgICAgICAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoJCSAgICAgICAgY29uc3QgRkNpID0gVDBpIC0gVDJpOwoKCQkgICAgICAgIGNvbnN0IEZCciA9IFQxciArIFQzaTsKCQkgICAgICAgIGNvbnN0IEZCaSA9IFQxaSAtIFQzcjsKCgkJICAgICAgICBjb25zdCBGRHIgPSBUMXIgLSBUM2k7CgkJICAgICAgICBjb25zdCBGRGkgPSBUMWkgKyBUM3I7CgoJCSAgICAgICAgb3V0W0FdID0gRkFyOwoJCSAgICAgICAgb3V0W0EgKyAxXSA9IEZBaTsKCQkgICAgICAgIG91dFtCXSA9IEZCcjsKCQkgICAgICAgIG91dFtCICsgMV0gPSBGQmk7CgkJICAgICAgICBvdXRbQ10gPSBGQ3I7CgkJICAgICAgICBvdXRbQyArIDFdID0gRkNpOwoJCSAgICAgICAgb3V0W0RdID0gRkRyOwoJCSAgICAgICAgb3V0W0QgKyAxXSA9IEZEaTsKCQkgICAgICB9CgkJICAgIH0KCQkgIH0KCQl9OwoKCQkvLyByYWRpeC0yIGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTQKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVUcmFuc2Zvcm0yID0gZnVuY3Rpb24gX3NpbmdsZVRyYW5zZm9ybTIob3V0T2ZmLCBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoKCQkgIGNvbnN0IGV2ZW5SID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgZXZlbkkgPSBkYXRhW29mZiArIDFdOwoJCSAgY29uc3Qgb2RkUiA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBvZGRJID0gZGF0YVtvZmYgKyBzdGVwICsgMV07CgoJCSAgY29uc3QgbGVmdFIgPSBldmVuUiArIG9kZFI7CgkJICBjb25zdCBsZWZ0SSA9IGV2ZW5JICsgb2RkSTsKCQkgIGNvbnN0IHJpZ2h0UiA9IGV2ZW5SIC0gb2RkUjsKCQkgIGNvbnN0IHJpZ2h0SSA9IGV2ZW5JIC0gb2RkSTsKCgkJICBvdXRbb3V0T2ZmXSA9IGxlZnRSOwoJCSAgb3V0W291dE9mZiArIDFdID0gbGVmdEk7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSByaWdodFI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSByaWdodEk7CgkJfTsKCgkJLy8gcmFkaXgtNAoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj04CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9zaW5nbGVUcmFuc2Zvcm00KG91dE9mZiwgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCQkgIGNvbnN0IGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIGNvbnN0IHN0ZXAyID0gc3RlcCAqIDI7CgkJICBjb25zdCBzdGVwMyA9IHN0ZXAgKiAzOwoKCQkgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgY29uc3QgQXIgPSBkYXRhW29mZl07CgkJICBjb25zdCBBaSA9IGRhdGFbb2ZmICsgMV07CgkJICBjb25zdCBCciA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBCaSA9IGRhdGFbb2ZmICsgc3RlcCArIDFdOwoJCSAgY29uc3QgQ3IgPSBkYXRhW29mZiArIHN0ZXAyXTsKCQkgIGNvbnN0IENpID0gZGF0YVtvZmYgKyBzdGVwMiArIDFdOwoJCSAgY29uc3QgRHIgPSBkYXRhW29mZiArIHN0ZXAzXTsKCQkgIGNvbnN0IERpID0gZGF0YVtvZmYgKyBzdGVwMyArIDFdOwoKCQkgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IFQwciA9IEFyICsgQ3I7CgkJICBjb25zdCBUMGkgPSBBaSArIENpOwoJCSAgY29uc3QgVDFyID0gQXIgLSBDcjsKCQkgIGNvbnN0IFQxaSA9IEFpIC0gQ2k7CgkJICBjb25zdCBUMnIgPSBCciArIERyOwoJCSAgY29uc3QgVDJpID0gQmkgKyBEaTsKCQkgIGNvbnN0IFQzciA9IGludiAqIChCciAtIERyKTsKCQkgIGNvbnN0IFQzaSA9IGludiAqIChCaSAtIERpKTsKCgkJICAvLyBGaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCQkgIGNvbnN0IEZBaSA9IFQwaSArIFQyaTsKCgkJICBjb25zdCBGQnIgPSBUMXIgKyBUM2k7CgkJICBjb25zdCBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoJCSAgY29uc3QgRkNpID0gVDBpIC0gVDJpOwoKCQkgIGNvbnN0IEZEciA9IFQxciAtIFQzaTsKCQkgIGNvbnN0IEZEaSA9IFQxaSArIFQzcjsKCgkJICBvdXRbb3V0T2ZmXSA9IEZBcjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IEZBaTsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IEZCcjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IEZCaTsKCQkgIG91dFtvdXRPZmYgKyA0XSA9IEZDcjsKCQkgIG91dFtvdXRPZmYgKyA1XSA9IEZDaTsKCQkgIG91dFtvdXRPZmYgKyA2XSA9IEZEcjsKCQkgIG91dFtvdXRPZmYgKyA3XSA9IEZEaTsKCQl9OwoKCQkvLyBSZWFsIGlucHV0IHJhZGl4LTQgaW1wbGVtZW50YXRpb24KCQlGRlQucHJvdG90eXBlLl9yZWFsVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9yZWFsVHJhbnNmb3JtNCgpIHsKCQkgIHZhciBvdXQgPSB0aGlzLl9vdXQ7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoKCQkgIC8vIEluaXRpYWwgc3RlcCAocGVybXV0ZSBhbmQgdHJhbnNmb3JtKQoJCSAgdmFyIHdpZHRoID0gdGhpcy5fd2lkdGg7CgkJICB2YXIgc3RlcCA9IDEgPDwgd2lkdGg7CgkJICB2YXIgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoKCQkgIHZhciBvdXRPZmY7CgkJICB2YXIgdDsKCQkgIHZhciBiaXRyZXYgPSB0aGlzLl9iaXRyZXY7CgkJICBpZiAobGVuID09PSA0KSB7CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVSZWFsVHJhbnNmb3JtMihvdXRPZmYsIG9mZiA+Pj4gMSwgc3RlcCA+Pj4gMSk7CgkJICAgIH0KCQkgIH0gZWxzZSB7CgkJICAgIC8vIGxlbiA9PT0gOAoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlUmVhbFRyYW5zZm9ybTQob3V0T2ZmLCBvZmYgPj4+IDEsIHN0ZXAgPj4+IDEpOwoJCSAgICB9CgkJICB9CgoJCSAgLy8gTG9vcCB0aHJvdWdoIHN0ZXBzIGluIGRlY3JlYXNpbmcgb3JkZXIKCQkgIHZhciBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICB2YXIgdGFibGUgPSB0aGlzLnRhYmxlOwoJCSAgZm9yIChzdGVwID4+PSAyOyBzdGVwID49IDI7IHN0ZXAgPj49IDIpIHsKCQkgICAgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoJCSAgICB2YXIgaGFsZkxlbiA9IGxlbiA+Pj4gMTsKCQkgICAgdmFyIHF1YXJ0ZXJMZW4gPSBoYWxmTGVuID4+PiAxOwoJCSAgICB2YXIgaHF1YXJ0ZXJMZW4gPSBxdWFydGVyTGVuID4+PiAxOwoKCQkgICAgLy8gTG9vcCB0aHJvdWdoIG9mZnNldHMgaW4gdGhlIGRhdGEKCQkgICAgZm9yIChvdXRPZmYgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuKSB7CgkJICAgICAgZm9yICh2YXIgaSA9IDAsIGsgPSAwOyBpIDw9IGhxdWFydGVyTGVuOyBpICs9IDIsIGsgKz0gc3RlcCkgewoJCSAgICAgICAgdmFyIEEgPSBvdXRPZmYgKyBpOwoJCSAgICAgICAgdmFyIEIgPSBBICsgcXVhcnRlckxlbjsKCQkgICAgICAgIHZhciBDID0gQiArIHF1YXJ0ZXJMZW47CgkJICAgICAgICB2YXIgRCA9IEMgKyBxdWFydGVyTGVuOwoKCQkgICAgICAgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIEFyID0gb3V0W0FdOwoJCSAgICAgICAgdmFyIEFpID0gb3V0W0EgKyAxXTsKCQkgICAgICAgIHZhciBCciA9IG91dFtCXTsKCQkgICAgICAgIHZhciBCaSA9IG91dFtCICsgMV07CgkJICAgICAgICB2YXIgQ3IgPSBvdXRbQ107CgkJICAgICAgICB2YXIgQ2kgPSBvdXRbQyArIDFdOwoJCSAgICAgICAgdmFyIERyID0gb3V0W0RdOwoJCSAgICAgICAgdmFyIERpID0gb3V0W0QgKyAxXTsKCgkJICAgICAgICAvLyBNaWRkbGUgdmFsdWVzCgkJICAgICAgICB2YXIgTUFyID0gQXI7CgkJICAgICAgICB2YXIgTUFpID0gQWk7CgoJCSAgICAgICAgdmFyIHRhYmxlQnIgPSB0YWJsZVtrXTsKCQkgICAgICAgIHZhciB0YWJsZUJpID0gaW52ICogdGFibGVbayArIDFdOwoJCSAgICAgICAgdmFyIE1CciA9IEJyICogdGFibGVCciAtIEJpICogdGFibGVCaTsKCQkgICAgICAgIHZhciBNQmkgPSBCciAqIHRhYmxlQmkgKyBCaSAqIHRhYmxlQnI7CgoJCSAgICAgICAgdmFyIHRhYmxlQ3IgPSB0YWJsZVsyICoga107CgkJICAgICAgICB2YXIgdGFibGVDaSA9IGludiAqIHRhYmxlWzIgKiBrICsgMV07CgkJICAgICAgICB2YXIgTUNyID0gQ3IgKiB0YWJsZUNyIC0gQ2kgKiB0YWJsZUNpOwoJCSAgICAgICAgdmFyIE1DaSA9IENyICogdGFibGVDaSArIENpICogdGFibGVDcjsKCgkJICAgICAgICB2YXIgdGFibGVEciA9IHRhYmxlWzMgKiBrXTsKCQkgICAgICAgIHZhciB0YWJsZURpID0gaW52ICogdGFibGVbMyAqIGsgKyAxXTsKCQkgICAgICAgIHZhciBNRHIgPSBEciAqIHRhYmxlRHIgLSBEaSAqIHRhYmxlRGk7CgkJICAgICAgICB2YXIgTURpID0gRHIgKiB0YWJsZURpICsgRGkgKiB0YWJsZURyOwoKCQkgICAgICAgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBUMHIgPSBNQXIgKyBNQ3I7CgkJICAgICAgICB2YXIgVDBpID0gTUFpICsgTUNpOwoJCSAgICAgICAgdmFyIFQxciA9IE1BciAtIE1DcjsKCQkgICAgICAgIHZhciBUMWkgPSBNQWkgLSBNQ2k7CgkJICAgICAgICB2YXIgVDJyID0gTUJyICsgTURyOwoJCSAgICAgICAgdmFyIFQyaSA9IE1CaSArIE1EaTsKCQkgICAgICAgIHZhciBUM3IgPSBpbnYgKiAoTUJyIC0gTURyKTsKCQkgICAgICAgIHZhciBUM2kgPSBpbnYgKiAoTUJpIC0gTURpKTsKCgkJICAgICAgICAvLyBGaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBGQXIgPSBUMHIgKyBUMnI7CgkJICAgICAgICB2YXIgRkFpID0gVDBpICsgVDJpOwoKCQkgICAgICAgIHZhciBGQnIgPSBUMXIgKyBUM2k7CgkJICAgICAgICB2YXIgRkJpID0gVDFpIC0gVDNyOwoKCQkgICAgICAgIG91dFtBXSA9IEZBcjsKCQkgICAgICAgIG91dFtBICsgMV0gPSBGQWk7CgkJICAgICAgICBvdXRbQl0gPSBGQnI7CgkJICAgICAgICBvdXRbQiArIDFdID0gRkJpOwoKCQkgICAgICAgIC8vIE91dHB1dCBmaW5hbCBtaWRkbGUgcG9pbnQKCQkgICAgICAgIGlmIChpID09PSAwKSB7CgkJICAgICAgICAgIHZhciBGQ3IgPSBUMHIgLSBUMnI7CgkJICAgICAgICAgIHZhciBGQ2kgPSBUMGkgLSBUMmk7CgkJICAgICAgICAgIG91dFtDXSA9IEZDcjsKCQkgICAgICAgICAgb3V0W0MgKyAxXSA9IEZDaTsKCQkgICAgICAgICAgY29udGludWU7CgkJICAgICAgICB9CgoJCSAgICAgICAgLy8gRG8gbm90IG92ZXJ3cml0ZSBvdXJzZWx2ZXMKCQkgICAgICAgIGlmIChpID09PSBocXVhcnRlckxlbikKCQkgICAgICAgICAgY29udGludWU7CgoJCSAgICAgICAgLy8gSW4gdGhlIGZsaXBwZWQgY2FzZToKCQkgICAgICAgIC8vIE1BaSA9IC1NQWkKCQkgICAgICAgIC8vIE1Ccj0tTUJpLCBNQmk9LU1CcgoJCSAgICAgICAgLy8gTUNyPS1NQ3IKCQkgICAgICAgIC8vIE1Ecj1NRGksIE1EaT1NRHIKCQkgICAgICAgIHZhciBTVDByID0gVDFyOwoJCSAgICAgICAgdmFyIFNUMGkgPSAtVDFpOwoJCSAgICAgICAgdmFyIFNUMXIgPSBUMHI7CgkJICAgICAgICB2YXIgU1QxaSA9IC1UMGk7CgkJICAgICAgICB2YXIgU1QyciA9IC1pbnYgKiBUM2k7CgkJICAgICAgICB2YXIgU1QyaSA9IC1pbnYgKiBUM3I7CgkJICAgICAgICB2YXIgU1QzciA9IC1pbnYgKiBUMmk7CgkJICAgICAgICB2YXIgU1QzaSA9IC1pbnYgKiBUMnI7CgoJCSAgICAgICAgdmFyIFNGQXIgPSBTVDByICsgU1QycjsKCQkgICAgICAgIHZhciBTRkFpID0gU1QwaSArIFNUMmk7CgoJCSAgICAgICAgdmFyIFNGQnIgPSBTVDFyICsgU1QzaTsKCQkgICAgICAgIHZhciBTRkJpID0gU1QxaSAtIFNUM3I7CgoJCSAgICAgICAgdmFyIFNBID0gb3V0T2ZmICsgcXVhcnRlckxlbiAtIGk7CgkJICAgICAgICB2YXIgU0IgPSBvdXRPZmYgKyBoYWxmTGVuIC0gaTsKCgkJICAgICAgICBvdXRbU0FdID0gU0ZBcjsKCQkgICAgICAgIG91dFtTQSArIDFdID0gU0ZBaTsKCQkgICAgICAgIG91dFtTQl0gPSBTRkJyOwoJCSAgICAgICAgb3V0W1NCICsgMV0gPSBTRkJpOwoJCSAgICAgIH0KCQkgICAgfQoJCSAgfQoJCX07CgoJCS8vIHJhZGl4LTIgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49NAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVJlYWxUcmFuc2Zvcm0yID0gZnVuY3Rpb24gX3NpbmdsZVJlYWxUcmFuc2Zvcm0yKG91dE9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgoJCSAgY29uc3QgZXZlblIgPSBkYXRhW29mZl07CgkJICBjb25zdCBvZGRSID0gZGF0YVtvZmYgKyBzdGVwXTsKCgkJICBjb25zdCBsZWZ0UiA9IGV2ZW5SICsgb2RkUjsKCQkgIGNvbnN0IHJpZ2h0UiA9IGV2ZW5SIC0gb2RkUjsKCgkJICBvdXRbb3V0T2ZmXSA9IGxlZnRSOwoJCSAgb3V0W291dE9mZiArIDFdID0gMDsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IHJpZ2h0UjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IDA7CgkJfTsKCgkJLy8gcmFkaXgtNAoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj04CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlUmVhbFRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfc2luZ2xlUmVhbFRyYW5zZm9ybTQob3V0T2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCQkgIGNvbnN0IGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIGNvbnN0IHN0ZXAyID0gc3RlcCAqIDI7CgkJICBjb25zdCBzdGVwMyA9IHN0ZXAgKiAzOwoKCQkgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgY29uc3QgQXIgPSBkYXRhW29mZl07CgkJICBjb25zdCBCciA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBDciA9IGRhdGFbb2ZmICsgc3RlcDJdOwoJCSAgY29uc3QgRHIgPSBkYXRhW29mZiArIHN0ZXAzXTsKCgkJICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICBjb25zdCBUMHIgPSBBciArIENyOwoJCSAgY29uc3QgVDFyID0gQXIgLSBDcjsKCQkgIGNvbnN0IFQyciA9IEJyICsgRHI7CgkJICBjb25zdCBUM3IgPSBpbnYgKiAoQnIgLSBEcik7CgoJCSAgLy8gRmluYWwgdmFsdWVzCgkJICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgoJCSAgY29uc3QgRkJyID0gVDFyOwoJCSAgY29uc3QgRkJpID0gLVQzcjsKCgkJICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgoJCSAgY29uc3QgRkRyID0gVDFyOwoJCSAgY29uc3QgRkRpID0gVDNyOwoKCQkgIG91dFtvdXRPZmZdID0gRkFyOwoJCSAgb3V0W291dE9mZiArIDFdID0gMDsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IEZCcjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IEZCaTsKCQkgIG91dFtvdXRPZmYgKyA0XSA9IEZDcjsKCQkgIG91dFtvdXRPZmYgKyA1XSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgNl0gPSBGRHI7CgkJICBvdXRbb3V0T2ZmICsgN10gPSBGRGk7CgkJfTsKCQlyZXR1cm4gZmZ0OwoJfQoKCXZhciBmZnRFeHBvcnRzID0gcmVxdWlyZUZmdCgpOwoJdmFyIEZGVCA9IC8qQF9fUFVSRV9fKi9nZXREZWZhdWx0RXhwb3J0RnJvbUNqcyhmZnRFeHBvcnRzKTsKCgljb25zdCBXRUJBVURJT19CTE9DS19TSVpFID0gMTI4OwoJY29uc3QgREVGQVVMVF9CTE9DS19TSVpFID0gMTAyNDsgLy8gRGVmYXVsdCBibG9jayBzaXplIGlmIG5vdCBwcm92aWRlZCBpbiBvcHRpb25zCgkvKiogT3ZlcmxhcC1BZGQgTm9kZSAqLwoJY2xhc3MgT0xBUHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKCSAgICBuYklucHV0czsKCSAgICBuYk91dHB1dHM7CgkgICAgYmxvY2tTaXplOwoJICAgIGhvcFNpemU7CgkgICAgbmJPdmVybGFwczsKCSAgICBpbnB1dEJ1ZmZlcnMgPSBbXTsKCSAgICBpbnB1dEJ1ZmZlcnNIZWFkID0gW107CgkgICAgaW5wdXRCdWZmZXJzVG9TZW5kID0gW107CgkgICAgb3V0cHV0QnVmZmVycyA9IFtdOwoJICAgIG91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlID0gW107CgkgICAgY29uc3RydWN0b3Iob3B0aW9ucykgewoJICAgICAgICBzdXBlcihvcHRpb25zKTsKCSAgICAgICAgdGhpcy5uYklucHV0cyA9IG9wdGlvbnM/Lm51bWJlck9mSW5wdXRzIHx8IDE7CgkgICAgICAgIHRoaXMubmJPdXRwdXRzID0gb3B0aW9ucz8ubnVtYmVyT2ZPdXRwdXRzIHx8IDE7CgkgICAgICAgIC8vIHByb2Nlc3Nvck9wdGlvbnMgaXMgdHlwZWQgYHVua25vd25gIGF0IHRoZSBhbWJpZW50IGJvdW5kYXJ5OyBuYXJyb3cgaGVyZS4KCSAgICAgICAgY29uc3QgcHJvY09wdHMgPSAob3B0aW9ucz8ucHJvY2Vzc29yT3B0aW9ucyA/PyB7fSk7CgkgICAgICAgIHRoaXMuYmxvY2tTaXplID0gcHJvY09wdHMuYmxvY2tTaXplIHx8IERFRkFVTFRfQkxPQ0tfU0laRTsKCSAgICAgICAgdGhpcy5ob3BTaXplID0gV0VCQVVESU9fQkxPQ0tfU0laRTsKCSAgICAgICAgdGhpcy5uYk92ZXJsYXBzID0gTWF0aC5mbG9vcih0aGlzLmJsb2NrU2l6ZSAvIHRoaXMuaG9wU2l6ZSk7CgkgICAgICAgIHRoaXMuaW5pdGlhbGl6ZUJ1ZmZlcnMoKTsKCSAgICB9CgkgICAgaW5pdGlhbGl6ZUJ1ZmZlcnMoKSB7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWQgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnMgPSBuZXcgQXJyYXkodGhpcy5uYk91dHB1dHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlID0gbmV3IEFycmF5KHRoaXMubmJPdXRwdXRzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVJbnB1dENoYW5uZWxzKGksIDEpOwoJICAgICAgICB9CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZU91dHB1dENoYW5uZWxzKGksIDEpOwoJICAgICAgICB9CgkgICAgfQoJICAgIGFsbG9jYXRlSW5wdXRDaGFubmVscyhpbnB1dEluZGV4LCBuYkNoYW5uZWxzKSB7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWRbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iQ2hhbm5lbHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplICsgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXS5maWxsKDApOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2lucHV0SW5kZXhdW2ldID0gdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0uc3ViYXJyYXkoMCwgdGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaW5wdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICBhbGxvY2F0ZU91dHB1dENoYW5uZWxzKG91dHB1dEluZGV4LCBuYkNoYW5uZWxzKSB7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbb3V0cHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iQ2hhbm5lbHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XVtpXS5maWxsKDApOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtvdXRwdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICByZWFsbG9jYXRlQ2hhbm5lbHNJZk5lZWRlZChpbnB1dHMsIG91dHB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IG5iQ2hhbm5lbHMgPSBpbnB1dHNbaV0ubGVuZ3RoOwoJICAgICAgICAgICAgaWYgKG5iQ2hhbm5lbHMgIT09IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aCkgewoJICAgICAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVJbnB1dENoYW5uZWxzKGksIG5iQ2hhbm5lbHMpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgY29uc3QgbmJDaGFubmVscyA9IG91dHB1dHNbaV0ubGVuZ3RoOwoJICAgICAgICAgICAgaWYgKG5iQ2hhbm5lbHMgIT09IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGgpIHsKCSAgICAgICAgICAgICAgICB0aGlzLmFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMoaSwgbmJDaGFubmVscyk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcmVhZElucHV0cyhpbnB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCB3ZWJBdWRpb0Jsb2NrID0gaW5wdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2ldW2pdLnNldCh3ZWJBdWRpb0Jsb2NrLCB0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgd3JpdGVPdXRwdXRzKG91dHB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGNvbnN0IHdlYkF1ZGlvQmxvY2sgPSBvdXRwdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIHdlYkF1ZGlvQmxvY2suc2V0KHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5zdWJhcnJheSgwLCBXRUJBVURJT19CTE9DS19TSVpFKSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgc2hpZnRJbnB1dEJ1ZmZlcnMoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaV1bal0uY29weVdpdGhpbigwLCBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBzaGlmdE91dHB1dEJ1ZmZlcnMoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uY29weVdpdGhpbigwLCBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uZmlsbCgwLCB0aGlzLmJsb2NrU2l6ZSAtIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHByZXBhcmVJbnB1dEJ1ZmZlcnNUb1NlbmQoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaV1bal0uc2V0KHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpXVtqXSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgaGFuZGxlT3V0cHV0QnVmZmVyc1RvUmV0cmlldmUoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHRoaXMuYmxvY2tTaXplOyBrKyspIHsKCSAgICAgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdW2tdICs9IHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbaV1bal1ba10gLyB0aGlzLm5iT3ZlcmxhcHM7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CgkgICAgICAgIHRoaXMucmVhbGxvY2F0ZUNoYW5uZWxzSWZOZWVkZWQoaW5wdXRzLCBvdXRwdXRzKTsKCSAgICAgICAgdGhpcy5yZWFkSW5wdXRzKGlucHV0cyk7CgkgICAgICAgIHRoaXMuc2hpZnRJbnB1dEJ1ZmZlcnMoKTsKCSAgICAgICAgdGhpcy5wcmVwYXJlSW5wdXRCdWZmZXJzVG9TZW5kKCk7CgkgICAgICAgIHRoaXMucHJvY2Vzc09MQSh0aGlzLmlucHV0QnVmZmVyc1RvU2VuZCwgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSwgcGFyYW1ldGVycyk7CgkgICAgICAgIHRoaXMuaGFuZGxlT3V0cHV0QnVmZmVyc1RvUmV0cmlldmUoKTsKCSAgICAgICAgdGhpcy53cml0ZU91dHB1dHMob3V0cHV0cyk7CgkgICAgICAgIHRoaXMuc2hpZnRPdXRwdXRCdWZmZXJzKCk7CgkgICAgICAgIHJldHVybiB0cnVlOwoJICAgIH0KCX0KCgkvKgoJICogUGhhc2Utdm9jb2RlciBjb3JlIOKAlCBjb250ZXh0LWZyZWUsIHVuaXQtdGVzdGFibGUgRFNQIGZvciBwZWFrLWJhc2VkCgkgKiBwaXRjaC1zaGlmdGluZyB3aXRoIElkZW50aXR5IFBoYXNlLUxvY2tpbmcuCgkgKgoJICogQWxnb3JpdGhtOiBKZWFuIExhcm9jaGUgJiBNYXJrIERvbHNvbiwgIk5ldyBQaGFzZS1Wb2NvZGVyIFRlY2huaXF1ZXMgZm9yCgkgKiBQaXRjaC1TaGlmdGluZywgSGFybW9uaXppbmcgYW5kIE90aGVyIEV4b3RpYyBFZmZlY3RzIiwgUHJvYy4gMTk5OSBJRUVFCgkgKiBXQVNQQUEsIE5ldyBQYWx0eiwgTlkuIFRoZSBwaXRjaC1zaGlmdCBkZXRlY3RzIHNwZWN0cmFsIHBlYWtzLCBkaXZpZGVzIHRoZQoJICogZnJlcXVlbmN5IGF4aXMgaW50byBwZXItcGVhayAicmVnaW9ucyBvZiBpbmZsdWVuY2UiLCBhbmQgcmlnaWRseSB0cmFuc2xhdGVzCgkgKiBlYWNoIHJlZ2lvbiB0byB0aGUgcGVhaydzIHNoaWZ0ZWQgZnJlcXVlbmN5IChTZWN0aW9uIDMuMS0zLjQpLgoJICoKCSAqIFRoZSBwaGFzZSBoYW5kbGluZyBpcyB0aGUgIklkZW50aXR5IFBoYXNlLUxvY2tpbmciIHJ1bGUgb2YgTGFyb2NoZS1Eb2xzb24KCSAqIDE5OTkgU2VjdGlvbiAzLjU6IHRvIG1haW50YWluIGZyYW1lLXRvLWZyYW1lIChob3Jpem9udGFsKSBwaGFzZSBjb2hlcmVuY2UKCSAqIGFmdGVyIHNoaWZ0aW5nIGEgcGVhayBieSBEZWx0YS1vbWVnYSwgRVZFUlkgZnJlcXVlbmN5IGJpbiBpbiB0aGF0IHBlYWsncwoJICogcmVnaW9uIG9mIGluZmx1ZW5jZSBpcyBtdWx0aXBsaWVkIGJ5IGEgU0lOR0xFIGNvbXBsZXggbnVtYmVyCgkgKgoJICogICAgIFpfdSA9IGV4cChqICogRGVsdGEtb21lZ2EgKiBSKSAgICAgICAgICAgIChMYXJvY2hlLURvbHNvbiAxOTk5LCBlcS4gcC4zKQoJICoKCSAqIChSID0gaG9wIHNpemUpLCBhbmQgdGhlIHJvdGF0aW9uIGlzIGN1bXVsYXRlZCBmcmFtZSB0byBmcmFtZQoJICoKCSAqICAgICBaX3t1KzF9ID0gWl91ICogZXhwKGogKiBEZWx0YS1vbWVnYV97dSsxfSAqIFIpLgoJICoKCSAqIEJlY2F1c2UgYWxsIGJpbnMgaW4gYSByZWdpb24gYXJlIHJvdGF0ZWQgYnkgdGhlIFNBTUUgYW5nbGUsIHRoZSBwaGFzZQoJICogcmVsYXRpb25zaGlwcyBCRVRXRUVOIGJpbnMgYXJvdW5kIGEgcGVhayAodGhlIHZlcnRpY2FsIC8gaW50cmEtcGVhawoJICogY29oZXJlbmNlIHRoYXQgaWRlbnRpZmllcyB0aGUgc2ludXNvaWQpIGFyZSBQUkVTRVJWRUQgYWNyb3NzIHRoZSBtb3ZlIOKAlCB0aGlzCgkgKiBpcyBleGFjdGx5IHdoYXQgcmVtb3ZlcyB0aGUgInBoYXNpbmVzcyIgYXJ0aWZhY3Qgb2YgdGhlIG5haXZlIGJpbi1pbmRlcGVuZGVudAoJICogcGhhc2Ugdm9jb2Rlci4gTm8ga25vd2xlZGdlIG9mIHRoZSB0cnVlIHBlYWsgZnJlcXVlbmN5IG9tZWdhIGlzIG5lZWRlZCwgc28gbm8KCSAqIGFyY3RhbmdlbnQgYW5kIG5vIHBoYXNlLXVud3JhcHBpbmcgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjUpLgoJICoKCSAqIFRoaXMgbW9kdWxlIG1pcnJvcnMgdGhlIHByb2plY3QncyBjb3JlL3NoZWxsIHNwbGl0IChjZi4gd2F2ZXNoYXBlci1jb3JlLnRzLAoJICogZHluYW1pY3MtY29yZS50cyk6IHRoZSBGRlQgZnJhbWluZyBhbmQgd29ya2xldCBwbHVtYmluZyBsaXZlIGluIHRoZQoJICogQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHNoZWxsIChwaGFzZS12b2NvZGVyLnRzKTsgdGhlIHNwZWN0cnVtIG1hbmlwdWxhdGlvbgoJICogdGhhdCBjYXJyaWVzIHRoZSB0ZXN0YWJsZSBpbnZhcmlhbnRzIGxpdmVzIGhlcmUuCgkgKi8KCS8qKgoJICogU3F1YXJlZC1tYWduaXR1ZGUgc3BlY3RydW0gZnJvbSBhbiBpbnRlcmxlYXZlZCBbcmUsIGltLCByZSwgaW0sIC4uLl0gY29tcGxleAoJICogYnVmZmVyLiBgbWFnbml0dWRlc1tpXWAgY29ycmVzcG9uZHMgdG8gYmluIGBpYC4gV3JpdGVzIGludG8gYG91dGAgKGxlbmd0aAoJICogZmZ0U2l6ZS8yICsgMSkgdG8gYXZvaWQgcGVyLWNhbGwgYWxsb2NhdGlvbiBpbiB0aGUgd29ya2xldC4KCSAqLwoJZnVuY3Rpb24gY29tcHV0ZU1hZ25pdHVkZXMoY29tcGxleCwgb3V0KSB7CgkgICAgZm9yIChsZXQgaSA9IDAsIGogPSAwOyBpIDwgb3V0Lmxlbmd0aDsgaSsrLCBqICs9IDIpIHsKCSAgICAgICAgY29uc3QgcmVhbCA9IGNvbXBsZXhbal07CgkgICAgICAgIGNvbnN0IGltYWcgPSBjb21wbGV4W2ogKyAxXTsKCSAgICAgICAgb3V0W2ldID0gcmVhbCAqKiAyICsgaW1hZyAqKiAyOwoJICAgIH0KCX0KCS8qKgoJICogUGVhayBkZXRlY3Rpb24gcGVyIExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjI6IGEgYmluIGlzIGEgcGVhayBpZmYgaXRzCgkgKiBtYWduaXR1ZGUgc3RyaWN0bHkgZXhjZWVkcyBpdHMgdHdvIG5lYXJlc3QgbmVpZ2hib3VycyBvbiBlYWNoIHNpZGUuIFdyaXRlcwoJICogdGhlIHBlYWsgYmluIGluZGljZXMgaW50byBgcGVha0luZGV4ZXNgIGFuZCByZXR1cm5zIHRoZSBjb3VudC4KCSAqLwoJZnVuY3Rpb24gZmluZFBlYWtzKG1hZ25pdHVkZXMsIHBlYWtJbmRleGVzKSB7CgkgICAgbGV0IG5iUGVha3MgPSAwOwoJICAgIGZvciAobGV0IGkgPSAyLCBlbmQgPSBtYWduaXR1ZGVzLmxlbmd0aCAtIDI7IGkgPCBlbmQ7IGkrKykgewoJICAgICAgICBjb25zdCBtYWcgPSBtYWduaXR1ZGVzW2ldOwoJICAgICAgICBpZiAobWFnbml0dWRlc1tpIC0gMV0gPj0gbWFnIHx8IG1hZ25pdHVkZXNbaSAtIDJdID49IG1hZyB8fCBtYWduaXR1ZGVzW2kgKyAxXSA+PSBtYWcgfHwgbWFnbml0dWRlc1tpICsgMl0gPj0gbWFnKSB7CgkgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgfQoJICAgICAgICBwZWFrSW5kZXhlc1tuYlBlYWtzKytdID0gaTsKCSAgICB9CgkgICAgcmV0dXJuIG5iUGVha3M7Cgl9CgkvKioKCSAqIFRoZSBwZXItZnJhbWUgcGhhc2UgaW5jcmVtZW50IGV4cChqICogRGVsdGEtb21lZ2EgKiBSKSBmb3IgT05FIHBlYWsgc2hpZnRlZAoJICogYnkgRGVsdGEtb21lZ2Egb3ZlciBhIGhvcCBvZiBSIHN5bnRoZXNpcyBzYW1wbGVzLiBMYXJvY2hlLURvbHNvbiAxOTk5IGVxLgoJICogcC4zIChTZWN0aW9uIDMuNSksIHRoZSBzaW5nbGUgY29tcGxleCBudW1iZXIgYXBwbGllZCB1bmlmb3JtbHkgdG8gdGhlIHBlYWsncwoJICogd2hvbGUgcmVnaW9uIG9mIGluZmx1ZW5jZS4KCSAqCgkgKiBEZWx0YS1vbWVnYSAocmFkL3NhbXBsZSkgaXMgdGhlIGZyZXF1ZW5jeSBzaGlmdCB0aGUgcGVhayB1bmRlcmdvZXM6CgkgKiAgIERlbHRhLW9tZWdhID0gMipwaSAqIChwZWFrSW5kZXhTaGlmdGVkIC0gcGVha0luZGV4KSAvIGZmdFNpemUuCgkgKgoJICogVGhpcyBpcyB0aGUgcGVyLWZyYW1lIGZhY3RvciwgTk9UIHRoZSBjdW11bGF0aXZlIHJvdGF0b3IuIENyb3NzLWZyYW1lCgkgKiBjdW11bGF0aW9uIFpfe3UrMX0gPSBaX3UgKiBleHAoaipEZWx0YS1vbWVnYV97dSsxfSpSKSBpcyB0aGUgam9iIG9mCgkgKiB7QGxpbmsgUGVha1JvdGF0b3JTdGF0ZX07IHRoaXMgZnVuY3Rpb24gcHJvZHVjZXMgdGhlIGBleHAoLi4uKWAgZmFjdG9yIHRoYXQKCSAqIHN0YXRlIG11bHRpcGxpZXMgaW4gZWFjaCBmcmFtZS4KCSAqLwoJZnVuY3Rpb24gZnJhbWVSb3RhdGlvbihwZWFrSW5kZXgsIHBlYWtJbmRleFNoaWZ0ZWQsIGZmdFNpemUsIGhvcCkgewoJICAgIGNvbnN0IG9tZWdhRGVsdGEgPSAoMiAqIE1hdGguUEkgKiAocGVha0luZGV4U2hpZnRlZCAtIHBlYWtJbmRleCkpIC8gZmZ0U2l6ZTsKCSAgICBjb25zdCBhbmdsZSA9IG9tZWdhRGVsdGEgKiBob3A7CgkgICAgcmV0dXJuIHsgcmU6IE1hdGguY29zKGFuZ2xlKSwgaW06IE1hdGguc2luKGFuZ2xlKSB9OwoJfQoJLyoqCgkgKiBDcm9zcy1mcmFtZSBjdW11bGF0aXZlIHBoYXNlLWxvY2sgc3RhdGUgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjUpLgoJICoKCSAqIFRoZSBwYXBlciByZXF1aXJlcyB0aGUgcGVyLXBlYWsgcm90YXRpb24gYmUgQUNDVU1VTEFURUQgZnJhbWUgdG8gZnJhbWU6CgkgKgoJICogICAgIFpfe3UrMX0gPSBaX3UgKiBleHAoaiAqIERlbHRhLW9tZWdhX3t1KzF9ICogUikKCSAqCgkgKiB3aXRoIERlbHRhLW9tZWdhIGFsbG93ZWQgdG8gdmFyeSBwZXIgZnJhbWUgKGF1dG9tYXRlZCAvIHRpbWUtdmFyeWluZyBwaXRjaCkuCgkgKiBBIG5haXZlIGBvbWVnYURlbHRhICogZWxhcHNlZFRpbWVgIHJvdGF0b3IgaXMgd3Jvbmc6IHdoZW4gdGhlIHNoaWZ0IGNoYW5nZXMsCgkgKiBpdCByZXRyb2FjdGl2ZWx5IHJlLXBoYXNlcyBldmVyeSBwcmlvciBmcmFtZSBhbmQgcHJvZHVjZXMgYSBkaXNjb250aW51aXR5LgoJICoKCSAqIFRoaXMgc3RhdGUga2VlcHMgb25lIGN1bXVsYXRpdmUgcm90YXRvciBaX3UgUEVSIFBFQUssIGtleWVkIGJ5IHRoZSBwZWFrJ3MKCSAqIHNvdXJjZSBiaW4gaW5kZXguIHtAbGluayBQZWFrUm90YXRvclN0YXRlLmFkdmFuY2V9IG11bHRpcGxpZXMgZWFjaCBwZWFrJ3MgWgoJICogYnkgdGhpcyBmcmFtZSdzIGV4cChqKkRlbHRhLW9tZWdhKlIpIChzbyBoaXN0b3J5IGlzIHByZXNlcnZlZCBhY3Jvc3MgcGl0Y2gKCSAqIGNoYW5nZXMpLCBhbmQge0BsaW5rIFBlYWtSb3RhdG9yU3RhdGUuZ2V0fSByZXR1cm5zIHRoZSBjdXJyZW50IGN1bXVsYXRpdmUKCSAqIHJvdGF0b3IgdG8gYXBwbHkgdG8gdGhhdCBwZWFrJ3MgcmVnaW9uIG9mIGluZmx1ZW5jZS4KCSAqLwoJY2xhc3MgUGVha1JvdGF0b3JTdGF0ZSB7CgkgICAgLyoqIHBlYWsgc291cmNlLWJpbiBpbmRleCAtPiBjdW11bGF0aXZlIHJvdGF0b3IgWl91ICh1bml0IG1vZHVsdXMpLiAqLwoJICAgIHJvdGF0b3JzID0gbmV3IE1hcCgpOwoJICAgIC8qKiBzY3JhdGNoIHNldCBvZiBiaW5zIHNlZW4gdGhpcyBmcmFtZSwgZm9yIHBydW5pbmcgdmFuaXNoZWQgcGVha3MuICovCgkgICAgc2VlbiA9IG5ldyBTZXQoKTsKCSAgICAvKioKCSAgICAgKiBBZHZhbmNlIGV2ZXJ5IGN1cnJlbnRseS1kZXRlY3RlZCBwZWFrJ3MgY3VtdWxhdGl2ZSByb3RhdG9yIGJ5IHRoaXMgZnJhbWUncwoJICAgICAqIGV4cChqKkRlbHRhLW9tZWdhKlIpLiBOZXcgcGVha3Mgc3RhcnQgYXQgWiA9IDEgKG5vIHJvdGF0aW9uKSB0aGVuIHRha2UgdGhpcwoJICAgICAqIGZyYW1lJ3MgaW5jcmVtZW50OyBwZWFrcyBub3QgcHJlc2VudCB0aGlzIGZyYW1lIGFyZSBkcm9wcGVkIHNvIHRoZWlyIHN0YWxlCgkgICAgICogcGhhc2UgZG9lcyBub3QgbGVhayBpbnRvIGEgbGF0ZXIgcmUtZGV0ZWN0aW9uLgoJICAgICAqCgkgICAgICogYHBpdGNoRmFjdG9yYCBhbmQgYGhvcGAgZGVmaW5lIHRoaXMgZnJhbWUncyBwZXItcGVhayBEZWx0YS1vbWVnYSB2aWEgdGhlCgkgICAgICogc2hpZnRlZCBiaW4gYHJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKWAuCgkgICAgICovCgkgICAgYWR2YW5jZShwZWFrSW5kZXhlcywgbmJQZWFrcywgZmZ0U2l6ZSwgcGl0Y2hGYWN0b3IsIGhvcCkgewoJICAgICAgICB0aGlzLnNlZW4uY2xlYXIoKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYlBlYWtzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgICAgICAgICAgdGhpcy5zZWVuLmFkZChwZWFrSW5kZXgpOwoJICAgICAgICAgICAgY29uc3QgcGVha0luZGV4U2hpZnRlZCA9IE1hdGgucm91bmQocGVha0luZGV4ICogcGl0Y2hGYWN0b3IpOwoJICAgICAgICAgICAgY29uc3QgaW5jID0gZnJhbWVSb3RhdGlvbihwZWFrSW5kZXgsIHBlYWtJbmRleFNoaWZ0ZWQsIGZmdFNpemUsIGhvcCk7CgkgICAgICAgICAgICBjb25zdCBwcmV2ID0gdGhpcy5yb3RhdG9ycy5nZXQocGVha0luZGV4KSA/PyB7IHJlOiAxLCBpbTogMCB9OwoJICAgICAgICAgICAgLy8gWl97dSsxfSA9IFpfdSAqIGluYyAgKGNvbXBsZXggbXVsdGlwbHkpCgkgICAgICAgICAgICB0aGlzLnJvdGF0b3JzLnNldChwZWFrSW5kZXgsIHsKCSAgICAgICAgICAgICAgICByZTogcHJldi5yZSAqIGluYy5yZSAtIHByZXYuaW0gKiBpbmMuaW0sCgkgICAgICAgICAgICAgICAgaW06IHByZXYucmUgKiBpbmMuaW0gKyBwcmV2LmltICogaW5jLnJlLAoJICAgICAgICAgICAgfSk7CgkgICAgICAgIH0KCSAgICAgICAgLy8gUHJ1bmUgcGVha3MgdGhhdCBkaXNhcHBlYXJlZCB0aGlzIGZyYW1lLgoJICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiB0aGlzLnJvdGF0b3JzLmtleXMoKSkgewoJICAgICAgICAgICAgaWYgKCF0aGlzLnNlZW4uaGFzKGtleSkpCgkgICAgICAgICAgICAgICAgdGhpcy5yb3RhdG9ycy5kZWxldGUoa2V5KTsKCSAgICAgICAgfQoJICAgIH0KCSAgICAvKiogVGhlIGN1cnJlbnQgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSBmb3IgYSBwZWFrIChpZGVudGl0eSBpZiB1bnNlZW4pLiAqLwoJICAgIGdldChwZWFrSW5kZXgpIHsKCSAgICAgICAgcmV0dXJuIHRoaXMucm90YXRvcnMuZ2V0KHBlYWtJbmRleCkgPz8geyByZTogMSwgaW06IDAgfTsKCSAgICB9CgkgICAgLyoqIFJlc2V0IGFsbCBhY2N1bXVsYXRlZCBwaGFzZSAoZS5nLiBvbiBzdG9wIC8gcmUtc2VlaykuICovCgkgICAgcmVzZXQoKSB7CgkgICAgICAgIHRoaXMucm90YXRvcnMuY2xlYXIoKTsKCSAgICAgICAgdGhpcy5zZWVuLmNsZWFyKCk7CgkgICAgfQoJICAgIC8qKiBOdW1iZXIgb2YgcGVha3MgY3VycmVudGx5IHRyYWNrZWQgKHRlc3QvaW50cm9zcGVjdGlvbiBoZWxwZXIpLiAqLwoJICAgIGdldCBzaXplKCkgewoJICAgICAgICByZXR1cm4gdGhpcy5yb3RhdG9ycy5zaXplOwoJICAgIH0KCX0KCS8qKgoJICogSGFsZi13YXkgcmVnaW9uLW9mLWluZmx1ZW5jZSBib3VuZGFyaWVzIGZvciBwZWFrIGBpYCAoTGFyb2NoZS1Eb2xzb24gMTk5OQoJICogU2VjdGlvbiAzLjIg4oCUIGJvdW5kYXJ5IHNldCBtaWR3YXkgYmV0d2VlbiBhZGphY2VudCBwZWFrcykuIFJldHVybnMgdGhlCgkgKiBbc3RhcnRJbmRleCwgZW5kSW5kZXgpIGJpbiByYW5nZSBvd25lZCBieSB0aGlzIHBlYWsuCgkgKgoJICogVGhlIExBU1QgcGVhaydzIHJlZ2lvbiBtdXN0IGVuZCBhdCB0aGUgbm9uLXJlZHVuZGFudCBoYWxmLXNwZWN0cnVtIGxlbmd0aAoJICogYG1hZ25pdHVkZXNMZW5ndGhgICg9IGZmdFNpemUvMiArIDEpLCBOT1QgYXQgYGZmdFNpemVgLiBUaGUgYW5hbHlzZWQgc3BlY3RydW0KCSAqIG9ubHkgY2FycmllcyBiaW5zIFswLCBmZnRTaXplLzJdOyB0aGUgdXBwZXIgaGFsZiBpcyB0aGUgY29uanVnYXRlLXN5bW1ldHJpYwoJICogbWlycm9yIGZpbGxlZCBsYXRlciBieSBgY29tcGxldGVTcGVjdHJ1bWAuIFJlYWRpbmcgc291cmNlIGJpbnMgcGFzdCBOeXF1aXN0CgkgKiBmb2xkcyBzdGFsZSAvIG5vdC15ZXQtcG9wdWxhdGVkIG5lZ2F0aXZlLWZyZXF1ZW5jeSBkYXRhIGludG8gdGhlIG91dHB1dCwgc28KCSAqIHRoZSByZWdpb24gaXMgY2xhbXBlZCB0byB0aGUgYW5hbHlzZWQgcG9zaXRpdmUgc3BlY3RydW0uCgkgKi8KCWZ1bmN0aW9uIHJlZ2lvbk9mSW5mbHVlbmNlKHBlYWtJbmRleGVzLCBpLCBuYlBlYWtzLCBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgY29uc3QgcGVha0luZGV4ID0gcGVha0luZGV4ZXNbaV07CgkgICAgY29uc3Qgc3RhcnRJbmRleCA9IGkgPiAwID8gcGVha0luZGV4IC0gTWF0aC5mbG9vcigocGVha0luZGV4IC0gcGVha0luZGV4ZXNbaSAtIDFdKSAvIDIpIDogMDsKCSAgICBjb25zdCBlbmRJbmRleCA9IGkgPCBuYlBlYWtzIC0gMSA/IHBlYWtJbmRleCArIE1hdGguY2VpbCgocGVha0luZGV4ZXNbaSArIDFdIC0gcGVha0luZGV4KSAvIDIpIDogbWFnbml0dWRlc0xlbmd0aDsKCSAgICByZXR1cm4geyBzdGFydEluZGV4LCBlbmRJbmRleCB9OwoJfQoJLyoqCgkgKiBQZWFrLXNoaWZ0IHdpdGggSWRlbnRpdHkgUGhhc2UtTG9ja2luZyAoTGFyb2NoZS1Eb2xzb24gMTk5OSwgU2VjdGlvbnMgMy40ICYKCSAqIDMuNSkuIEZvciBlYWNoIGRldGVjdGVkIHBlYWs6CgkgKiAgLSBjb21wdXRlIHRoZSBzaGlmdGVkIHBlYWsgYmluIChwZWFrSW5kZXggKiBwaXRjaEZhY3Rvciwgcm91bmRlZCDigJQgdGhlCgkgKiAgICBpbnRlZ2VyLWJpbiBjYXNlIG9mIFNlY3Rpb24gMy40LCBhIGxvc3NsZXNzIHJlZ2lvbiBjb3B5KSwKCSAqICAtIGNvbXB1dGUgT05FIHJvdGF0b3IgWl91ID0gZXhwKGoqRGVsdGEtb21lZ2EqdGltZUN1cnNvcikgZm9yIHRoZSBwZWFrCgkgKiAgICAoYHBlYWtSb3RhdG9yYCksCgkgKiAgLSByaWdpZGx5IHRyYW5zbGF0ZSB0aGUgcGVhaydzIHJlZ2lvbiBvZiBpbmZsdWVuY2UgdG8gdGhlIHNoaWZ0ZWQgbG9jYXRpb24sCgkgKiAgICBtdWx0aXBseWluZyBFVkVSWSBiaW4gaW4gdGhlIHJlZ2lvbiBieSB0aGF0IFNBTUUgWl91LgoJICogT3ZlcmxhcHBpbmcgc2hpZnRlZCByZWdpb25zIGFyZSBzdW1tZWQgKFNlY3Rpb24gMy40KS4KCSAqCgkgKiBgY29tcGxleGAgaXMgdGhlIGFuYWx5c2lzIHNwZWN0cnVtIChpbnRlcmxlYXZlZCByZS9pbSkuIGBzaGlmdGVkYCBpcyB0aGUKCSAqIG91dHB1dCBzcGVjdHJ1bSAoaW50ZXJsZWF2ZWQgcmUvaW0pIGFuZCBpcyB6ZXJvLWZpbGxlZCBoZXJlIGJlZm9yZQoJICogYWNjdW11bGF0aW9uLiBgbWFnbml0dWRlc0xlbmd0aGAgPSBmZnRTaXplLzIgKyAxICh0aGUgbm9uLXJlZHVuZGFudCBiaW4KCSAqIGNvdW50KTsgYmlucyBiZXlvbmQgaXQgYXJlIHNraXBwZWQgLyB0ZXJtaW5hdGUgdGhlIHBlYWsgbG9vcCwgbWF0Y2hpbmcgdGhlCgkgKiB3b3JrbGV0J3MgcHJlLWBjb21wbGV0ZVNwZWN0cnVtYCBoYWxmLXNwZWN0cnVtLgoJICovCglmdW5jdGlvbiBzaGlmdFBlYWtzKGNvbXBsZXgsIHNoaWZ0ZWQsIHBlYWtJbmRleGVzLCBuYlBlYWtzLCBmZnRTaXplLCBtYWduaXR1ZGVzTGVuZ3RoLCBwaXRjaEZhY3Rvciwgcm90YXRvcnMpIHsKCSAgICBzaGlmdGVkLmZpbGwoMCk7CgkgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYlBlYWtzOyBpKyspIHsKCSAgICAgICAgY29uc3QgcGVha0luZGV4ID0gcGVha0luZGV4ZXNbaV07CgkgICAgICAgIGNvbnN0IHBlYWtJbmRleFNoaWZ0ZWQgPSBNYXRoLnJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKTsKCSAgICAgICAgY29uc3QgeyBzdGFydEluZGV4LCBlbmRJbmRleCB9ID0gcmVnaW9uT2ZJbmZsdWVuY2UocGVha0luZGV4ZXMsIGksIG5iUGVha3MsIG1hZ25pdHVkZXNMZW5ndGgpOwoJICAgICAgICAvLyBMYXJvY2hlLURvbHNvbiAxOTk5IElkZW50aXR5IFBoYXNlLUxvY2tpbmc6IE9ORSBjdW11bGF0aXZlIHJvdGF0b3IgWl91CgkgICAgICAgIC8vIHBlciBwZWFrLCBhcHBsaWVkIHVuaWZvcm1seSB0byB0aGUgd2hvbGUgcmVnaW9uIG9mIGluZmx1ZW5jZSAob25lIGNvbXBsZXgKCSAgICAgICAgLy8gbXVsdGlwbHkgcGVyIGJpbiksIHByZXNlcnZpbmcgdGhlIGludHJhLXJlZ2lvbiBwaGFzZSByZWxhdGlvbnNoaXBzLiBaX3UKCSAgICAgICAgLy8gaXMgYWNjdW11bGF0ZWQgZnJhbWUtdG8tZnJhbWUgYnkgUGVha1JvdGF0b3JTdGF0ZSDigJQgbm90IHJlY29tcHV0ZWQgZnJvbQoJICAgICAgICAvLyBhYnNvbHV0ZSB0aW1lIOKAlCBzbyBhIGNoYW5naW5nIHBpdGNoRmFjdG9yIGRvZXMgbm90IHJldHJvYWN0aXZlbHkgcmVwaGFzZS4KCSAgICAgICAgY29uc3Qgcm90ID0gcm90YXRvcnMuZ2V0KHBlYWtJbmRleCk7CgkgICAgICAgIGZvciAobGV0IGogPSBzdGFydEluZGV4IC0gcGVha0luZGV4OyBqIDwgZW5kSW5kZXggLSBwZWFrSW5kZXg7IGorKykgewoJICAgICAgICAgICAgY29uc3QgYmluSW5kZXggPSBwZWFrSW5kZXggKyBqOwoJICAgICAgICAgICAgY29uc3QgYmluSW5kZXhTaGlmdGVkID0gcGVha0luZGV4U2hpZnRlZCArIGo7CgkgICAgICAgICAgICBpZiAoYmluSW5kZXhTaGlmdGVkID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICBicmVhazsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIC8vIFNvdXJjZSBiaW5zIGFyZSBvbmx5IHZhbGlkIGluc2lkZSB0aGUgYW5hbHlzZWQgcG9zaXRpdmUgc3BlY3RydW0uCgkgICAgICAgICAgICBpZiAoYmluSW5kZXggPCAwIHx8IGJpbkluZGV4ID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIGNvbnN0IGluZGV4UmVhbCA9IGJpbkluZGV4ICogMjsKCSAgICAgICAgICAgIGNvbnN0IGluZGV4SW1hZyA9IGluZGV4UmVhbCArIDE7CgkgICAgICAgICAgICBjb25zdCB2YWx1ZVJlYWwgPSBjb21wbGV4W2luZGV4UmVhbF07CgkgICAgICAgICAgICBjb25zdCB2YWx1ZUltYWcgPSBjb21wbGV4W2luZGV4SW1hZ107CgkgICAgICAgICAgICAvLyBDb21wbGV4IG11bHRpcGx5IGJ5IHRoZSBzaW5nbGUgcGVyLXBlYWsgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdS4KCSAgICAgICAgICAgIGNvbnN0IHZhbHVlU2hpZnRlZFJlYWwgPSB2YWx1ZVJlYWwgKiByb3QucmUgLSB2YWx1ZUltYWcgKiByb3QuaW07CgkgICAgICAgICAgICBjb25zdCB2YWx1ZVNoaWZ0ZWRJbWFnID0gdmFsdWVSZWFsICogcm90LmltICsgdmFsdWVJbWFnICogcm90LnJlOwoJICAgICAgICAgICAgaWYgKGJpbkluZGV4U2hpZnRlZCA8IDApIHsKCSAgICAgICAgICAgICAgICAvLyBMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy40OiBhIHJlZ2lvbiBvZiBpbmZsdWVuY2Ugc3BpbGxpbmcgb250bwoJICAgICAgICAgICAgICAgIC8vIHRoZSBORUdBVElWRS1mcmVxdWVuY3kgYXhpcyBpcyByZWZsZWN0ZWQgYmFjayBpbnRvIHRoZSBwb3NpdGl2ZQoJICAgICAgICAgICAgICAgIC8vIGZyZXF1ZW5jaWVzIHdpdGggQ09NUExFWCBDT05KVUdBVElPTiwgYmVjYXVzZSB0aGUgb3JpZ2luYWwgc2lnbmFsIGlzCgkgICAgICAgICAgICAgICAgLy8gcmVhbCAoSGVybWl0aWFuIHN5bW1ldHJ5IFgoLXcpID0gY29uaihYKHcpKSkuIFdpdGhvdXQgdGhpcyB0aGUgZW5lcmd5CgkgICAgICAgICAgICAgICAgLy8gYmVsb3cgREMgaXMgZHJvcHBlZCBhbmQgZG93bndhcmQgc2hpZnRzIGxvc2UgdGhlaXIgbG93IGVuZC4KCSAgICAgICAgICAgICAgICBjb25zdCByZWZsZWN0ZWQgPSAtYmluSW5kZXhTaGlmdGVkOwoJICAgICAgICAgICAgICAgIGlmIChyZWZsZWN0ZWQgPj0gbWFnbml0dWRlc0xlbmd0aCkgewoJICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICAgICAgY29uc3QgcmVmbFJlYWwgPSByZWZsZWN0ZWQgKiAyOwoJICAgICAgICAgICAgICAgIGNvbnN0IHJlZmxJbWFnID0gcmVmbFJlYWwgKyAxOwoJICAgICAgICAgICAgICAgIHNoaWZ0ZWRbcmVmbFJlYWxdICs9IHZhbHVlU2hpZnRlZFJlYWw7CgkgICAgICAgICAgICAgICAgc2hpZnRlZFtyZWZsSW1hZ10gKz0gLXZhbHVlU2hpZnRlZEltYWc7IC8vIGNvbmp1Z2F0ZQoJICAgICAgICAgICAgICAgIGNvbnRpbnVlOwoJICAgICAgICAgICAgfQoJICAgICAgICAgICAgY29uc3QgaW5kZXhTaGlmdGVkUmVhbCA9IGJpbkluZGV4U2hpZnRlZCAqIDI7CgkgICAgICAgICAgICBjb25zdCBpbmRleFNoaWZ0ZWRJbWFnID0gaW5kZXhTaGlmdGVkUmVhbCArIDE7CgkgICAgICAgICAgICBzaGlmdGVkW2luZGV4U2hpZnRlZFJlYWxdICs9IHZhbHVlU2hpZnRlZFJlYWw7CgkgICAgICAgICAgICBzaGlmdGVkW2luZGV4U2hpZnRlZEltYWddICs9IHZhbHVlU2hpZnRlZEltYWc7CgkgICAgICAgIH0KCSAgICB9Cgl9CgoJLyoKCSAqIFBoYXNlLXZvY29kZXIgQXVkaW9Xb3JrbGV0IHNoZWxsIOKAlCBwZWFrLWJhc2VkIHBpdGNoLXNoaWZ0ZXIgd2l0aCBJZGVudGl0eQoJICogUGhhc2UtTG9ja2luZywgSmVhbiBMYXJvY2hlICYgTWFyayBEb2xzb24sICJOZXcgUGhhc2UtVm9jb2RlciBUZWNobmlxdWVzIGZvcgoJICogUGl0Y2gtU2hpZnRpbmcsIEhhcm1vbml6aW5nIGFuZCBPdGhlciBFeG90aWMgRWZmZWN0cyIsIFByb2MuIDE5OTkgSUVFRSBXQVNQQUEuCgkgKgoJICogVGhpcyBmaWxlIG93bnMgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKEZGVCBmcmFtaW5nIG9uIHRvcCBvZiBPTEFQcm9jZXNzb3IsCgkgKiBwYXJhbWV0ZXJEZXNjcmlwdG9ycywgdGhlIHJlZ2lzdGVyUHJvY2Vzc29yIGNhbGwpLiBUaGUgcGVhayBkZXRlY3Rpb24sCgkgKiByZWdpb24tb2YtaW5mbHVlbmNlIHRyYW5zbGF0aW9uIGFuZCB0aGUgTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNQoJICogaWRlbnRpdHktcGhhc2UtbG9jayByb3RhdGlvbiBsaXZlIGluIHRoZSBjb250ZXh0LWZyZWUsIHVuaXQtdGVzdGVkCgkgKiBwaGFzZS12b2NvZGVyLWNvcmUudHMgKG1pcnJvcmluZyB0aGUgd2F2ZXNoYXBlciAvIGR5bmFtaWNzIGNvcmUvc2hlbGwgc3BsaXQpLgoJICovCgljb25zdCBCVUZGRVJFRF9CTE9DS19TSVpFID0gMjA0ODsKCWZ1bmN0aW9uIGdlbkhhbm5XaW5kb3cobGVuZ3RoKSB7CgkgICAgY29uc3Qgd2luID0gbmV3IEZsb2F0MzJBcnJheShsZW5ndGgpOwoJICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKCSAgICAgICAgd2luW2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIGxlbmd0aCkpOwoJICAgIH0KCSAgICByZXR1cm4gd2luOwoJfQoJY2xhc3MgUGhhc2VWb2NvZGVyUHJvY2Vzc29yIGV4dGVuZHMgT0xBUHJvY2Vzc29yIHsKCSAgICBmZnRTaXplOwoJICAgIHRpbWVDdXJzb3I7CgkgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNSBjdW11bGF0aXZlIHBoYXNlIFpfdSBpcyBQRVIgU0lHTkFMIFNUUkVBTToKCSAgICAvLyBlYWNoIGNoYW5uZWwgaXMgaXRzIG93biBzdHJlYW0sIHNvIGVhY2ggZ2V0cyBpdHMgb3duIFBlYWtSb3RhdG9yU3RhdGUuIEEKCSAgICAvLyBzaW5nbGUgc2hhcmVkIHN0YXRlIHdvdWxkIGxldCBjaGFubmVsIDAgYWR2YW5jZS91c2UgWl91IGFuZCB0aGVuIGNoYW5uZWwgMQoJICAgIC8vIGFkdmFuY2UgdGhlIFNBTUUgcGVhayB0byBaX3t1KzF9IGluIHRoZSBzYW1lIE9MQSBmcmFtZSAoaW50ZXItY2hhbm5lbCBwaGFzZQoJICAgIC8vIG1pc21hdGNoKSBhbmQgd291bGQgbGV0IG9uZSBjaGFubmVsJ3MgcGVhayBzZXQgcHJ1bmUgdGhlIG90aGVyJ3MuIEluZGV4ZWQKCSAgICAvLyBbaW5wdXRJbmRleF1bY2hhbm5lbEluZGV4XTsgZ3Jvd24gbGF6aWx5IGFzIGNoYW5uZWxzIGFwcGVhci4KCSAgICByb3RhdG9yczsKCSAgICBoYW5uV2luZG93OwoJICAgIGZmdDsKCSAgICAvLyBmZnQuanMgd2l0aCBhIEZsb2F0MzJBcnJheSBpbnB1dCByZXR1cm5zIGEgZmxhdCBpbnRlcmxlYXZlZCBGbG9hdDMyQXJyYXkKCSAgICAvLyBvZiBsZW5ndGggMipzaXplIChyZWFsL2ltYWcgcGFpcnMpLiBUaGUgbGlicmFyeSdzIHR5cGVzIGFyZSBgYW55W11gIHNvIHdlCgkgICAgLy8gbmFycm93IGF0IHRoZSBwZXJpbWV0ZXIg4oCUIHNlZSBhbGxvY2F0aW9uIHNpdGUgYmVsb3cuCgkgICAgZnJlcUNvbXBsZXhCdWZmZXI7CgkgICAgZnJlcUNvbXBsZXhCdWZmZXJTaGlmdGVkOwoJICAgIHRpbWVDb21wbGV4QnVmZmVyOwoJICAgIG1hZ25pdHVkZXM7CgkgICAgcGVha0luZGV4ZXM7CgkgICAgbmJQZWFrczsKCSAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewoJICAgICAgICByZXR1cm4gWwoJICAgICAgICAgICAgewoJICAgICAgICAgICAgICAgIG5hbWU6ICJwaXRjaEZhY3RvciIsCgkgICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiAxLjAsCgkgICAgICAgICAgICB9LAoJICAgICAgICBdOwoJICAgIH0KCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIC8vIE1lcmdlIGRlZmF1bHRzIHdpdGggY2FsbGVyLXN1cHBsaWVkIHByb2Nlc3Nvck9wdGlvbnMgKGNhbGxlciB3aW5zKS4KCSAgICAgICAgY29uc3QgYmFzZU9wdGlvbnMgPSBvcHRpb25zID8/IHt9OwoJICAgICAgICBjb25zdCBjYWxsZXJPcHRzID0gKGJhc2VPcHRpb25zLnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwoJICAgICAgICBiYXNlT3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID0gewoJICAgICAgICAgICAgYmxvY2tTaXplOiBCVUZGRVJFRF9CTE9DS19TSVpFLAoJICAgICAgICAgICAgLi4uY2FsbGVyT3B0cywKCSAgICAgICAgfTsKCSAgICAgICAgc3VwZXIoYmFzZU9wdGlvbnMpOwoJICAgICAgICB0aGlzLmZmdFNpemUgPSB0aGlzLmJsb2NrU2l6ZTsKCSAgICAgICAgdGhpcy50aW1lQ3Vyc29yID0gMDsKCSAgICAgICAgLy8gUGVyLWNoYW5uZWwgcGVyLXBlYWsgY3VtdWxhdGl2ZSBwaGFzZS1sb2NrIHN0YXRlIChMYXJvY2hlLURvbHNvbiAxOTk5CgkgICAgICAgIC8vIFNlY3Rpb24gMy41KTogWl97dSsxfSA9IFpfdSAqIGV4cChqKkRlbHRhLW9tZWdhKlIpLCBhZHZhbmNlZCBvbmNlIHBlcgoJICAgICAgICAvLyBmcmFtZSwga2VwdCBpbmRlcGVuZGVudGx5IGZvciBlYWNoIGNoYW5uZWwgKHNpZ25hbCBzdHJlYW0pLiBHcm93biBsYXppbHkKCSAgICAgICAgLy8gcGVyIGNoYW5uZWwgaW4gcHJvY2Vzc09MQSgpIHNvIGl0IG1hdGNoZXMgdGhlIGxpdmUgY2hhbm5lbCBjb3VudC4KCSAgICAgICAgdGhpcy5yb3RhdG9ycyA9IFtdOwoJICAgICAgICB0aGlzLmhhbm5XaW5kb3cgPSBnZW5IYW5uV2luZG93KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgLy8gcHJlcGFyZSBGRlQgYW5kIHByZS1hbGxvY2F0ZSBidWZmZXJzCgkgICAgICAgIHRoaXMuZmZ0ID0gbmV3IEZGVCh0aGlzLmZmdFNpemUpOwoJICAgICAgICAvLyBmZnQuanMgcmV0dXJucyBpdHMgY29tcGxleCBhcnJheSBhcyBgYW55W11gOyBydW50aW1lIGlzIGEgZmxhdCBGbG9hdDMyQXJyYXkuCgkgICAgICAgIHRoaXMuZnJlcUNvbXBsZXhCdWZmZXIgPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy5mcmVxQ29tcGxleEJ1ZmZlclNoaWZ0ZWQgPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy50aW1lQ29tcGxleEJ1ZmZlciA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm1hZ25pdHVkZXMgPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuZmZ0U2l6ZSAvIDIgKyAxKTsKCSAgICAgICAgdGhpcy5wZWFrSW5kZXhlcyA9IG5ldyBJbnQzMkFycmF5KHRoaXMubWFnbml0dWRlcy5sZW5ndGgpOwoJICAgICAgICB0aGlzLm5iUGVha3MgPSAwOwoJICAgIH0KCSAgICBwcm9jZXNzT0xBKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICBjb25zdCBwaXRjaEZhY3RvciA9IHBhcmFtZXRlcnMucGl0Y2hGYWN0b3JbcGFyYW1ldGVycy5waXRjaEZhY3Rvci5sZW5ndGggLSAxXTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IGlucHV0Um90YXRvcnMgPSAodGhpcy5yb3RhdG9yc1tpXSA/Pz0gW10pOwoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dHNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIC8vIEVhY2ggY2hhbm5lbCBrZWVwcyBpdHMgT1dOIGN1bXVsYXRpdmUgcm90YXRvciBzdGF0ZSAoTGFyb2NoZS1Eb2xzb24KCSAgICAgICAgICAgICAgICAvLyAxOTk5IFNlY3Rpb24gMy41IHBlci1zdHJlYW0gcGhhc2UpLiBTaGFyaW5nIG9uZSBhY3Jvc3MgY2hhbm5lbHMgd291bGQKCSAgICAgICAgICAgICAgICAvLyBkb3VibGUtYWR2YW5jZSBzaGFyZWQgcGVha3Mgd2l0aGluIGEgZnJhbWUgYW5kIGNyb3NzLXBydW5lIHBlYWsgc2V0cy4KCSAgICAgICAgICAgICAgICBjb25zdCBjaGFubmVsUm90YXRvcnMgPSAoaW5wdXRSb3RhdG9yc1tqXSA/Pz0gbmV3IFBlYWtSb3RhdG9yU3RhdGUoKSk7CgkgICAgICAgICAgICAgICAgdGhpcy5hcHBseUhhbm5XaW5kb3coaW5wdXQpOwoJICAgICAgICAgICAgICAgIHRoaXMuZmZ0LnJlYWxUcmFuc2Zvcm0odGhpcy5mcmVxQ29tcGxleEJ1ZmZlciwgaW5wdXQpOwoJICAgICAgICAgICAgICAgIC8vIFBlYWsgZGV0ZWN0ICsgcmVnaW9uLW9mLWluZmx1ZW5jZSB0cmFuc2xhdGUgKyBMYXJvY2hlLURvbHNvbiAxOTk5CgkgICAgICAgICAgICAgICAgLy8gU2VjdGlvbiAzLjUgaWRlbnRpdHktcGhhc2UtbG9jayByb3RhdGlvbiAob25lIFpfdSBwZXIgcGVhayBhcHBsaWVkCgkgICAgICAgICAgICAgICAgLy8gdW5pZm9ybWx5IHRvIGl0cyByZWdpb24pLiBBbGwgbWF0aCBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KCSAgICAgICAgICAgICAgICBjb21wdXRlTWFnbml0dWRlcyh0aGlzLmZyZXFDb21wbGV4QnVmZmVyLCB0aGlzLm1hZ25pdHVkZXMpOwoJICAgICAgICAgICAgICAgIHRoaXMubmJQZWFrcyA9IGZpbmRQZWFrcyh0aGlzLm1hZ25pdHVkZXMsIHRoaXMucGVha0luZGV4ZXMpOwoJICAgICAgICAgICAgICAgIC8vIEFjY3VtdWxhdGUgZWFjaCBwZWFrJ3MgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSBieSB0aGlzIGZyYW1lJ3MKCSAgICAgICAgICAgICAgICAvLyBleHAoaipEZWx0YS1vbWVnYSpSKSBCRUZPUkUgYXBwbHlpbmcgaXQgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbgoJICAgICAgICAgICAgICAgIC8vIDMuNSBjcm9zcy1mcmFtZSBjdW11bGF0aW9uKS4gaG9wU2l6ZSBpcyB0aGUgc3ludGhlc2lzIGhvcCBSLgoJICAgICAgICAgICAgICAgIGNoYW5uZWxSb3RhdG9ycy5hZHZhbmNlKHRoaXMucGVha0luZGV4ZXMsIHRoaXMubmJQZWFrcywgdGhpcy5mZnRTaXplLCBwaXRjaEZhY3RvciwgdGhpcy5ob3BTaXplKTsKCSAgICAgICAgICAgICAgICBzaGlmdFBlYWtzKHRoaXMuZnJlcUNvbXBsZXhCdWZmZXIsIHRoaXMuZnJlcUNvbXBsZXhCdWZmZXJTaGlmdGVkLCB0aGlzLnBlYWtJbmRleGVzLCB0aGlzLm5iUGVha3MsIHRoaXMuZmZ0U2l6ZSwgdGhpcy5tYWduaXR1ZGVzLmxlbmd0aCwgcGl0Y2hGYWN0b3IsIGNoYW5uZWxSb3RhdG9ycyk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuY29tcGxldGVTcGVjdHJ1bSh0aGlzLmZyZXFDb21wbGV4QnVmZmVyU2hpZnRlZCk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuaW52ZXJzZVRyYW5zZm9ybSh0aGlzLnRpbWVDb21wbGV4QnVmZmVyLCB0aGlzLmZyZXFDb21wbGV4QnVmZmVyU2hpZnRlZCk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuZnJvbUNvbXBsZXhBcnJheSh0aGlzLnRpbWVDb21wbGV4QnVmZmVyLCBvdXRwdXQpOwoJICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlIYW5uV2luZG93KG91dHB1dCk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgdGhpcy50aW1lQ3Vyc29yICs9IHRoaXMuaG9wU2l6ZTsKCSAgICB9CgkgICAgYXBwbHlIYW5uV2luZG93KGlucHV0KSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5ibG9ja1NpemU7IGkrKykgewoJICAgICAgICAgICAgaW5wdXRbaV0gKj0gdGhpcy5oYW5uV2luZG93W2ldOwoJICAgICAgICB9CgkgICAgfQoJfQoJcmVnaXN0ZXJQcm9jZXNzb3IoInBoYXNlLXZvY29kZXIiLCBQaGFzZVZvY29kZXJQcm9jZXNzb3IpOwoJY29uc29sZS5sb2coIlBoYXNlVm9jb2RlclByb2Nlc3NvciByZWdpc3RlcmVkIik7CgoJZXhwb3J0cy5QaGFzZVZvY29kZXJQcm9jZXNzb3IgPSBQaGFzZVZvY29kZXJQcm9jZXNzb3I7CgoJcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXBoYXNlLXZvY29kZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHBoYXNlciA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qCiAgICAgKiBQaGFzZXIgcHJvY2Vzc29yIGNvcmUg4oCUIGNvbnRleHQtZnJlZSBEU1AgbWF0aCBmb3IgYSBjbGFzc2ljIE1YUi9Vbml2aWJlLXN0eWxlCiAgICAgKiBwaGFzZSBzaGlmdGVyOiBhIGNhc2NhZGUgb2YgTiBpZGVudGljYWwgZmlyc3Qtb3JkZXIgYWxscGFzcyBzZWN0aW9ucyBhdCBhCiAgICAgKiBjb21tb24gTEZPLXN3ZXB0IGJyZWFrIGZyZXF1ZW5jeSwgc3VtbWVkIGFkZGl0aXZlbHkgd2l0aCB0aGUgZHJ5IHNpZ25hbC4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBhbGxwYXNzLXBoYXNlciBkZXNpZ24gZnJvbToKICAgICAqICAgSi4gTy4gU21pdGggSUlJLCAiQW4gQWxscGFzcyBBcHByb2FjaCB0byBEaWdpdGFsIFBoYXNpbmcgYW5kIEZsYW5naW5nIiwKICAgICAqICAgQ0NSTUEgUmVwb3J0IFNUQU4tTS0yMSAoMTk4MiksIHByZXNlbnRlZCBhdCBJQ01DLTg0OwogICAgICogICBhbmQgdGhlIGZpcnN0LW9yZGVyIHZpcnR1YWwtYW5hbG9nIHJlY2lwZSBpbiBKLiBPLiBTbWl0aCwgIlBoeXNpY2FsIEF1ZGlvCiAgICAgKiAgIFNpZ25hbCBQcm9jZXNzaW5nIiwgwqc4LjkgIkNsYXNzaWMgVmlydHVhbCBBbmFsb2cgUGhhc2UgU2hpZnRlcnMiLgogICAgICoKICAgICAqIFRvcG9sb2d5IChTbWl0aCBTVEFOLU0tMjEgVkc2L1ZHMTc7IFBBU1Agwqc4LjksIEZpZy4gOC4yMyk6CiAgICAgKiAgIHlbbl0gPSB4W25dICsgbWl4ICogY2FzY2FkZSh4W25dKQogICAgICogVGhlIGNhc2NhZGUgaXMgTiBmaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb25zIGluIHNlcmllcy4gQSBmaXJzdC1vcmRlciBhbGxwYXNzCiAgICAgKiBpcyBGTEFULW1hZ25pdHVkZSAofEh8ID0gMSBhdCBhbGwgZnJlcXVlbmNpZXMpLCBzbyB0aGUgRUZGRUNUIGNvbWVzIGVudGlyZWx5CiAgICAgKiBmcm9tIFNVTU1JTkcgdGhlIHBoYXNlLXNoaWZ0ZWQgY2FzY2FkZSBvdXRwdXQgd2l0aCB0aGUgZHJ5IHNpZ25hbDogYXQgZXZlcnkKICAgICAqIGZyZXF1ZW5jeSB3aGVyZSB0aGUgY2FzY2FkZSdzIGFjY3VtdWxhdGVkIHBoYXNlIHJlYWNoZXMgYW4gb2RkIG11bHRpcGxlIG9mCiAgICAgKiAxODAgZGVnIHRoZSBzdW0gY2FuY2VscywgcHJvZHVjaW5nIGEgbm90Y2guIFR3byBmaXJzdC1vcmRlciBzZWN0aW9ucyBjcmVhdGUKICAgICAqIG9uZSBub3RjaCAoUEFTUCDCpzguOTogImZvciBlYWNoIG5vdGNoIC4uLiBhZGQgdHdvIG5ldyBmaXJzdC1vcmRlciBhbGxwYXNzCiAgICAgKiBzZWN0aW9ucyIpLCBzbyBgc3RhZ2VzYCBzZWN0aW9ucyBnaXZlIHN0YWdlcy8yIG5vdGNoZXMuIFRoaXMgaXMgYW4gQURESVRJVkUKICAgICAqIHN1bSwgTk9UIGEgd2V0L2RyeSBjcm9zc2ZhZGUg4oCUIGEgY3Jvc3NmYWRlIG9mIGEgZmxhdC1tYWduaXR1ZGUgYWxscGFzcyB3b3VsZAogICAgICogbGVhdmUgdGhlIHNwZWN0cnVtIHVuY2hhbmdlZC4KICAgICAqCiAgICAgKiBGaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb24gKFBBU1Agwqc4LjkpLCBwZXItc2VjdGlvbiBzdGF0ZSB6MToKICAgICAqICAgSCh6KSA9IChhICsgel4tMSkgLyAoMSArIGEqel4tMSkKICAgICAqICAgdiAgPSBhKnMgKyB6MSA7ICB6MSA9IHMgLSBhKnYgICAgICAgIChvbmUtbXVsdGlwbHkgZGlyZWN0IGZvcm0pCiAgICAgKgogICAgICogQnJlYWstZnJlcXVlbmN5IC0+IGNvZWZmaWNpZW50IChleGFjdCBiaWxpbmVhci90YW4gbWFwLCBQQVNQIEVxLiA4LjIwKToKICAgICAqICAgcF9kID0gKDEgLSB0YW4ocGkqZmIvZnMpKSAvICgxICsgdGFuKHBpKmZiL2ZzKSkgICAgKGZiID0gYnJlYWsgZnJlcSwgSHopCiAgICAgKiBQQVNQIHdyaXRlcyBpdHMgYWxscGFzcyBhcyAocF9kIC0gel4tMSkvKDEgLSBwX2Qqel4tMSk7IGZvciBUSElTIHRlbXBsYXRlCiAgICAgKiAoYSArIHpeLTEpLygxICsgYSp6Xi0xKSB0aGUgbWF0Y2hpbmcgY29lZmZpY2llbnQgaXMgYSA9IC1wX2QgKFBBU1Agwqc4LjksIHRoZQogICAgICogc2lnbi1jb252ZW50aW9uIG5vdGUpLCBzbyB0aGUgY2FzY2FkZSdzIG5vdGNoZXMgbGFuZCBBVCB0aGUgYnJlYWsgZnJlcXVlbmN5CiAgICAgKiByYXRoZXIgdGhhbiBhdCBpdHMgTnlxdWlzdCBjb21wbGVtZW50LiBGb3IgZmIgaW4gKDAsIGZzLzIpIHRoaXMgZ2l2ZXMgfGF8IDwgMQogICAgICogKGEgc3RhYmxlIGFsbHBhc3MgcG9sZSksIGFuZCBTbWl0aCdzIHN0cnVjdHVyZS1nYWluIGJvdW5kIGtlZXBzIHRoZSBzdW1tZWQKICAgICAqIG91dHB1dCBzdHJpY3RseSBpbiBbMCwgMl0gZm9yIGFueSBub3RjaCBzZXR0aW5nIGdpdmVuIHN0YWJsZSBzZWN0aW9ucwogICAgICogKFNUQU4tTS0yMSBwYWdlLTAzKS4KICAgICAqCiAgICAgKiBQZXIgc2FtcGxlOgogICAgICogICBmYiAgID0gZnJlcXVlbmN5ICogMl4oZGVwdGggKiBsZm8pICAgICAgIC8vIG11bHRpcGxpY2F0aXZlL2xvZyBzd2VlcCAobGZvIGluIFstMSwxXSkKICAgICAqICAgcCAgICA9IGJpbGluZWFyKGZiKSAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgcHJlc2VydmVzIG5vdGNoIGZyZXF1ZW5jeSBSQVRJT1MKICAgICAqICAgcyAgICA9IHggKyBmZWVkYmFjayAqIGxhc3RDYXNjYWRlT3V0ICAgICAgIC8vIHJlZ2VuZXJhdGlvbi9yZXNvbmFuY2UgKGZlZWRiYWNrPTAgPT4gcz14KQogICAgICogICB2ICAgID0gY2FzY2FkZSBvZiBgc3RhZ2VzYCBhbGxwYXNzIHNlY3Rpb25zIGFwcGxpZWQgdG8gcwogICAgICogICB5ICAgID0geCArIG1peCAqIHYgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQURESVRJVkUgKFNtaXRoIHkgPSB4ICsgZyphbGxwYXNzKQogICAgICogICBsYXN0Q2FzY2FkZU91dCA9IHYKICAgICAqIExGTzogc2ludXNvaWRhbCwgYmlwb2xhciBbLTEsIDFdOyBwaGFzZSBhZHZhbmNlcyBhdCBgcmF0ZWAgSHouIFN0ZXJlbyB1c2VzIGEKICAgICAqIHF1YWRyYXR1cmUgTEZPICg5MCBkZWcgcGVyIGNoYW5uZWwsIERhdHRvcnJvIDE5OTcgcC43NzYpLCByZWFsaXplZCBieSBzZWVkaW5nCiAgICAgKiBlYWNoIGNoYW5uZWwncyBjb3JlIHdpdGggbGZvUGhhc2UwID0gY2ggKiBwaS8yIGF0IGNvbnN0cnVjdGlvbiAoc2VlIHBoYXNlci50cykuCiAgICAgKgogICAgICogVGhpcyBmaWxlIGhvbGRzIE9OTFkgcHVyZSBudW1lcmljIG1hdGggKHBsYWluIG51bWJlcnMgKyBGbG9hdDMyQXJyYXkpLiBJdCBoYXMKICAgICAqIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMsIHNvIGl0IGlzIHVuaXQtdGVzdGFibGUgZGlyZWN0bHk7IHRoZQogICAgICogd29ya2xldCBzaGVsbCBpbiBwaGFzZXIudHMgZGVsZWdhdGVzIHRvIGl0LgogICAgICovCiAgICAvKioKICAgICAqIERlZmF1bHQgQXVkaW9QYXJhbSB2YWx1ZXMgZm9yIHRoZSBwaGFzZXIgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncyBvd24gdW5pdHMuCiAgICAgKiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AgKHBoYXNlci50cykKICAgICAqIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdCBuZWVkIHRoZSBzaGlwcGVkCiAgICAgKiBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgY2xhc3NpYyA0LXNlY3Rpb24gKDItbm90Y2gpIE1YUi1zdHlsZSBwaGFzZXIsIG1pZC1zcGVjdHJ1bSBjZW50ZXIsCiAgICAgKiBzbG93IHN3ZWVwIOKAlCB0aGUgY3JlYXRlUGhhc2VyIGZhY3RvcnkgKGNhY29waG9ueS50cykgc3ByZWFkcyBvcHRpb24gb3ZlcnJpZGVzCiAgICAgKiBvdmVyIHRoZXNlLgogICAgICovCiAgICBjb25zdCBQSEFTRVJfREVGQVVMVFMgPSB7CiAgICAgICAgZnJlcXVlbmN5OiA1MDAsCiAgICAgICAgcmF0ZTogMC41LAogICAgICAgIGRlcHRoOiAxLjUsCiAgICAgICAgc3RhZ2VzOiA0LAogICAgICAgIGZlZWRiYWNrOiAwLAogICAgICAgIG1peDogMC41LAogICAgfTsKICAgIC8qKiBNYXhpbXVtIG51bWJlciBvZiBhbGxwYXNzIHNlY3Rpb25zIHRoZSBzZWN0aW9uLXN0YXRlIGFycmF5IGlzIHNpemVkIGZvci4gKi8KICAgIGNvbnN0IE1BWF9TVEFHRVMgPSAxMjsKICAgIC8qKiBTdGFiaWxpdHkvc2FmZXR5IGJvdW5kIG9uIHxmZWVkYmFja3wgZm9yIHRoZSByZWdlbmVyYXRpb24gbG9vcC4gKi8KICAgIGNvbnN0IE1BWF9QSEFTRVJfRkVFREJBQ0sgPSAwLjk1OwogICAgLyoqCiAgICAgKiBCcmVhay1mcmVxdWVuY3kgLT4gZmlyc3Qtb3JkZXItYWxscGFzcyBjb2VmZmljaWVudCB2aWEgdGhlIGV4YWN0IGJpbGluZWFyL3RhbgogICAgICogbWFwIChQQVNQIEVxLiA4LjIwKToKICAgICAqICAgcCA9ICgxIC0gdGFuKHBpKmZiL2ZzKSkgLyAoMSArIHRhbihwaSpmYi9mcykpCiAgICAgKiBGb3IgZmIgaW4gKDAsIGZzLzIpIHRoZSByZXN1bHQgbGllcyBpbiAoLTEsIDEpIChhIHN0YWJsZSBhbGxwYXNzIHBvbGUpOyBhcwogICAgICogZmIgLT4gMCwgcCAtPiAxLCBhbmQgYXMgZmIgLT4gZnMvMiwgcCAtPiAtMS4gRXhwb3NlZCBwdXJlIGZvciBpc29sYXRlZCB0ZXN0aW5nLgogICAgICovCiAgICBmdW5jdGlvbiBicmVha0ZyZXFUb0FsbHBhc3NDb2VmZihicmVha0ZyZXFIeiwgc2FtcGxlUmF0ZSkgewogICAgICAgIGNvbnN0IHQgPSBNYXRoLnRhbigoTWF0aC5QSSAqIGJyZWFrRnJlcUh6KSAvIHNhbXBsZVJhdGUpOwogICAgICAgIHJldHVybiAoMSAtIHQpIC8gKDEgKyB0KTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgcGhhc2VyIHByb2Nlc3NvciBmb3IgT05FIGNoYW5uZWwuIE93bnMgdGhlIHBlci1zZWN0aW9uIGFsbHBhc3Mgc3RhdGVzCiAgICAgKiAoejEgZm9yIGVhY2ggb2YgdXAgdG8gTUFYX1NUQUdFUyBzZWN0aW9ucyksIHRoZSBmZWVkYmFjayBtZW1vcnkgYW5kIHRoZSBMRk8KICAgICAqIHBoYXNlLCBhbmQgaXMgcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvIHRoZSBhbGxwYXNzIGhpc3RvcmllcywgcmVnZW5lcmF0aW9uIGFuZAogICAgICogTEZPIGNvbnRpbnVpdHkgYWxsIHN1cnZpdmUgYWNyb3NzIHByb2Nlc3MoKSBjYWxscyDigJQgY29uc3RydWN0aW5nIGEgZnJlc2gKICAgICAqIGluc3RhbmNlIHBlciBibG9jayB3b3VsZCBjbGVhciB0aGUgc2VjdGlvbiBzdGF0ZXMgYW5kIHJlc2V0IHRoZSBMRk8sIHByb2R1Y2luZwogICAgICogYXVkaWJsZSBjbGlja3MgKHNhbWUgaGF6YXJkIGRvY3VtZW50ZWQgZm9yIHRoZSBtb2R1bGF0ZWQtZGVsYXkgY29yZSkuCiAgICAgKi8KICAgIGNsYXNzIFBoYXNlclByb2Nlc3NvciB7CiAgICAgICAgc2FtcGxlUmF0ZTsKICAgICAgICAvKiogUGVyLXNlY3Rpb24gYWxscGFzcyBkZWxheS1lbGVtZW50IHN0YXRlcyB6MSAob25lIHNsb3QgcGVyIHNlY3Rpb24pLiAqLwogICAgICAgIHoxOwogICAgICAgIC8qKiBMYXN0IGNhc2NhZGUgb3V0cHV0LCBmZWQgYmFjayBpbnRvIHRoZSBpbnB1dCAocmVnZW5lcmF0aW9uKS4gKi8KICAgICAgICBsYXN0Q2FzY2FkZU91dCA9IDA7CiAgICAgICAgLyoqIEN1cnJlbnQgTEZPIHBoYXNlIChyYWRpYW5zKS4gKi8KICAgICAgICBsZm9QaGFzZTsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlLCBsZm9QaGFzZTAgPSAwKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMuejEgPSBuZXcgRmxvYXQzMkFycmF5KE1BWF9TVEFHRVMpOwogICAgICAgICAgICB0aGlzLmxmb1BoYXNlID0gbGZvUGhhc2UwOwogICAgICAgIH0KICAgICAgICAvKiogQ2xlYXIgYWxsIGFsbHBhc3Mgc2VjdGlvbiBzdGF0ZXMsIHRoZSBmZWVkYmFjayBtZW1vcnkgYW5kIHRoZSBMRk8gcGhhc2UuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuejEuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy5sYXN0Q2FzY2FkZU91dCA9IDA7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2suIGBpbnB1dGAgYW5kIGBvdXRwdXRgIG1heSBhbGlhcyAoaW4tcGxhY2UgaXMgZmluZSkuCiAgICAgICAgICoKICAgICAgICAgKiBQZXIgc2FtcGxlIChTbWl0aCBTVEFOLU0tMjEgLyBQQVNQIMKnOC45KToKICAgICAgICAgKiAgIDEuIGxmbyAgPSBzaW4obGZvUGhhc2UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGJpcG9sYXIgWy0xLCAxXSkKICAgICAgICAgKiAgIDIuIGZiICAgPSBmcmVxdWVuY3kgKiAyXihkZXB0aCAqIGxmbykgICAgICAgICAgICAgICAgKG11bHRpcGxpY2F0aXZlIHN3ZWVwKQogICAgICAgICAqICAgMy4gYSAgICA9IC1iaWxpbmVhcihmYikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoRXEuIDguMjAsIGEgPSAtcF9kKQogICAgICAgICAqICAgNC4gcyAgICA9IHggKyBmZWVkYmFjayAqIGxhc3RDYXNjYWRlT3V0ICAgICAgICAgICAgICAocmVnZW5lcmF0aW9uKQogICAgICAgICAqICAgNS4gdiAgICA9IGNhc2NhZGUgb2YgYHN0YWdlc2AgYWxscGFzcyBzZWN0aW9ucyBvbiBzICAob25lIHoxIGVhY2gpCiAgICAgICAgICogICA2LiB5ICAgID0geCArIG1peCAqIHYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChBRERJVElWRSBzdW0pCiAgICAgICAgICogICA3LiBsYXN0Q2FzY2FkZU91dCA9IHYKICAgICAgICAgKiBUaGUgTEZPIHBoYXNlIGFkdmFuY2VzIGJ5IDIqcGkqcmF0ZS9zYW1wbGVSYXRlIGVhY2ggc2FtcGxlLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIC8vIGstcmF0ZS1zdHlsZSBjb2VmZmljaWVudHMgY29tcHV0ZWQgb25jZSBwZXIgYmxvY2suCiAgICAgICAgICAgIGNvbnN0IGZyZXF1ZW5jeSA9IHBhcmFtcy5mcmVxdWVuY3k7CiAgICAgICAgICAgIGNvbnN0IGRlcHRoID0gcGFyYW1zLmRlcHRoOwogICAgICAgICAgICBjb25zdCBmZWVkYmFjayA9IE1hdGgubWF4KC1NQVhfUEhBU0VSX0ZFRURCQUNLLCBNYXRoLm1pbihNQVhfUEhBU0VSX0ZFRURCQUNLLCBwYXJhbXMuZmVlZGJhY2spKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1zLm1peDsKICAgICAgICAgICAgY29uc3Qgc3RhZ2VzID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oTUFYX1NUQUdFUywgTWF0aC5yb3VuZChwYXJhbXMuc3RhZ2VzKSkpOwogICAgICAgICAgICBjb25zdCBwaGFzZUluYyA9ICgyICogTWF0aC5QSSAqIHBhcmFtcy5yYXRlKSAvIHRoaXMuc2FtcGxlUmF0ZTsKICAgICAgICAgICAgY29uc3QgbnlxdWlzdCA9IHRoaXMuc2FtcGxlUmF0ZSAvIDI7CiAgICAgICAgICAgIGNvbnN0IG4gPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgeCA9IGlucHV0W2ldOwogICAgICAgICAgICAgICAgLy8gU2ludXNvaWRhbCBiaXBvbGFyIExGTyAtPiBtdWx0aXBsaWNhdGl2ZSBicmVhay1mcmVxdWVuY3kgc3dlZXAuIENsYW1wIHRoZQogICAgICAgICAgICAgICAgLy8gc3dlcHQgZnJlcXVlbmN5IHRvICgwLCBOeXF1aXN0KSBzbyB0aGUgYmlsaW5lYXIgbWFwIHN0YXlzIHdlbGwtZGVmaW5lZC4KICAgICAgICAgICAgICAgIGNvbnN0IGxmbyA9IE1hdGguc2luKHRoaXMubGZvUGhhc2UpOwogICAgICAgICAgICAgICAgbGV0IGZiID0gZnJlcXVlbmN5ICogMiAqKiAoZGVwdGggKiBsZm8pOwogICAgICAgICAgICAgICAgaWYgKGZiIDwgMSkKICAgICAgICAgICAgICAgICAgICBmYiA9IDE7CiAgICAgICAgICAgICAgICBpZiAoZmIgPiBueXF1aXN0IC0gMSkKICAgICAgICAgICAgICAgICAgICBmYiA9IG55cXVpc3QgLSAxOwogICAgICAgICAgICAgICAgLy8gQmlsaW5lYXIgcG9sZSBwX2QgKFBBU1AgRXEuIDguMjApLiBUaGUgc2VjdGlvbiB0ZW1wbGF0ZSBoZXJlIGlzCiAgICAgICAgICAgICAgICAvLyAoYSArIHpeLTEpLygxICsgYSp6Xi0xKSwgc28gYSA9IC1wX2QgbWFrZXMgdGhlIGNhc2NhZGUgbm90Y2ggQVQgZmIKICAgICAgICAgICAgICAgIC8vIChub3QgYXQgaXRzIE55cXVpc3QgY29tcGxlbWVudCkuIFNlZSBhbGxwYXNzU3RlcCdzIHNpZ24gbm90ZS4KICAgICAgICAgICAgICAgIGNvbnN0IGEgPSAtYnJlYWtGcmVxVG9BbGxwYXNzQ29lZmYoZmIsIHRoaXMuc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgICAgICAvLyBSZWdlbmVyYXRpb246IGZlZWQgdGhlIHByZXZpb3VzIGNhc2NhZGUgb3V0cHV0IGJhY2sgaW50byB0aGUgaW5wdXQuCiAgICAgICAgICAgICAgICBsZXQgdiA9IHggKyBmZWVkYmFjayAqIHRoaXMubGFzdENhc2NhZGVPdXQ7CiAgICAgICAgICAgICAgICAvLyBDYXNjYWRlIG9mIGBzdGFnZXNgIGlkZW50aWNhbCBmaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb25zLgogICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCBzdGFnZXM7IGsrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHByZXYgPSB0aGlzLnoxW2tdOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG91dCA9IGEgKiB2ICsgcHJldjsKICAgICAgICAgICAgICAgICAgICB0aGlzLnoxW2tdID0gdiAtIGEgKiBvdXQ7CiAgICAgICAgICAgICAgICAgICAgdiA9IG91dDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMubGFzdENhc2NhZGVPdXQgPSB2OwogICAgICAgICAgICAgICAgLy8gQWRkaXRpdmUgb3V0cHV0IChTbWl0aCB5ID0geCArIGcqYWxscGFzcyk6IHRoZSBub3RjaGVzIGNvbWUgZnJvbSB0aGUgc3VtLgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0geCArIG1peCAqIHY7CiAgICAgICAgICAgICAgICAvLyBBZHZhbmNlIHRoZSBMRk8gcGhhc2UuCiAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlICs9IHBoYXNlSW5jOwogICAgICAgICAgICAgICAgaWYgKHRoaXMubGZvUGhhc2UgPj0gMiAqIE1hdGguUEkpCiAgICAgICAgICAgICAgICAgICAgdGhpcy5sZm9QaGFzZSAtPSAyICogTWF0aC5QSTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKgogICAgICogUGhhc2VyIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMIERTUAogICAgICogbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFBoYXNlclByb2Nlc3NvciBpbiBwaGFzZXItY29yZS50cy4gTWlycm9ycyB0aGUKICAgICAqIG1vZHVsYXRlZC1kZWxheS50cyAvIGR5bmFtaWNzLnRzIGNvcmUvc2hlbGwgc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlCiAgICAgKiB3b3JrbGV0IHBsdW1iaW5nIChwYXJhbWV0ZXJEZXNjcmlwdG9ycywgcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IKICAgICAqIGNhbGwpOyB0aGUgYWxnb3JpdGhtIGxpdmVzIGluIHRoZSB1bml0LXRlc3RlZCBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogY2xhc3NpYyBNWFIvVW5pdmliZS1zdHlsZSBjYXNjYWRlIG9mIGZpcnN0LW9yZGVyIGFsbHBhc3Mgc2VjdGlvbnMKICAgICAqIGF0IGEgY29tbW9uIExGTy1zd2VwdCBicmVhayBmcmVxdWVuY3ksIHN1bW1lZCBhZGRpdGl2ZWx5IHdpdGggdGhlIGRyeSBzaWduYWwKICAgICAqIChTbWl0aCBTVEFOLU0tMjE7IFBBU1Agwqc4LjkpLiBTZWUgcGhhc2VyLWNvcmUudHMgaGVhZGVyIGZvciB0aGUgc2VjdGlvbgogICAgICogdHJhbnNmZXIgZnVuY3Rpb24sIHRoZSBiaWxpbmVhciBicmVhay1mcmVxdWVuY3kgbWFwLCB0aGUgYWRkaXRpdmUtbm90Y2gKICAgICAqIHJhdGlvbmFsZSBhbmQgdGhlIG11bHRpcGxpY2F0aXZlIExGTyBzd2VlcC4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpwaGFzZXJdIjsKICAgIGNsYXNzIFBoYXNlcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIC8vIE9uZSBzdGF0ZWZ1bCBjb3JlIHBlciBjaGFubmVsIHNvIGVhY2ggY2hhbm5lbCBrZWVwcyBpdHMgb3duIGFsbHBhc3Mgc2VjdGlvbgogICAgICAgIC8vIHN0YXRlcywgZmVlZGJhY2sgbWVtb3J5IGFuZCBMRk8gcGhhc2UgYWNyb3NzIHByb2Nlc3MoKSBibG9ja3MuIEVhY2gKICAgICAgICAvLyBjaGFubmVsJ3MgTEZPIGlzIHNlZWRlZCBpbiBxdWFkcmF0dXJlIChjaCAqIHBpLzIpIHNvIGEgc3RlcmVvIHBhaXIgZ2V0cyB0aGUKICAgICAgICAvLyBkeW5hbWljIHN0ZXJlbyBmaWVsZCBEYXR0b3JybyBkZXNjcmliZXMgKHAuNzc2KS4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBQSEFTRVJfREVGQVVMVFMgKHBoYXNlci1jb3JlLnRzKSDigJQgdGhlIHNpbmdsZQogICAgICAgICAgICAvLyBzb3VyY2Ugb2YgdHJ1dGggc2hhcmVkIHdpdGggdGhlIGNvcmUgdGVzdHMuIFJhbmdlcyBsaXZlIGhlcmUuIGBzdGFnZXNgIGlzCiAgICAgICAgICAgIC8vIGEgY291bnQgKHJvdW5kZWQgdG8gYW4gaW50IGluIHRoZSBjb3JlKTsgfGZlZWRiYWNrfCA8PSAwLjk1IGtlZXBzIHRoZQogICAgICAgICAgICAvLyByZWdlbmVyYXRpb24gbG9vcCBib3VuZGVkLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJmcmVxdWVuY3kiLCBQSEFTRVJfREVGQVVMVFMuZnJlcXVlbmN5LCAyMCwgMTAwMDAsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsicmF0ZSIsIFBIQVNFUl9ERUZBVUxUUy5yYXRlLCAwLCAyMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkZXB0aCIsIFBIQVNFUl9ERUZBVUxUUy5kZXB0aCwgMCwgNCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJzdGFnZXMiLCBQSEFTRVJfREVGQVVMVFMuc3RhZ2VzLCAyLCAxMiwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJmZWVkYmFjayIsIFBIQVNFUl9ERUZBVUxUUy5mZWVkYmFjaywgLTAuOTUsIDAuOTUsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWl4IiwgUEhBU0VSX0RFRkFVTFRTLm1peCwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICBdLm1hcCgoW25hbWUsIGRlZmF1bHRWYWx1ZSwgbWluVmFsdWUsIG1heFZhbHVlLCBhdXRvbWF0aW9uUmF0ZV0pID0+ICh7CiAgICAgICAgICAgICAgICBuYW1lOiBuYW1lLAogICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUsCiAgICAgICAgICAgICAgICBtaW5WYWx1ZTogbWluVmFsdWUsCiAgICAgICAgICAgICAgICBtYXhWYWx1ZTogbWF4VmFsdWUsCiAgICAgICAgICAgICAgICBhdXRvbWF0aW9uUmF0ZTogYXV0b21hdGlvblJhdGUsCiAgICAgICAgICAgIH0pKTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF07CiAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICAgICAgICAgIGlmICghaW5wdXQgfHwgaW5wdXQubGVuZ3RoID09PSAwIHx8ICFvdXRwdXQgfHwgb3V0cHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gay1yYXRlIHBhcmFtczogdGFrZSB0aGUgZmlyc3QgKGNvbnN0YW50LW92ZXItYmxvY2spIHZhbHVlLgogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICBmcmVxdWVuY3k6IHBhcmFtZXRlcnMuZnJlcXVlbmN5WzBdLAogICAgICAgICAgICAgICAgcmF0ZTogcGFyYW1ldGVycy5yYXRlWzBdLAogICAgICAgICAgICAgICAgZGVwdGg6IHBhcmFtZXRlcnMuZGVwdGhbMF0sCiAgICAgICAgICAgICAgICBzdGFnZXM6IHBhcmFtZXRlcnMuc3RhZ2VzWzBdLAogICAgICAgICAgICAgICAgZmVlZGJhY2s6IHBhcmFtZXRlcnMuZmVlZGJhY2tbMF0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gU2VlZCB0aGUgTEZPIDkwIGRlZyBhcGFydCBwZXIgY2hhbm5lbCBmb3IgdGhlIHF1YWRyYXR1cmUgc3RlcmVvIGZpZWxkCiAgICAgICAgICAgICAgICAgICAgLy8gKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBQaGFzZXJQcm9jZXNzb3Ioc2FtcGxlUmF0ZSwgKGNoICogTWF0aC5QSSkgLyAyKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJwaGFzZXIiLCBQaGFzZXJXb3JrbGV0UHJvY2Vzc29yKTsKICAgICAgICBjb25zb2xlLmluZm8oYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBjb21wbGV0ZWApOwogICAgfQogICAgY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgY29uc29sZS5lcnJvcihgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGZhaWxlZGAsIGVycm9yKTsKICAgICAgICB0aHJvdyBlcnJvcjsKICAgIH0KCiAgICBleHBvcnRzLlBoYXNlcldvcmtsZXRQcm9jZXNzb3IgPSBQaGFzZXJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1waGFzZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHNwZWN0cmFsRnJlZXplID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CgkndXNlIHN0cmljdCc7CgoJZnVuY3Rpb24gZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMgKHgpIHsKCQlyZXR1cm4geCAmJiB4Ll9fZXNNb2R1bGUgJiYgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdkZWZhdWx0JykgPyB4WydkZWZhdWx0J10gOiB4OwoJfQoKCXZhciBmZnQ7Cgl2YXIgaGFzUmVxdWlyZWRGZnQ7CgoJZnVuY3Rpb24gcmVxdWlyZUZmdCAoKSB7CgkJaWYgKGhhc1JlcXVpcmVkRmZ0KSByZXR1cm4gZmZ0OwoJCWhhc1JlcXVpcmVkRmZ0ID0gMTsKCgkJZnVuY3Rpb24gRkZUKHNpemUpIHsKCQkgIHRoaXMuc2l6ZSA9IHNpemUgfCAwOwoJCSAgaWYgKHRoaXMuc2l6ZSA8PSAxIHx8ICh0aGlzLnNpemUgJiAodGhpcy5zaXplIC0gMSkpICE9PSAwKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZGVCBzaXplIG11c3QgYmUgYSBwb3dlciBvZiB0d28gYW5kIGJpZ2dlciB0aGFuIDEnKTsKCgkJICB0aGlzLl9jc2l6ZSA9IHNpemUgPDwgMTsKCgkJICAvLyBOT1RFOiBVc2Ugb2YgYHZhcmAgaXMgaW50ZW50aW9uYWwgZm9yIG9sZCBWOCB2ZXJzaW9ucwoJCSAgdmFyIHRhYmxlID0gbmV3IEFycmF5KHRoaXMuc2l6ZSAqIDIpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCB0YWJsZS5sZW5ndGg7IGkgKz0gMikgewoJCSAgICBjb25zdCBhbmdsZSA9IE1hdGguUEkgKiBpIC8gdGhpcy5zaXplOwoJCSAgICB0YWJsZVtpXSA9IE1hdGguY29zKGFuZ2xlKTsKCQkgICAgdGFibGVbaSArIDFdID0gLU1hdGguc2luKGFuZ2xlKTsKCQkgIH0KCQkgIHRoaXMudGFibGUgPSB0YWJsZTsKCgkJICAvLyBGaW5kIHNpemUncyBwb3dlciBvZiB0d28KCQkgIHZhciBwb3dlciA9IDA7CgkJICBmb3IgKHZhciB0ID0gMTsgdGhpcy5zaXplID4gdDsgdCA8PD0gMSkKCQkgICAgcG93ZXIrKzsKCgkJICAvLyBDYWxjdWxhdGUgaW5pdGlhbCBzdGVwJ3Mgd2lkdGg6CgkJICAvLyAgICogSWYgd2UgYXJlIGZ1bGwgcmFkaXgtNCAtIGl0IGlzIDJ4IHNtYWxsZXIgdG8gZ2l2ZSBpbml0YWwgbGVuPTgKCQkgIC8vICAgKiBPdGhlcndpc2UgaXQgaXMgdGhlIHNhbWUgYXMgYHBvd2VyYCB0byBnaXZlIGxlbj00CgkJICB0aGlzLl93aWR0aCA9IHBvd2VyICUgMiA9PT0gMCA/IHBvd2VyIC0gMSA6IHBvd2VyOwoKCQkgIC8vIFByZS1jb21wdXRlIGJpdC1yZXZlcnNhbCBwYXR0ZXJucwoJCSAgdGhpcy5fYml0cmV2ID0gbmV3IEFycmF5KDEgPDwgdGhpcy5fd2lkdGgpOwoJCSAgZm9yICh2YXIgaiA9IDA7IGogPCB0aGlzLl9iaXRyZXYubGVuZ3RoOyBqKyspIHsKCQkgICAgdGhpcy5fYml0cmV2W2pdID0gMDsKCQkgICAgZm9yICh2YXIgc2hpZnQgPSAwOyBzaGlmdCA8IHRoaXMuX3dpZHRoOyBzaGlmdCArPSAyKSB7CgkJICAgICAgdmFyIHJldlNoaWZ0ID0gdGhpcy5fd2lkdGggLSBzaGlmdCAtIDI7CgkJICAgICAgdGhpcy5fYml0cmV2W2pdIHw9ICgoaiA+Pj4gc2hpZnQpICYgMykgPDwgcmV2U2hpZnQ7CgkJICAgIH0KCQkgIH0KCgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJICB0aGlzLl9pbnYgPSAwOwoJCX0KCQlmZnQgPSBGRlQ7CgoJCUZGVC5wcm90b3R5cGUuZnJvbUNvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIGZyb21Db21wbGV4QXJyYXkoY29tcGxleCwgc3RvcmFnZSkgewoJCSAgdmFyIHJlcyA9IHN0b3JhZ2UgfHwgbmV3IEFycmF5KGNvbXBsZXgubGVuZ3RoID4+PiAxKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgY29tcGxleC5sZW5ndGg7IGkgKz0gMikKCQkgICAgcmVzW2kgPj4+IDFdID0gY29tcGxleFtpXTsKCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5jcmVhdGVDb21wbGV4QXJyYXkgPSBmdW5jdGlvbiBjcmVhdGVDb21wbGV4QXJyYXkoKSB7CgkJICBjb25zdCByZXMgPSBuZXcgQXJyYXkodGhpcy5fY3NpemUpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXMubGVuZ3RoOyBpKyspCgkJICAgIHJlc1tpXSA9IDA7CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUudG9Db21wbGV4QXJyYXkgPSBmdW5jdGlvbiB0b0NvbXBsZXhBcnJheShpbnB1dCwgc3RvcmFnZSkgewoJCSAgdmFyIHJlcyA9IHN0b3JhZ2UgfHwgdGhpcy5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzLmxlbmd0aDsgaSArPSAyKSB7CgkJICAgIHJlc1tpXSA9IGlucHV0W2kgPj4+IDFdOwoJCSAgICByZXNbaSArIDFdID0gMDsKCQkgIH0KCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5jb21wbGV0ZVNwZWN0cnVtID0gZnVuY3Rpb24gY29tcGxldGVTcGVjdHJ1bShzcGVjdHJ1bSkgewoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCQkgIHZhciBoYWxmID0gc2l6ZSA+Pj4gMTsKCQkgIGZvciAodmFyIGkgPSAyOyBpIDwgaGFsZjsgaSArPSAyKSB7CgkJICAgIHNwZWN0cnVtW3NpemUgLSBpXSA9IHNwZWN0cnVtW2ldOwoJCSAgICBzcGVjdHJ1bVtzaXplIC0gaSArIDFdID0gLXNwZWN0cnVtW2kgKyAxXTsKCQkgIH0KCQl9OwoKCQlGRlQucHJvdG90eXBlLnRyYW5zZm9ybSA9IGZ1bmN0aW9uIHRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAwOwoJCSAgdGhpcy5fdHJhbnNmb3JtNCgpOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCUZGVC5wcm90b3R5cGUucmVhbFRyYW5zZm9ybSA9IGZ1bmN0aW9uIHJlYWxUcmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMDsKCQkgIHRoaXMuX3JlYWxUcmFuc2Zvcm00KCk7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5pbnZlcnNlVHJhbnNmb3JtID0gZnVuY3Rpb24gaW52ZXJzZVRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAxOwoJCSAgdGhpcy5fdHJhbnNmb3JtNCgpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCBvdXQubGVuZ3RoOyBpKyspCgkJICAgIG91dFtpXSAvPSB0aGlzLnNpemU7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJLy8gcmFkaXgtNCBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogVXNlcyBvZiBgdmFyYCBhcmUgaW50ZW50aW9uYWwgZm9yIG9sZGVyIFY4IHZlcnNpb24gdGhhdCBkbyBub3QKCQkvLyBzdXBwb3J0IGJvdGggYGxldCBjb21wb3VuZCBhc3NpZ25tZW50c2AgYW5kIGBjb25zdCBwaGlgCgkJRkZULnByb3RvdHlwZS5fdHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF90cmFuc2Zvcm00KCkgewoJCSAgdmFyIG91dCA9IHRoaXMuX291dDsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgoJCSAgLy8gSW5pdGlhbCBzdGVwIChwZXJtdXRlIGFuZCB0cmFuc2Zvcm0pCgkJICB2YXIgd2lkdGggPSB0aGlzLl93aWR0aDsKCQkgIHZhciBzdGVwID0gMSA8PCB3aWR0aDsKCQkgIHZhciBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgoJCSAgdmFyIG91dE9mZjsKCQkgIHZhciB0OwoJCSAgdmFyIGJpdHJldiA9IHRoaXMuX2JpdHJldjsKCQkgIGlmIChsZW4gPT09IDQpIHsKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVRyYW5zZm9ybTIob3V0T2ZmLCBvZmYsIHN0ZXApOwoJCSAgICB9CgkJICB9IGVsc2UgewoJCSAgICAvLyBsZW4gPT09IDgKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVRyYW5zZm9ybTQob3V0T2ZmLCBvZmYsIHN0ZXApOwoJCSAgICB9CgkJICB9CgoJCSAgLy8gTG9vcCB0aHJvdWdoIHN0ZXBzIGluIGRlY3JlYXNpbmcgb3JkZXIKCQkgIHZhciBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICB2YXIgdGFibGUgPSB0aGlzLnRhYmxlOwoJCSAgZm9yIChzdGVwID4+PSAyOyBzdGVwID49IDI7IHN0ZXAgPj49IDIpIHsKCQkgICAgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoJCSAgICB2YXIgcXVhcnRlckxlbiA9IGxlbiA+Pj4gMjsKCgkJICAgIC8vIExvb3AgdGhyb3VnaCBvZmZzZXRzIGluIHRoZSBkYXRhCgkJICAgIGZvciAob3V0T2ZmID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbikgewoJCSAgICAgIC8vIEZ1bGwgY2FzZQoJCSAgICAgIHZhciBsaW1pdCA9IG91dE9mZiArIHF1YXJ0ZXJMZW47CgkJICAgICAgZm9yICh2YXIgaSA9IG91dE9mZiwgayA9IDA7IGkgPCBsaW1pdDsgaSArPSAyLCBrICs9IHN0ZXApIHsKCQkgICAgICAgIGNvbnN0IEEgPSBpOwoJCSAgICAgICAgY29uc3QgQiA9IEEgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgY29uc3QgQyA9IEIgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgY29uc3QgRCA9IEMgKyBxdWFydGVyTGVuOwoKCQkgICAgICAgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgQXIgPSBvdXRbQV07CgkJICAgICAgICBjb25zdCBBaSA9IG91dFtBICsgMV07CgkJICAgICAgICBjb25zdCBCciA9IG91dFtCXTsKCQkgICAgICAgIGNvbnN0IEJpID0gb3V0W0IgKyAxXTsKCQkgICAgICAgIGNvbnN0IENyID0gb3V0W0NdOwoJCSAgICAgICAgY29uc3QgQ2kgPSBvdXRbQyArIDFdOwoJCSAgICAgICAgY29uc3QgRHIgPSBvdXRbRF07CgkJICAgICAgICBjb25zdCBEaSA9IG91dFtEICsgMV07CgoJCSAgICAgICAgLy8gTWlkZGxlIHZhbHVlcwoJCSAgICAgICAgY29uc3QgTUFyID0gQXI7CgkJICAgICAgICBjb25zdCBNQWkgPSBBaTsKCgkJICAgICAgICBjb25zdCB0YWJsZUJyID0gdGFibGVba107CgkJICAgICAgICBjb25zdCB0YWJsZUJpID0gaW52ICogdGFibGVbayArIDFdOwoJCSAgICAgICAgY29uc3QgTUJyID0gQnIgKiB0YWJsZUJyIC0gQmkgKiB0YWJsZUJpOwoJCSAgICAgICAgY29uc3QgTUJpID0gQnIgKiB0YWJsZUJpICsgQmkgKiB0YWJsZUJyOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlQ3IgPSB0YWJsZVsyICoga107CgkJICAgICAgICBjb25zdCB0YWJsZUNpID0gaW52ICogdGFibGVbMiAqIGsgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1DciA9IENyICogdGFibGVDciAtIENpICogdGFibGVDaTsKCQkgICAgICAgIGNvbnN0IE1DaSA9IENyICogdGFibGVDaSArIENpICogdGFibGVDcjsKCgkJICAgICAgICBjb25zdCB0YWJsZURyID0gdGFibGVbMyAqIGtdOwoJCSAgICAgICAgY29uc3QgdGFibGVEaSA9IGludiAqIHRhYmxlWzMgKiBrICsgMV07CgkJICAgICAgICBjb25zdCBNRHIgPSBEciAqIHRhYmxlRHIgLSBEaSAqIHRhYmxlRGk7CgkJICAgICAgICBjb25zdCBNRGkgPSBEciAqIHRhYmxlRGkgKyBEaSAqIHRhYmxlRHI7CgoJCSAgICAgICAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgVDByID0gTUFyICsgTUNyOwoJCSAgICAgICAgY29uc3QgVDBpID0gTUFpICsgTUNpOwoJCSAgICAgICAgY29uc3QgVDFyID0gTUFyIC0gTUNyOwoJCSAgICAgICAgY29uc3QgVDFpID0gTUFpIC0gTUNpOwoJCSAgICAgICAgY29uc3QgVDJyID0gTUJyICsgTURyOwoJCSAgICAgICAgY29uc3QgVDJpID0gTUJpICsgTURpOwoJCSAgICAgICAgY29uc3QgVDNyID0gaW52ICogKE1CciAtIE1Ecik7CgkJICAgICAgICBjb25zdCBUM2kgPSBpbnYgKiAoTUJpIC0gTURpKTsKCgkJICAgICAgICAvLyBGaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCQkgICAgICAgIGNvbnN0IEZBaSA9IFQwaSArIFQyaTsKCgkJICAgICAgICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgkJICAgICAgICBjb25zdCBGQ2kgPSBUMGkgLSBUMmk7CgoJCSAgICAgICAgY29uc3QgRkJyID0gVDFyICsgVDNpOwoJCSAgICAgICAgY29uc3QgRkJpID0gVDFpIC0gVDNyOwoKCQkgICAgICAgIGNvbnN0IEZEciA9IFQxciAtIFQzaTsKCQkgICAgICAgIGNvbnN0IEZEaSA9IFQxaSArIFQzcjsKCgkJICAgICAgICBvdXRbQV0gPSBGQXI7CgkJICAgICAgICBvdXRbQSArIDFdID0gRkFpOwoJCSAgICAgICAgb3V0W0JdID0gRkJyOwoJCSAgICAgICAgb3V0W0IgKyAxXSA9IEZCaTsKCQkgICAgICAgIG91dFtDXSA9IEZDcjsKCQkgICAgICAgIG91dFtDICsgMV0gPSBGQ2k7CgkJICAgICAgICBvdXRbRF0gPSBGRHI7CgkJICAgICAgICBvdXRbRCArIDFdID0gRkRpOwoJCSAgICAgIH0KCQkgICAgfQoJCSAgfQoJCX07CgoJCS8vIHJhZGl4LTIgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49NAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVRyYW5zZm9ybTIgPSBmdW5jdGlvbiBfc2luZ2xlVHJhbnNmb3JtMihvdXRPZmYsIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgoJCSAgY29uc3QgZXZlblIgPSBkYXRhW29mZl07CgkJICBjb25zdCBldmVuSSA9IGRhdGFbb2ZmICsgMV07CgkJICBjb25zdCBvZGRSID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IG9kZEkgPSBkYXRhW29mZiArIHN0ZXAgKyAxXTsKCgkJICBjb25zdCBsZWZ0UiA9IGV2ZW5SICsgb2RkUjsKCQkgIGNvbnN0IGxlZnRJID0gZXZlbkkgKyBvZGRJOwoJCSAgY29uc3QgcmlnaHRSID0gZXZlblIgLSBvZGRSOwoJCSAgY29uc3QgcmlnaHRJID0gZXZlbkkgLSBvZGRJOwoKCQkgIG91dFtvdXRPZmZdID0gbGVmdFI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSBsZWZ0STsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IHJpZ2h0UjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IHJpZ2h0STsKCQl9OwoKCQkvLyByYWRpeC00CgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTgKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3NpbmdsZVRyYW5zZm9ybTQob3V0T2ZmLCBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoJCSAgY29uc3QgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgY29uc3Qgc3RlcDIgPSBzdGVwICogMjsKCQkgIGNvbnN0IHN0ZXAzID0gc3RlcCAqIDM7CgoJCSAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICBjb25zdCBBciA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IEFpID0gZGF0YVtvZmYgKyAxXTsKCQkgIGNvbnN0IEJyID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IEJpID0gZGF0YVtvZmYgKyBzdGVwICsgMV07CgkJICBjb25zdCBDciA9IGRhdGFbb2ZmICsgc3RlcDJdOwoJCSAgY29uc3QgQ2kgPSBkYXRhW29mZiArIHN0ZXAyICsgMV07CgkJICBjb25zdCBEciA9IGRhdGFbb2ZmICsgc3RlcDNdOwoJCSAgY29uc3QgRGkgPSBkYXRhW29mZiArIHN0ZXAzICsgMV07CgoJCSAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgY29uc3QgVDByID0gQXIgKyBDcjsKCQkgIGNvbnN0IFQwaSA9IEFpICsgQ2k7CgkJICBjb25zdCBUMXIgPSBBciAtIENyOwoJCSAgY29uc3QgVDFpID0gQWkgLSBDaTsKCQkgIGNvbnN0IFQyciA9IEJyICsgRHI7CgkJICBjb25zdCBUMmkgPSBCaSArIERpOwoJCSAgY29uc3QgVDNyID0gaW52ICogKEJyIC0gRHIpOwoJCSAgY29uc3QgVDNpID0gaW52ICogKEJpIC0gRGkpOwoKCQkgIC8vIEZpbmFsIHZhbHVlcwoJCSAgY29uc3QgRkFyID0gVDByICsgVDJyOwoJCSAgY29uc3QgRkFpID0gVDBpICsgVDJpOwoKCQkgIGNvbnN0IEZCciA9IFQxciArIFQzaTsKCQkgIGNvbnN0IEZCaSA9IFQxaSAtIFQzcjsKCgkJICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgkJICBjb25zdCBGQ2kgPSBUMGkgLSBUMmk7CgoJCSAgY29uc3QgRkRyID0gVDFyIC0gVDNpOwoJCSAgY29uc3QgRkRpID0gVDFpICsgVDNyOwoKCQkgIG91dFtvdXRPZmZdID0gRkFyOwoJCSAgb3V0W291dE9mZiArIDFdID0gRkFpOwoJCSAgb3V0W291dE9mZiArIDJdID0gRkJyOwoJCSAgb3V0W291dE9mZiArIDNdID0gRkJpOwoJCSAgb3V0W291dE9mZiArIDRdID0gRkNyOwoJCSAgb3V0W291dE9mZiArIDVdID0gRkNpOwoJCSAgb3V0W291dE9mZiArIDZdID0gRkRyOwoJCSAgb3V0W291dE9mZiArIDddID0gRkRpOwoJCX07CgoJCS8vIFJlYWwgaW5wdXQgcmFkaXgtNCBpbXBsZW1lbnRhdGlvbgoJCUZGVC5wcm90b3R5cGUuX3JlYWxUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3JlYWxUcmFuc2Zvcm00KCkgewoJCSAgdmFyIG91dCA9IHRoaXMuX291dDsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgoJCSAgLy8gSW5pdGlhbCBzdGVwIChwZXJtdXRlIGFuZCB0cmFuc2Zvcm0pCgkJICB2YXIgd2lkdGggPSB0aGlzLl93aWR0aDsKCQkgIHZhciBzdGVwID0gMSA8PCB3aWR0aDsKCQkgIHZhciBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgoJCSAgdmFyIG91dE9mZjsKCQkgIHZhciB0OwoJCSAgdmFyIGJpdHJldiA9IHRoaXMuX2JpdHJldjsKCQkgIGlmIChsZW4gPT09IDQpIHsKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVJlYWxUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmID4+PiAxLCBzdGVwID4+PiAxKTsKCQkgICAgfQoJCSAgfSBlbHNlIHsKCQkgICAgLy8gbGVuID09PSA4CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVSZWFsVHJhbnNmb3JtNChvdXRPZmYsIG9mZiA+Pj4gMSwgc3RlcCA+Pj4gMSk7CgkJICAgIH0KCQkgIH0KCgkJICAvLyBMb29wIHRocm91Z2ggc3RlcHMgaW4gZGVjcmVhc2luZyBvcmRlcgoJCSAgdmFyIGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIHZhciB0YWJsZSA9IHRoaXMudGFibGU7CgkJICBmb3IgKHN0ZXAgPj49IDI7IHN0ZXAgPj0gMjsgc3RlcCA+Pj0gMikgewoJCSAgICBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgkJICAgIHZhciBoYWxmTGVuID0gbGVuID4+PiAxOwoJCSAgICB2YXIgcXVhcnRlckxlbiA9IGhhbGZMZW4gPj4+IDE7CgkJICAgIHZhciBocXVhcnRlckxlbiA9IHF1YXJ0ZXJMZW4gPj4+IDE7CgoJCSAgICAvLyBMb29wIHRocm91Z2ggb2Zmc2V0cyBpbiB0aGUgZGF0YQoJCSAgICBmb3IgKG91dE9mZiA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4pIHsKCQkgICAgICBmb3IgKHZhciBpID0gMCwgayA9IDA7IGkgPD0gaHF1YXJ0ZXJMZW47IGkgKz0gMiwgayArPSBzdGVwKSB7CgkJICAgICAgICB2YXIgQSA9IG91dE9mZiArIGk7CgkJICAgICAgICB2YXIgQiA9IEEgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgdmFyIEMgPSBCICsgcXVhcnRlckxlbjsKCQkgICAgICAgIHZhciBEID0gQyArIHF1YXJ0ZXJMZW47CgoJCSAgICAgICAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICAgICAgICB2YXIgQXIgPSBvdXRbQV07CgkJICAgICAgICB2YXIgQWkgPSBvdXRbQSArIDFdOwoJCSAgICAgICAgdmFyIEJyID0gb3V0W0JdOwoJCSAgICAgICAgdmFyIEJpID0gb3V0W0IgKyAxXTsKCQkgICAgICAgIHZhciBDciA9IG91dFtDXTsKCQkgICAgICAgIHZhciBDaSA9IG91dFtDICsgMV07CgkJICAgICAgICB2YXIgRHIgPSBvdXRbRF07CgkJICAgICAgICB2YXIgRGkgPSBvdXRbRCArIDFdOwoKCQkgICAgICAgIC8vIE1pZGRsZSB2YWx1ZXMKCQkgICAgICAgIHZhciBNQXIgPSBBcjsKCQkgICAgICAgIHZhciBNQWkgPSBBaTsKCgkJICAgICAgICB2YXIgdGFibGVCciA9IHRhYmxlW2tdOwoJCSAgICAgICAgdmFyIHRhYmxlQmkgPSBpbnYgKiB0YWJsZVtrICsgMV07CgkJICAgICAgICB2YXIgTUJyID0gQnIgKiB0YWJsZUJyIC0gQmkgKiB0YWJsZUJpOwoJCSAgICAgICAgdmFyIE1CaSA9IEJyICogdGFibGVCaSArIEJpICogdGFibGVCcjsKCgkJICAgICAgICB2YXIgdGFibGVDciA9IHRhYmxlWzIgKiBrXTsKCQkgICAgICAgIHZhciB0YWJsZUNpID0gaW52ICogdGFibGVbMiAqIGsgKyAxXTsKCQkgICAgICAgIHZhciBNQ3IgPSBDciAqIHRhYmxlQ3IgLSBDaSAqIHRhYmxlQ2k7CgkJICAgICAgICB2YXIgTUNpID0gQ3IgKiB0YWJsZUNpICsgQ2kgKiB0YWJsZUNyOwoKCQkgICAgICAgIHZhciB0YWJsZURyID0gdGFibGVbMyAqIGtdOwoJCSAgICAgICAgdmFyIHRhYmxlRGkgPSBpbnYgKiB0YWJsZVszICogayArIDFdOwoJCSAgICAgICAgdmFyIE1EciA9IERyICogdGFibGVEciAtIERpICogdGFibGVEaTsKCQkgICAgICAgIHZhciBNRGkgPSBEciAqIHRhYmxlRGkgKyBEaSAqIHRhYmxlRHI7CgoJCSAgICAgICAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIFQwciA9IE1BciArIE1DcjsKCQkgICAgICAgIHZhciBUMGkgPSBNQWkgKyBNQ2k7CgkJICAgICAgICB2YXIgVDFyID0gTUFyIC0gTUNyOwoJCSAgICAgICAgdmFyIFQxaSA9IE1BaSAtIE1DaTsKCQkgICAgICAgIHZhciBUMnIgPSBNQnIgKyBNRHI7CgkJICAgICAgICB2YXIgVDJpID0gTUJpICsgTURpOwoJCSAgICAgICAgdmFyIFQzciA9IGludiAqIChNQnIgLSBNRHIpOwoJCSAgICAgICAgdmFyIFQzaSA9IGludiAqIChNQmkgLSBNRGkpOwoKCQkgICAgICAgIC8vIEZpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIEZBciA9IFQwciArIFQycjsKCQkgICAgICAgIHZhciBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgICAgICAgdmFyIEZCciA9IFQxciArIFQzaTsKCQkgICAgICAgIHZhciBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgICAgICAgb3V0W0FdID0gRkFyOwoJCSAgICAgICAgb3V0W0EgKyAxXSA9IEZBaTsKCQkgICAgICAgIG91dFtCXSA9IEZCcjsKCQkgICAgICAgIG91dFtCICsgMV0gPSBGQmk7CgoJCSAgICAgICAgLy8gT3V0cHV0IGZpbmFsIG1pZGRsZSBwb2ludAoJCSAgICAgICAgaWYgKGkgPT09IDApIHsKCQkgICAgICAgICAgdmFyIEZDciA9IFQwciAtIFQycjsKCQkgICAgICAgICAgdmFyIEZDaSA9IFQwaSAtIFQyaTsKCQkgICAgICAgICAgb3V0W0NdID0gRkNyOwoJCSAgICAgICAgICBvdXRbQyArIDFdID0gRkNpOwoJCSAgICAgICAgICBjb250aW51ZTsKCQkgICAgICAgIH0KCgkJICAgICAgICAvLyBEbyBub3Qgb3ZlcndyaXRlIG91cnNlbHZlcwoJCSAgICAgICAgaWYgKGkgPT09IGhxdWFydGVyTGVuKQoJCSAgICAgICAgICBjb250aW51ZTsKCgkJICAgICAgICAvLyBJbiB0aGUgZmxpcHBlZCBjYXNlOgoJCSAgICAgICAgLy8gTUFpID0gLU1BaQoJCSAgICAgICAgLy8gTUJyPS1NQmksIE1CaT0tTUJyCgkJICAgICAgICAvLyBNQ3I9LU1DcgoJCSAgICAgICAgLy8gTURyPU1EaSwgTURpPU1EcgoJCSAgICAgICAgdmFyIFNUMHIgPSBUMXI7CgkJICAgICAgICB2YXIgU1QwaSA9IC1UMWk7CgkJICAgICAgICB2YXIgU1QxciA9IFQwcjsKCQkgICAgICAgIHZhciBTVDFpID0gLVQwaTsKCQkgICAgICAgIHZhciBTVDJyID0gLWludiAqIFQzaTsKCQkgICAgICAgIHZhciBTVDJpID0gLWludiAqIFQzcjsKCQkgICAgICAgIHZhciBTVDNyID0gLWludiAqIFQyaTsKCQkgICAgICAgIHZhciBTVDNpID0gLWludiAqIFQycjsKCgkJICAgICAgICB2YXIgU0ZBciA9IFNUMHIgKyBTVDJyOwoJCSAgICAgICAgdmFyIFNGQWkgPSBTVDBpICsgU1QyaTsKCgkJICAgICAgICB2YXIgU0ZCciA9IFNUMXIgKyBTVDNpOwoJCSAgICAgICAgdmFyIFNGQmkgPSBTVDFpIC0gU1QzcjsKCgkJICAgICAgICB2YXIgU0EgPSBvdXRPZmYgKyBxdWFydGVyTGVuIC0gaTsKCQkgICAgICAgIHZhciBTQiA9IG91dE9mZiArIGhhbGZMZW4gLSBpOwoKCQkgICAgICAgIG91dFtTQV0gPSBTRkFyOwoJCSAgICAgICAgb3V0W1NBICsgMV0gPSBTRkFpOwoJCSAgICAgICAgb3V0W1NCXSA9IFNGQnI7CgkJICAgICAgICBvdXRbU0IgKyAxXSA9IFNGQmk7CgkJICAgICAgfQoJCSAgICB9CgkJICB9CgkJfTsKCgkJLy8gcmFkaXgtMiBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj00CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlUmVhbFRyYW5zZm9ybTIgPSBmdW5jdGlvbiBfc2luZ2xlUmVhbFRyYW5zZm9ybTIob3V0T2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCgkJICBjb25zdCBldmVuUiA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IG9kZFIgPSBkYXRhW29mZiArIHN0ZXBdOwoKCQkgIGNvbnN0IGxlZnRSID0gZXZlblIgKyBvZGRSOwoJCSAgY29uc3QgcmlnaHRSID0gZXZlblIgLSBvZGRSOwoKCQkgIG91dFtvdXRPZmZdID0gbGVmdFI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDJdID0gcmlnaHRSOwoJCSAgb3V0W291dE9mZiArIDNdID0gMDsKCQl9OwoKCQkvLyByYWRpeC00CgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTgKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVSZWFsVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9zaW5nbGVSZWFsVHJhbnNmb3JtNChvdXRPZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoJCSAgY29uc3QgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgY29uc3Qgc3RlcDIgPSBzdGVwICogMjsKCQkgIGNvbnN0IHN0ZXAzID0gc3RlcCAqIDM7CgoJCSAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICBjb25zdCBBciA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IEJyID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IENyID0gZGF0YVtvZmYgKyBzdGVwMl07CgkJICBjb25zdCBEciA9IGRhdGFbb2ZmICsgc3RlcDNdOwoKCQkgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IFQwciA9IEFyICsgQ3I7CgkJICBjb25zdCBUMXIgPSBBciAtIENyOwoJCSAgY29uc3QgVDJyID0gQnIgKyBEcjsKCQkgIGNvbnN0IFQzciA9IGludiAqIChCciAtIERyKTsKCgkJICAvLyBGaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCgkJICBjb25zdCBGQnIgPSBUMXI7CgkJICBjb25zdCBGQmkgPSAtVDNyOwoKCQkgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCgkJICBjb25zdCBGRHIgPSBUMXI7CgkJICBjb25zdCBGRGkgPSBUM3I7CgoJCSAgb3V0W291dE9mZl0gPSBGQXI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDJdID0gRkJyOwoJCSAgb3V0W291dE9mZiArIDNdID0gRkJpOwoJCSAgb3V0W291dE9mZiArIDRdID0gRkNyOwoJCSAgb3V0W291dE9mZiArIDVdID0gMDsKCQkgIG91dFtvdXRPZmYgKyA2XSA9IEZEcjsKCQkgIG91dFtvdXRPZmYgKyA3XSA9IEZEaTsKCQl9OwoJCXJldHVybiBmZnQ7Cgl9CgoJdmFyIGZmdEV4cG9ydHMgPSByZXF1aXJlRmZ0KCk7Cgl2YXIgRkZUID0gLypAX19QVVJFX18qL2dldERlZmF1bHRFeHBvcnRGcm9tQ2pzKGZmdEV4cG9ydHMpOwoKCWNvbnN0IFdFQkFVRElPX0JMT0NLX1NJWkUgPSAxMjg7Cgljb25zdCBERUZBVUxUX0JMT0NLX1NJWkUgPSAxMDI0OyAvLyBEZWZhdWx0IGJsb2NrIHNpemUgaWYgbm90IHByb3ZpZGVkIGluIG9wdGlvbnMKCS8qKiBPdmVybGFwLUFkZCBOb2RlICovCgljbGFzcyBPTEFQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewoJICAgIG5iSW5wdXRzOwoJICAgIG5iT3V0cHV0czsKCSAgICBibG9ja1NpemU7CgkgICAgaG9wU2l6ZTsKCSAgICBuYk92ZXJsYXBzOwoJICAgIGlucHV0QnVmZmVycyA9IFtdOwoJICAgIGlucHV0QnVmZmVyc0hlYWQgPSBbXTsKCSAgICBpbnB1dEJ1ZmZlcnNUb1NlbmQgPSBbXTsKCSAgICBvdXRwdXRCdWZmZXJzID0gW107CgkgICAgb3V0cHV0QnVmZmVyc1RvUmV0cmlldmUgPSBbXTsKCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIHN1cGVyKG9wdGlvbnMpOwoJICAgICAgICB0aGlzLm5iSW5wdXRzID0gb3B0aW9ucz8ubnVtYmVyT2ZJbnB1dHMgfHwgMTsKCSAgICAgICAgdGhpcy5uYk91dHB1dHMgPSBvcHRpb25zPy5udW1iZXJPZk91dHB1dHMgfHwgMTsKCSAgICAgICAgLy8gcHJvY2Vzc29yT3B0aW9ucyBpcyB0eXBlZCBgdW5rbm93bmAgYXQgdGhlIGFtYmllbnQgYm91bmRhcnk7IG5hcnJvdyBoZXJlLgoJICAgICAgICBjb25zdCBwcm9jT3B0cyA9IChvcHRpb25zPy5wcm9jZXNzb3JPcHRpb25zID8/IHt9KTsKCSAgICAgICAgdGhpcy5ibG9ja1NpemUgPSBwcm9jT3B0cy5ibG9ja1NpemUgfHwgREVGQVVMVF9CTE9DS19TSVpFOwoJICAgICAgICB0aGlzLmhvcFNpemUgPSBXRUJBVURJT19CTE9DS19TSVpFOwoJICAgICAgICB0aGlzLm5iT3ZlcmxhcHMgPSBNYXRoLmZsb29yKHRoaXMuYmxvY2tTaXplIC8gdGhpcy5ob3BTaXplKTsKCSAgICAgICAgdGhpcy5pbml0aWFsaXplQnVmZmVycygpOwoJICAgIH0KCSAgICBpbml0aWFsaXplQnVmZmVycygpIHsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnMgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZCA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmQgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVycyA9IG5ldyBBcnJheSh0aGlzLm5iT3V0cHV0cyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmUgPSBuZXcgQXJyYXkodGhpcy5uYk91dHB1dHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaSwgMSk7CgkgICAgICAgIH0KCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMoaSwgMSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgYWxsb2NhdGVJbnB1dENoYW5uZWxzKGlucHV0SW5kZXgsIG5iQ2hhbm5lbHMpIHsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJDaGFubmVsczsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUgKyBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldLmZpbGwoMCk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWRbaW5wdXRJbmRleF1baV0gPSB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXS5zdWJhcnJheSgwLCB0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpbnB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB9CgkgICAgfQoJICAgIGFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMob3V0cHV0SW5kZXgsIG5iQ2hhbm5lbHMpIHsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtvdXRwdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJDaGFubmVsczsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdW2ldLmZpbGwoMCk7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW291dHB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB9CgkgICAgfQoJICAgIHJlYWxsb2NhdGVDaGFubmVsc0lmTmVlZGVkKGlucHV0cywgb3V0cHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgY29uc3QgbmJDaGFubmVscyA9IGlucHV0c1tpXS5sZW5ndGg7CgkgICAgICAgICAgICBpZiAobmJDaGFubmVscyAhPT0gdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaSwgbmJDaGFubmVscyk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBuYkNoYW5uZWxzID0gb3V0cHV0c1tpXS5sZW5ndGg7CgkgICAgICAgICAgICBpZiAobmJDaGFubmVscyAhPT0gdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aCkgewoJICAgICAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVPdXRwdXRDaGFubmVscyhpLCBuYkNoYW5uZWxzKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICByZWFkSW5wdXRzKGlucHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGNvbnN0IHdlYkF1ZGlvQmxvY2sgPSBpbnB1dHNbaV1bal07CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaV1bal0uc2V0KHdlYkF1ZGlvQmxvY2ssIHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICB3cml0ZU91dHB1dHMob3V0cHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgY29uc3Qgd2ViQXVkaW9CbG9jayA9IG91dHB1dHNbaV1bal07CgkgICAgICAgICAgICAgICAgd2ViQXVkaW9CbG9jay5zZXQodGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLnN1YmFycmF5KDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBzaGlmdElucHV0QnVmZmVycygpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpXVtqXS5jb3B5V2l0aGluKDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHNoaWZ0T3V0cHV0QnVmZmVycygpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5jb3B5V2l0aGluKDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5maWxsKDAsIHRoaXMuYmxvY2tTaXplIC0gV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcHJlcGFyZUlucHV0QnVmZmVyc1RvU2VuZCgpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpXVtqXS5zZXQodGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2ldW2pdKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBoYW5kbGVPdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSgpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgdGhpcy5ibG9ja1NpemU7IGsrKykgewoJICAgICAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal1ba10gKz0gdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtpXVtqXVtrXSAvIHRoaXMubmJPdmVybGFwczsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKCSAgICAgICAgdGhpcy5yZWFsbG9jYXRlQ2hhbm5lbHNJZk5lZWRlZChpbnB1dHMsIG91dHB1dHMpOwoJICAgICAgICB0aGlzLnJlYWRJbnB1dHMoaW5wdXRzKTsKCSAgICAgICAgdGhpcy5zaGlmdElucHV0QnVmZmVycygpOwoJICAgICAgICB0aGlzLnByZXBhcmVJbnB1dEJ1ZmZlcnNUb1NlbmQoKTsKCSAgICAgICAgdGhpcy5wcm9jZXNzT0xBKHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kLCB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlLCBwYXJhbWV0ZXJzKTsKCSAgICAgICAgdGhpcy5oYW5kbGVPdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSgpOwoJICAgICAgICB0aGlzLndyaXRlT3V0cHV0cyhvdXRwdXRzKTsKCSAgICAgICAgdGhpcy5zaGlmdE91dHB1dEJ1ZmZlcnMoKTsKCSAgICAgICAgcmV0dXJuIHRydWU7CgkgICAgfQoJfQoKCWNvbnN0IHdyYXBQaGFzZSA9IChwaGFzZSkgPT4gTWF0aC5hdGFuMihNYXRoLnNpbihwaGFzZSksIE1hdGguY29zKHBoYXNlKSk7CgkvKiogU3RhdGVmdWwgc3BlY3RyYWwtZnJhbWUgZnJlZXplciB3aXRoIHBoYXNlIGNvbnRpbnVhdGlvbiBhbmQgMy1mcmFtZSBtYWduaXR1ZGUgc21lYXIuICovCgljbGFzcyBTcGVjdHJhbEZyZWV6ZVN0YXRlIHsKCSAgICBiaW5Db3VudDsKCSAgICBmZnRTaXplOwoJICAgIGhvcFNpemU7CgkgICAgcHJldmlvdXM7CgkgICAgaGVsZE1hZ25pdHVkZTsKCSAgICBoZWxkUGhhc2U7CgkgICAgcGhhc2VTdGVwOwoJICAgIG1hZ25pdHVkZUhpc3Rvcnk7CgkgICAgaGlzdG9yeUluZGV4ID0gMDsKCSAgICBoaXN0b3J5Q291bnQgPSAwOwoJICAgIHdhc0Zyb3plbiA9IGZhbHNlOwoJICAgIGhhc1ByZXZpb3VzID0gZmFsc2U7CgkgICAgY29uc3RydWN0b3IoYmluQ291bnQsIGZmdFNpemUsIGhvcFNpemUpIHsKCSAgICAgICAgdGhpcy5iaW5Db3VudCA9IGJpbkNvdW50OwoJICAgICAgICB0aGlzLmZmdFNpemUgPSBmZnRTaXplOwoJICAgICAgICB0aGlzLmhvcFNpemUgPSBob3BTaXplOwoJICAgICAgICB0aGlzLnByZXZpb3VzID0gbmV3IEZsb2F0MzJBcnJheShmZnRTaXplICogMik7CgkgICAgICAgIHRoaXMuaGVsZE1hZ25pdHVkZSA9IG5ldyBGbG9hdDMyQXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLmhlbGRQaGFzZSA9IG5ldyBGbG9hdDY0QXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLnBoYXNlU3RlcCA9IG5ldyBGbG9hdDY0QXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLm1hZ25pdHVkZUhpc3RvcnkgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiAzIH0sICgpID0+IG5ldyBGbG9hdDMyQXJyYXkoYmluQ291bnQpKTsKCSAgICB9CgkgICAgcHJvY2VzcyhpbnB1dCwgb3V0cHV0LCBmcm96ZW4sIHNtZWFyLCBtaXgpIHsKCSAgICAgICAgY29uc3QgY3VycmVudE1hZ25pdHVkZXMgPSB0aGlzLm1hZ25pdHVkZUhpc3RvcnlbdGhpcy5oaXN0b3J5SW5kZXhdOwoJICAgICAgICBmb3IgKGxldCBiaW4gPSAwOyBiaW4gPCB0aGlzLmJpbkNvdW50OyBiaW4rKykgewoJICAgICAgICAgICAgY29uc3QgaSA9IGJpbiAqIDI7CgkgICAgICAgICAgICBjdXJyZW50TWFnbml0dWRlc1tiaW5dID0gTWF0aC5oeXBvdChpbnB1dFtpXSwgaW5wdXRbaSArIDFdKTsKCSAgICAgICAgfQoJICAgICAgICB0aGlzLmhpc3RvcnlJbmRleCA9ICh0aGlzLmhpc3RvcnlJbmRleCArIDEpICUgdGhpcy5tYWduaXR1ZGVIaXN0b3J5Lmxlbmd0aDsKCSAgICAgICAgdGhpcy5oaXN0b3J5Q291bnQgPSBNYXRoLm1pbih0aGlzLm1hZ25pdHVkZUhpc3RvcnkubGVuZ3RoLCB0aGlzLmhpc3RvcnlDb3VudCArIDEpOwoJICAgICAgICBpZiAoZnJvemVuICYmICF0aGlzLndhc0Zyb3plbikgewoJICAgICAgICAgICAgY29uc3Qgc21lYXJBbW91bnQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBzbWVhcikpOwoJICAgICAgICAgICAgZm9yIChsZXQgYmluID0gMDsgYmluIDwgdGhpcy5iaW5Db3VudDsgYmluKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICBsZXQgYXZlcmFnZSA9IDA7CgkgICAgICAgICAgICAgICAgZm9yIChsZXQgaCA9IDA7IGggPCB0aGlzLmhpc3RvcnlDb3VudDsgaCsrKQoJICAgICAgICAgICAgICAgICAgICBhdmVyYWdlICs9IHRoaXMubWFnbml0dWRlSGlzdG9yeVtoXVtiaW5dOwoJICAgICAgICAgICAgICAgIGF2ZXJhZ2UgLz0gdGhpcy5oaXN0b3J5Q291bnQ7CgkgICAgICAgICAgICAgICAgdGhpcy5oZWxkTWFnbml0dWRlW2Jpbl0gPSBjdXJyZW50TWFnbml0dWRlc1tiaW5dICogKDEgLSBzbWVhckFtb3VudCkgKyBhdmVyYWdlICogc21lYXJBbW91bnQ7CgkgICAgICAgICAgICAgICAgdGhpcy5oZWxkUGhhc2VbYmluXSA9IE1hdGguYXRhbjIoaW5wdXRbaSArIDFdLCBpbnB1dFtpXSk7CgkgICAgICAgICAgICAgICAgaWYgKHRoaXMuaGFzUHJldmlvdXMpIHsKCSAgICAgICAgICAgICAgICAgICAgY29uc3QgY3Jvc3NSZWFsID0gaW5wdXRbaV0gKiB0aGlzLnByZXZpb3VzW2ldICsgaW5wdXRbaSArIDFdICogdGhpcy5wcmV2aW91c1tpICsgMV07CgkgICAgICAgICAgICAgICAgICAgIGNvbnN0IGNyb3NzSW1hZyA9IGlucHV0W2kgKyAxXSAqIHRoaXMucHJldmlvdXNbaV0gLSBpbnB1dFtpXSAqIHRoaXMucHJldmlvdXNbaSArIDFdOwoJICAgICAgICAgICAgICAgICAgICB0aGlzLnBoYXNlU3RlcFtiaW5dID0gTWF0aC5hdGFuMihjcm9zc0ltYWcsIGNyb3NzUmVhbCk7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgICAgIGVsc2UgewoJICAgICAgICAgICAgICAgICAgICB0aGlzLnBoYXNlU3RlcFtiaW5dID0gKDIgKiBNYXRoLlBJICogYmluICogdGhpcy5ob3BTaXplKSAvIHRoaXMuZmZ0U2l6ZTsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgY29uc3Qgd2V0ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgbWl4KSk7CgkgICAgICAgIGlmIChmcm96ZW4pIHsKCSAgICAgICAgICAgIGZvciAobGV0IGJpbiA9IDA7IGJpbiA8IHRoaXMuYmluQ291bnQ7IGJpbisrKSB7CgkgICAgICAgICAgICAgICAgaWYgKHRoaXMud2FzRnJvemVuKQoJICAgICAgICAgICAgICAgICAgICB0aGlzLmhlbGRQaGFzZVtiaW5dID0gd3JhcFBoYXNlKHRoaXMuaGVsZFBoYXNlW2Jpbl0gKyB0aGlzLnBoYXNlU3RlcFtiaW5dKTsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICBjb25zdCBoZWxkUmVhbCA9IHRoaXMuaGVsZE1hZ25pdHVkZVtiaW5dICogTWF0aC5jb3ModGhpcy5oZWxkUGhhc2VbYmluXSk7CgkgICAgICAgICAgICAgICAgY29uc3QgaGVsZEltYWcgPSB0aGlzLmhlbGRNYWduaXR1ZGVbYmluXSAqIE1hdGguc2luKHRoaXMuaGVsZFBoYXNlW2Jpbl0pOwoJICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IGlucHV0W2ldICogKDEgLSB3ZXQpICsgaGVsZFJlYWwgKiB3ZXQ7CgkgICAgICAgICAgICAgICAgb3V0cHV0W2kgKyAxXSA9IGlucHV0W2kgKyAxXSAqICgxIC0gd2V0KSArIGhlbGRJbWFnICogd2V0OwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgICAgIGVsc2UgewoJICAgICAgICAgICAgb3V0cHV0LnNldChpbnB1dCk7CgkgICAgICAgIH0KCSAgICAgICAgdGhpcy5wcmV2aW91cy5zZXQoaW5wdXQpOwoJICAgICAgICB0aGlzLmhhc1ByZXZpb3VzID0gdHJ1ZTsKCSAgICAgICAgdGhpcy53YXNGcm96ZW4gPSBmcm96ZW47CgkgICAgfQoJfQoKCWNvbnN0IEJMT0NLX1NJWkUgPSAyMDQ4OwoJY2xhc3MgU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgT0xBUHJvY2Vzc29yIHsKCSAgICBmZnQ7CgkgICAgd2luZG93OwoJICAgIGlucHV0U3BlY3RydW07CgkgICAgb3V0cHV0U3BlY3RydW07CgkgICAgdGltZUNvbXBsZXg7CgkgICAgc3RhdGVzID0gW107CgkgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKCSAgICAgICAgcmV0dXJuIFsKCSAgICAgICAgICAgIHsgbmFtZTogImZyZWV6ZSIsIGRlZmF1bHRWYWx1ZTogMCwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAxLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogInNtZWFyIiwgZGVmYXVsdFZhbHVlOiAwLjUsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCgkgICAgICAgICAgICB7IG5hbWU6ICJtaXgiLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCgkgICAgICAgIF07CgkgICAgfQoJICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKCSAgICAgICAgY29uc3Qgd29ya2xldE9wdGlvbnMgPSBvcHRpb25zID8/IHt9OwoJICAgICAgICB3b3JrbGV0T3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID0geyBibG9ja1NpemU6IEJMT0NLX1NJWkUsIC4uLih3b3JrbGV0T3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID8/IHt9KSB9OwoJICAgICAgICBzdXBlcih3b3JrbGV0T3B0aW9ucyk7CgkgICAgICAgIHRoaXMuZmZ0ID0gbmV3IEZGVCh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIHRoaXMud2luZG93ID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5ibG9ja1NpemU7IGkrKykKCSAgICAgICAgICAgIHRoaXMud2luZG93W2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIHRoaXMuYmxvY2tTaXplKSk7CgkgICAgICAgIHRoaXMuaW5wdXRTcGVjdHJ1bSA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudGltZUNvbXBsZXggPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICB9CgkgICAgcHJvY2Vzc09MQShpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKCSAgICAgICAgY29uc3QgZnJvemVuID0gcGFyYW1ldGVycy5mcmVlemVbcGFyYW1ldGVycy5mcmVlemUubGVuZ3RoIC0gMV0gPj0gMC41OwoJICAgICAgICBjb25zdCBzbWVhciA9IHBhcmFtZXRlcnMuc21lYXJbcGFyYW1ldGVycy5zbWVhci5sZW5ndGggLSAxXTsKCSAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1ldGVycy5taXhbcGFyYW1ldGVycy5taXgubGVuZ3RoIC0gMV07CgkgICAgICAgIGNvbnN0IGJpbnMgPSB0aGlzLmJsb2NrU2l6ZSAvIDIgKyAxOwoJICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IGlucHV0c1swXS5sZW5ndGg7IGNoYW5uZWwrKykgewoJICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF1bY2hhbm5lbF07CgkgICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdW2NoYW5uZWxdOwoJICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dC5sZW5ndGg7IGkrKykKCSAgICAgICAgICAgICAgICBpbnB1dFtpXSAqPSB0aGlzLndpbmRvd1tpXTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LnJlYWxUcmFuc2Zvcm0odGhpcy5pbnB1dFNwZWN0cnVtLCBpbnB1dCk7CgkgICAgICAgICAgICBjb25zdCBzdGF0ZSA9ICh0aGlzLnN0YXRlc1tjaGFubmVsXSA/Pz0gbmV3IFNwZWN0cmFsRnJlZXplU3RhdGUoYmlucywgdGhpcy5ibG9ja1NpemUsIHRoaXMuaG9wU2l6ZSkpOwoJICAgICAgICAgICAgc3RhdGUucHJvY2Vzcyh0aGlzLmlucHV0U3BlY3RydW0sIHRoaXMub3V0cHV0U3BlY3RydW0sIGZyb3plbiwgc21lYXIsIG1peCk7CgkgICAgICAgICAgICB0aGlzLmZmdC5jb21wbGV0ZVNwZWN0cnVtKHRoaXMub3V0cHV0U3BlY3RydW0pOwoJICAgICAgICAgICAgdGhpcy5mZnQuaW52ZXJzZVRyYW5zZm9ybSh0aGlzLnRpbWVDb21wbGV4LCB0aGlzLm91dHB1dFNwZWN0cnVtKTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LmZyb21Db21wbGV4QXJyYXkodGhpcy50aW1lQ29tcGxleCwgb3V0cHV0KTsKCSAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb3V0cHV0Lmxlbmd0aDsgaSsrKQoJICAgICAgICAgICAgICAgIG91dHB1dFtpXSAqPSB0aGlzLndpbmRvd1tpXTsKCSAgICAgICAgfQoJICAgIH0KCX0KCXJlZ2lzdGVyUHJvY2Vzc29yKCJzcGVjdHJhbC1mcmVlemUiLCBTcGVjdHJhbEZyZWV6ZVdvcmtsZXRQcm9jZXNzb3IpOwoKCWV4cG9ydHMuU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yID0gU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yOwoKCXJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zcGVjdHJhbC1mcmVlemUtYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHN0ZXJlb1RvQmZvcm1hdCA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNvbnN0IEJVVFRFUldPUlRIX1EgPSBNYXRoLlNRUlQxXzI7CiAgICBjb25zdCBMT1dfQ1VUT0ZGX0haID0gMjUwOwogICAgY29uc3QgSElHSF9DVVRPRkZfSFogPSAyMDAwOwogICAgLy8gVGllciAxIHBlcmNlcHR1YWwgdHVuaW5nOgogICAgLy8gLSBLZWVwIGxvdy1iYW5kIGVuZXJneSBtb3N0bHkgb21uaWRpcmVjdGlvbmFsLgogICAgLy8gLSBSZWR1Y2UgVyByZWxhdGl2ZSB0byB0aGUgb3JpZ2luYWwgc3FydCgxLzIpIG1hcHBpbmcuCiAgICAvLyAtIExldCB1cHBlciBiYW5kcyBjYXJyeSBtb3JlIG9mIHRoZSBob3Jpem9udGFsIGN1ZS4KICAgIGNvbnN0IFdfR0FJTl9MT1cgPSAwLjU7CiAgICBjb25zdCBXX0dBSU5fTUlEID0gMC40NTsKICAgIGNvbnN0IFdfR0FJTl9ISUdIID0gMC4zNTsKICAgIGNvbnN0IFhfR0FJTl9MT1cgPSAwOwogICAgY29uc3QgWF9HQUlOX01JRCA9IDAuMzU7CiAgICBjb25zdCBYX0dBSU5fSElHSCA9IDAuNTU7CiAgICBjb25zdCBZX0dBSU5fTE9XID0gMDsKICAgIGNvbnN0IFlfR0FJTl9NSUQgPSAwLjU7CiAgICBjb25zdCBZX0dBSU5fSElHSCA9IDAuODU7CiAgICBjb25zdCBTSURFX0RPTUlOQU5DRV9FUFNJTE9OID0gMWUtOTsKICAgIC8vIENvaGVyZW5jZSBzbW9vdGhpbmcuIEV4cG9uZW50aWFsIGZpcnN0LW9yZGVyIGxvdy1wYXNzIG9uIHBlci1iYW5kIHBvd2VyCiAgICAvLyBhbmQgY3Jvc3MtcG93ZXIuIEFscGhhIGNsb3NlciB0byAxIG1lYW5zIHNsb3dlciB0cmFja2luZzsgdGhpcyB2YWx1ZSBnaXZlcwogICAgLy8gYSB0aW1lIGNvbnN0YW50IG9mIGFib3V0IDIwIG1zIGF0IDQ4IGtIeiwgd2hpY2ggbGluZXMgdXAgd2l0aCBCQ0MtcGFwZXIKICAgIC8vIHJlY29tbWVuZGF0aW9ucyBmb3IgcGVyY2VwdHVhbC1jdWUgc21vb3RoaW5nLgogICAgY29uc3QgQ09IRVJFTkNFX1NNT09USElOR19BTFBIQSA9IDAuOTk5OwogICAgLy8gTnVtZXJpY2FsIGZsb29yIG9uIHBlci1iYW5kIHBvd2VyIHNvIHRoZSBJQ0MgZGVub21pbmF0b3IgbmV2ZXIgYmxvd3MgdXAKICAgIC8vIGR1cmluZyBzaWxlbmNlLgogICAgY29uc3QgQ09IRVJFTkNFX1BPV0VSX0VQU0lMT04gPSAxZS0xMjsKICAgIGNsYXNzIEJpcXVhZEZpbHRlciB7CiAgICAgICAgYjAgPSAwOwogICAgICAgIGIxID0gMDsKICAgICAgICBiMiA9IDA7CiAgICAgICAgYTEgPSAwOwogICAgICAgIGEyID0gMDsKICAgICAgICB4MSA9IDA7CiAgICAgICAgeDIgPSAwOwogICAgICAgIHkxID0gMDsKICAgICAgICB5MiA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodHlwZSwgc2FtcGxlUmF0ZSwgY3V0b2ZmSHosIHEgPSBCVVRURVJXT1JUSF9RKSB7CiAgICAgICAgICAgIHRoaXMuY29uZmlndXJlKHR5cGUsIHNhbXBsZVJhdGUsIGN1dG9mZkh6LCBxKTsKICAgICAgICB9CiAgICAgICAgY29uZmlndXJlKHR5cGUsIHNhbXBsZVJhdGUsIGN1dG9mZkh6LCBxKSB7CiAgICAgICAgICAgIGNvbnN0IGNsYW1wZWRDdXRvZmYgPSBNYXRoLm1heCgxLCBNYXRoLm1pbihjdXRvZmZIeiwgc2FtcGxlUmF0ZSAqIDAuNDUpKTsKICAgICAgICAgICAgY29uc3Qgb21lZ2EgPSAoMiAqIE1hdGguUEkgKiBjbGFtcGVkQ3V0b2ZmKSAvIHNhbXBsZVJhdGU7CiAgICAgICAgICAgIGNvbnN0IHNpbiA9IE1hdGguc2luKG9tZWdhKTsKICAgICAgICAgICAgY29uc3QgY29zID0gTWF0aC5jb3Mob21lZ2EpOwogICAgICAgICAgICBjb25zdCBhbHBoYSA9IHNpbiAvICgyICogcSk7CiAgICAgICAgICAgIGxldCBiMDsKICAgICAgICAgICAgbGV0IGIxOwogICAgICAgICAgICBsZXQgYjI7CiAgICAgICAgICAgIGlmICh0eXBlID09PSAibG93cGFzcyIpIHsKICAgICAgICAgICAgICAgIGIwID0gKDEgLSBjb3MpIC8gMjsKICAgICAgICAgICAgICAgIGIxID0gMSAtIGNvczsKICAgICAgICAgICAgICAgIGIyID0gKDEgLSBjb3MpIC8gMjsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIGIwID0gKDEgKyBjb3MpIC8gMjsKICAgICAgICAgICAgICAgIGIxID0gLSgxICsgY29zKTsKICAgICAgICAgICAgICAgIGIyID0gKDEgKyBjb3MpIC8gMjsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBhMCA9IDEgKyBhbHBoYTsKICAgICAgICAgICAgY29uc3QgYTEgPSAtMiAqIGNvczsKICAgICAgICAgICAgY29uc3QgYTIgPSAxIC0gYWxwaGE7CiAgICAgICAgICAgIHRoaXMuYjAgPSBiMCAvIGEwOwogICAgICAgICAgICB0aGlzLmIxID0gYjEgLyBhMDsKICAgICAgICAgICAgdGhpcy5iMiA9IGIyIC8gYTA7CiAgICAgICAgICAgIHRoaXMuYTEgPSBhMSAvIGEwOwogICAgICAgICAgICB0aGlzLmEyID0gYTIgLyBhMDsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSB0aGlzLmIwICogaW5wdXQgKyB0aGlzLmIxICogdGhpcy54MSArIHRoaXMuYjIgKiB0aGlzLngyIC0gdGhpcy5hMSAqIHRoaXMueTEgLSB0aGlzLmEyICogdGhpcy55MjsKICAgICAgICAgICAgdGhpcy54MiA9IHRoaXMueDE7CiAgICAgICAgICAgIHRoaXMueDEgPSBpbnB1dDsKICAgICAgICAgICAgdGhpcy55MiA9IHRoaXMueTE7CiAgICAgICAgICAgIHRoaXMueTEgPSBvdXRwdXQ7CiAgICAgICAgICAgIHJldHVybiBvdXRwdXQ7CiAgICAgICAgfQogICAgfQogICAgLyoqCiAgICAgKiBSdW5uaW5nIHBlci1iYW5kIGludGVyLWNoYW5uZWwgY29oZXJlbmNlIGVzdGltYXRvci4KICAgICAqCiAgICAgKiBNYWludGFpbnMgZXhwb25lbnRpYWxseSBzbW9vdGhlZCBlc3RpbWF0ZXMgb2YgTCpMLCBSKlIsIGFuZCBMKlIsIHRoZW4KICAgICAqIGRlcml2ZXMgYSBub3JtYWxpemVkIGNvaGVyZW5jZSB2YWx1ZSBgfDxMUj58IC8gc3FydCg8TMKyPiAqIDxSwrI+KWAgaW4KICAgICAqIHRoZSByYW5nZSBbMCwgMV0uIEhpZ2ggY29oZXJlbmNlIGluZGljYXRlcyBhIHN0YWJsZSBpbi1waGFzZSAvIHBhbm5lZAogICAgICogc291cmNlOyBsb3cgY29oZXJlbmNlIGluZGljYXRlcyBkZWNvcnJlbGF0ZWQgZGlmZnVzZSBjb250ZW50IGxpa2Ugcm9vbQogICAgICogcmV2ZXJiLgogICAgICoKICAgICAqIFRoZSBzaWduIG9mIDxMUj4gaXMgcHJlc2VydmVkIGludGVybmFsbHkgc28gY2FsbGVycyBjYW4gZGlmZmVyZW50aWF0ZQogICAgICogcG9zaXRpdmUgKGluLXBoYXNlIC8gY2VudGVyKSBmcm9tIG5lZ2F0aXZlIChhbnRpLXBoYXNlKSBjb3JyZWxhdGlvbiwKICAgICAqIGJ1dCB0aGUgcHVibGljIGBjb2hlcmVuY2UoKWAgaGVscGVyIHJldHVybnMgbWFnbml0dWRlIG9ubHkuCiAgICAgKi8KICAgIGNsYXNzIENyb3NzQ29ycmVsYXRvciB7CiAgICAgICAgc21vb3RoZWRMTCA9IDA7CiAgICAgICAgc21vb3RoZWRSUiA9IDA7CiAgICAgICAgc21vb3RoZWRMUiA9IDA7CiAgICAgICAgYWxwaGE7CiAgICAgICAgb25lTWludXNBbHBoYTsKICAgICAgICBjb25zdHJ1Y3RvcihhbHBoYSA9IENPSEVSRU5DRV9TTU9PVEhJTkdfQUxQSEEpIHsKICAgICAgICAgICAgdGhpcy5hbHBoYSA9IGFscGhhOwogICAgICAgICAgICB0aGlzLm9uZU1pbnVzQWxwaGEgPSAxIC0gYWxwaGE7CiAgICAgICAgfQogICAgICAgIHVwZGF0ZShsZWZ0LCByaWdodCkgewogICAgICAgICAgICB0aGlzLnNtb290aGVkTEwgPSB0aGlzLmFscGhhICogdGhpcy5zbW9vdGhlZExMICsgdGhpcy5vbmVNaW51c0FscGhhICogbGVmdCAqIGxlZnQ7CiAgICAgICAgICAgIHRoaXMuc21vb3RoZWRSUiA9IHRoaXMuYWxwaGEgKiB0aGlzLnNtb290aGVkUlIgKyB0aGlzLm9uZU1pbnVzQWxwaGEgKiByaWdodCAqIHJpZ2h0OwogICAgICAgICAgICB0aGlzLnNtb290aGVkTFIgPSB0aGlzLmFscGhhICogdGhpcy5zbW9vdGhlZExSICsgdGhpcy5vbmVNaW51c0FscGhhICogbGVmdCAqIHJpZ2h0OwogICAgICAgIH0KICAgICAgICAvKiogTWFnbml0dWRlIG9mIG5vcm1hbGl6ZWQgY3Jvc3MtY29ycmVsYXRpb24gaW4gWzAsIDFdLiAqLwogICAgICAgIGNvaGVyZW5jZSgpIHsKICAgICAgICAgICAgY29uc3QgZGVub20gPSBNYXRoLnNxcnQodGhpcy5zbW9vdGhlZExMICogdGhpcy5zbW9vdGhlZFJSKSArIENPSEVSRU5DRV9QT1dFUl9FUFNJTE9OOwogICAgICAgICAgICBjb25zdCBjID0gTWF0aC5hYnModGhpcy5zbW9vdGhlZExSKSAvIGRlbm9tOwogICAgICAgICAgICByZXR1cm4gYyA+IDEgPyAxIDogYzsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogU2lnbmVkIG5vcm1hbGl6ZWQgY3Jvc3MtY29ycmVsYXRpb24gaW4gWy0xLCArMV0uCiAgICAgICAgICoKICAgICAgICAgKiAtICsxOiBwZXJmZWN0bHkgaW4tcGhhc2UgKEwgYW5kIFIgbW92ZSB0b2dldGhlciwgbW9ubyAvIGNlbnRlciBwYW4pCiAgICAgICAgICogLSAwOiB1bmNvcnJlbGF0ZWQgKGRpZmZ1c2UgYW1iaWVudCwgaGFyZC1wYW5uZWQgc2luZ2xlIHNvdXJjZSkKICAgICAgICAgKiAtIC0xOiBwZXJmZWN0bHkgYW50aS1waGFzZSAoTCBhbmQgUiBtb3ZlIG9wcG9zaXRlbHksIHN0ZXJlbyB3aWR0aAogICAgICAgICAqICAgdHJpY2tzIG9yIG91dC1vZi1waGFzZSBjb250ZW50KQogICAgICAgICAqCiAgICAgICAgICogRnJvbnQvYmFjayByb3V0aW5nIHdhbnRzIHBvc2l0aXZlIGNvcnJlbGF0aW9uIChyZWFsIGNlbnRlciBtYXRlcmlhbCkuCiAgICAgICAgICogTGVmdC9yaWdodCByb3V0aW5nIHdhbnRzIHdoYXRldmVyIGlzbid0IGluLXBoYXNlIChhbnl0aGluZyB0aGF0IGhhcwogICAgICAgICAqIGEgc3RhYmxlIGRpZmZlcmVuY2UgYmV0d2VlbiBjaGFubmVscywgaW5jbHVkaW5nIGhhcmQgcGFucykuCiAgICAgICAgICovCiAgICAgICAgc2lnbmVkQ29oZXJlbmNlKCkgewogICAgICAgICAgICBjb25zdCBkZW5vbSA9IE1hdGguc3FydCh0aGlzLnNtb290aGVkTEwgKiB0aGlzLnNtb290aGVkUlIpICsgQ09IRVJFTkNFX1BPV0VSX0VQU0lMT047CiAgICAgICAgICAgIGNvbnN0IGMgPSB0aGlzLnNtb290aGVkTFIgLyBkZW5vbTsKICAgICAgICAgICAgaWYgKGMgPiAxKQogICAgICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgICAgIGlmIChjIDwgLTEpCiAgICAgICAgICAgICAgICByZXR1cm4gLTE7CiAgICAgICAgICAgIHJldHVybiBjOwogICAgICAgIH0KICAgIH0KICAgIGNsYXNzIFN0ZXJlb1RvRm9hVXBtaXhlciB7CiAgICAgICAgbG93TGVmdDsKICAgICAgICBsb3dSaWdodDsKICAgICAgICBoaWdoTGVmdDsKICAgICAgICBoaWdoUmlnaHQ7CiAgICAgICAgbG93Q29oZXJlbmNlOwogICAgICAgIG1pZENvaGVyZW5jZTsKICAgICAgICBoaWdoQ29oZXJlbmNlOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5sb3dMZWZ0ID0gbmV3IEJpcXVhZEZpbHRlcigibG93cGFzcyIsIHNhbXBsZVJhdGUsIExPV19DVVRPRkZfSFopOwogICAgICAgICAgICB0aGlzLmxvd1JpZ2h0ID0gbmV3IEJpcXVhZEZpbHRlcigibG93cGFzcyIsIHNhbXBsZVJhdGUsIExPV19DVVRPRkZfSFopOwogICAgICAgICAgICB0aGlzLmhpZ2hMZWZ0ID0gbmV3IEJpcXVhZEZpbHRlcigiaGlnaHBhc3MiLCBzYW1wbGVSYXRlLCBISUdIX0NVVE9GRl9IWik7CiAgICAgICAgICAgIHRoaXMuaGlnaFJpZ2h0ID0gbmV3IEJpcXVhZEZpbHRlcigiaGlnaHBhc3MiLCBzYW1wbGVSYXRlLCBISUdIX0NVVE9GRl9IWik7CiAgICAgICAgICAgIHRoaXMubG93Q29oZXJlbmNlID0gbmV3IENyb3NzQ29ycmVsYXRvcigpOwogICAgICAgICAgICB0aGlzLm1pZENvaGVyZW5jZSA9IG5ldyBDcm9zc0NvcnJlbGF0b3IoKTsKICAgICAgICAgICAgdGhpcy5oaWdoQ29oZXJlbmNlID0gbmV3IENyb3NzQ29ycmVsYXRvcigpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGxlZnQsIHJpZ2h0LCB3LCB5LCB6LCB4KSB7CiAgICAgICAgICAgIGNvbnN0IGZyYW1lQ291bnQgPSBNYXRoLm1pbihsZWZ0Lmxlbmd0aCwgcmlnaHQubGVuZ3RoLCB3Lmxlbmd0aCwgeC5sZW5ndGgsIHkubGVuZ3RoLCB6Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGZyYW1lID0gMDsgZnJhbWUgPCBmcmFtZUNvdW50OyBmcmFtZSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBsZWZ0U2FtcGxlID0gbGVmdFtmcmFtZV07CiAgICAgICAgICAgICAgICBjb25zdCByaWdodFNhbXBsZSA9IHJpZ2h0W2ZyYW1lXTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd0xlZnQgPSB0aGlzLmxvd0xlZnQucHJvY2VzcyhsZWZ0U2FtcGxlKTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd1JpZ2h0ID0gdGhpcy5sb3dSaWdodC5wcm9jZXNzKHJpZ2h0U2FtcGxlKTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hMZWZ0ID0gdGhpcy5oaWdoTGVmdC5wcm9jZXNzKGxlZnRTYW1wbGUpOwogICAgICAgICAgICAgICAgY29uc3QgaGlnaFJpZ2h0ID0gdGhpcy5oaWdoUmlnaHQucHJvY2VzcyhyaWdodFNhbXBsZSk7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRMZWZ0ID0gbGVmdFNhbXBsZSAtIGxvd0xlZnQgLSBoaWdoTGVmdDsKICAgICAgICAgICAgICAgIGNvbnN0IG1pZFJpZ2h0ID0gcmlnaHRTYW1wbGUgLSBsb3dSaWdodCAtIGhpZ2hSaWdodDsKICAgICAgICAgICAgICAgIHRoaXMubG93Q29oZXJlbmNlLnVwZGF0ZShsb3dMZWZ0LCBsb3dSaWdodCk7CiAgICAgICAgICAgICAgICB0aGlzLm1pZENvaGVyZW5jZS51cGRhdGUobWlkTGVmdCwgbWlkUmlnaHQpOwogICAgICAgICAgICAgICAgdGhpcy5oaWdoQ29oZXJlbmNlLnVwZGF0ZShoaWdoTGVmdCwgaGlnaFJpZ2h0KTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd01pZCA9IChsb3dMZWZ0ICsgbG93UmlnaHQpICogMC41OwogICAgICAgICAgICAgICAgY29uc3QgbG93U2lkZSA9IChsb3dMZWZ0IC0gbG93UmlnaHQpICogMC41OwogICAgICAgICAgICAgICAgY29uc3QgbWlkTWlkID0gKG1pZExlZnQgKyBtaWRSaWdodCkgKiAwLjU7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRTaWRlID0gKG1pZExlZnQgLSBtaWRSaWdodCkgKiAwLjU7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoTWlkID0gKGhpZ2hMZWZ0ICsgaGlnaFJpZ2h0KSAqIDAuNTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hTaWRlID0gKGhpZ2hMZWZ0IC0gaGlnaFJpZ2h0KSAqIDAuNTsKICAgICAgICAgICAgICAgIC8vIENvaGVyZW5jZSBnYXRpbmcgdXNlcyBzaWduZWQgY3Jvc3MtY29ycmVsYXRpb24gcGVyIGJhbmQ6CiAgICAgICAgICAgICAgICAvLyAgIM+BIOKJiCArMTogaW4tcGhhc2UgLyBjZW50ZXIgbWF0ZXJpYWwg4oaSIGZyb250YWwgWCBjdWUsIG5vIFkKICAgICAgICAgICAgICAgIC8vICAgz4Eg4omIICAwOiBoYXJkLXBhbm5lZCBzaW5nbGUgc291cmNlIG9yIGRpZmZ1c2UgYW1iaWVudCDihpIgWSBvcGVuLCBYIHN1cHByZXNzZWQKICAgICAgICAgICAgICAgIC8vICAgz4Eg4omIIC0xOiBhbnRpLXBoYXNlIC8gaW52ZXJ0ZWQtcGFpciBzdGVyZW8gdHJpY2tlcnkg4oaSIFkgb3BlbiwgWCBzdXBwcmVzc2VkCiAgICAgICAgICAgICAgICAvLwogICAgICAgICAgICAgICAgLy8gWCBnYWluIGZhY3RvciA9IG1heCgwLCDPgSkuIE9ubHkgcG9zaXRpdmUgaW4tcGhhc2UgY29udGVudCBzdGVlcnMKICAgICAgICAgICAgICAgIC8vIGZvcndhcmQ7IGRlY29ycmVsYXRlZCBhbmQgYW50aS1waGFzZSBjb250ZW50IHN0YXlzIG91dCBvZiBYLgogICAgICAgICAgICAgICAgLy8KICAgICAgICAgICAgICAgIC8vIFkgZ2FpbiBmYWN0b3IgPSAxIC0gbWF4KDAsIM+BKS4gQ2VudGVyIG1hdGVyaWFsIChoaWdoICvPgSkKICAgICAgICAgICAgICAgIC8vIHN1cHByZXNzZXMgWTsgZXZlcnl0aGluZyBlbHNlIChoYXJkIHBhbnMsIGFudGktcGhhc2UsIGFtYmllbnQpCiAgICAgICAgICAgICAgICAvLyBrZWVwcyB0aGUgc2lkZSBjdWUuCiAgICAgICAgICAgICAgICAvLwogICAgICAgICAgICAgICAgLy8gSGlnaC1mcmVxdWVuY3kgdm9jYWxzIHdlcmUgc3RpbGwgcGVlbGluZyBzaWRld2F5cyBiZWNhdXNlICJub3QKICAgICAgICAgICAgICAgIC8vIHBlcmZlY3RseSBjZW50ZXJlZCIgd2FzIGVub3VnaCB0byBrZWVwIGEgbG90IG9mIHRyZWJsZSBpbiBZLgogICAgICAgICAgICAgICAgLy8gTWFrZSB0aGUgaGlnaCBiYW5kIG1vcmUgc2VsZWN0aXZlOiBvbmx5IHN0ZWVyIGhhcmQgc2lkZXdheXMgd2hlbgogICAgICAgICAgICAgICAgLy8gdGhlIGJhbmQgaXMgYm90aCBub24tY2VudGVyZWQgYW5kIGFjdHVhbGx5IHNpZGUtZG9taW5hbnQuCiAgICAgICAgICAgICAgICBjb25zdCBsb3dSaG8gPSBNYXRoLm1heCgwLCB0aGlzLmxvd0NvaGVyZW5jZS5zaWduZWRDb2hlcmVuY2UoKSk7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRSaG8gPSBNYXRoLm1heCgwLCB0aGlzLm1pZENvaGVyZW5jZS5zaWduZWRDb2hlcmVuY2UoKSk7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoUmhvID0gTWF0aC5tYXgoMCwgdGhpcy5oaWdoQ29oZXJlbmNlLnNpZ25lZENvaGVyZW5jZSgpKTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hTaWRlRG9taW5hbmNlID0gTWF0aC5hYnMoaGlnaFNpZGUpIC8gKE1hdGguYWJzKGhpZ2hNaWQpICsgTWF0aC5hYnMoaGlnaFNpZGUpICsgU0lERV9ET01JTkFOQ0VfRVBTSUxPTik7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoWVdlaWdodCA9ICgxIC0gaGlnaFJobykgKiBoaWdoU2lkZURvbWluYW5jZSAqIGhpZ2hTaWRlRG9taW5hbmNlOwogICAgICAgICAgICAgICAgd1tmcmFtZV0gPSBsb3dNaWQgKiBXX0dBSU5fTE9XICsgbWlkTWlkICogV19HQUlOX01JRCArIGhpZ2hNaWQgKiBXX0dBSU5fSElHSDsKICAgICAgICAgICAgICAgIHlbZnJhbWVdID0KICAgICAgICAgICAgICAgICAgICBsb3dTaWRlICogWV9HQUlOX0xPVyAqICgxIC0gbG93UmhvKSArCiAgICAgICAgICAgICAgICAgICAgICAgIG1pZFNpZGUgKiBZX0dBSU5fTUlEICogKDEgLSBtaWRSaG8pICsKICAgICAgICAgICAgICAgICAgICAgICAgaGlnaFNpZGUgKiBZX0dBSU5fSElHSCAqIGhpZ2hZV2VpZ2h0OwogICAgICAgICAgICAgICAgeltmcmFtZV0gPSAwOwogICAgICAgICAgICAgICAgeFtmcmFtZV0gPSBsb3dNaWQgKiBYX0dBSU5fTE9XICogbG93UmhvICsgbWlkTWlkICogWF9HQUlOX01JRCAqIG1pZFJobyArIGhpZ2hNaWQgKiBYX0dBSU5fSElHSCAqIGhpZ2hSaG87CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpzdGVyZW8tdG8tYmZvcm1hdF0iOwogICAgY2xhc3MgU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBmcmFtZXNQcm9jZXNzZWQgPSAwOwogICAgICAgIGZyYW1lc1Nob3J0SW5wdXQgPSAwOwogICAgICAgIGxhc3RSZXBvcnRGcmFtZSA9IDA7CiAgICAgICAgdXBtaXhlciA9IG5ldyBTdGVyZW9Ub0ZvYVVwbWl4ZXIoc2FtcGxlUmF0ZSk7CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIF9wYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA8IDIgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoIDwgNCkgewogICAgICAgICAgICAgICAgdGhpcy5mcmFtZXNTaG9ydElucHV0Kys7CiAgICAgICAgICAgICAgICB0aGlzLmZyYW1lc1Byb2Nlc3NlZCsrOwogICAgICAgICAgICAgICAgdGhpcy5tYXliZVJlcG9ydChpbnB1dD8ubGVuZ3RoID8/IDAsICJzaG9ydC1pbnB1dCIsIGlucHV0LCBvdXRwdXQpOwogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy51cG1peGVyLnByb2Nlc3MoaW5wdXRbMF0sIGlucHV0WzFdLCBvdXRwdXRbMF0sIG91dHB1dFsxXSwgb3V0cHV0WzJdLCBvdXRwdXRbM10pOwogICAgICAgICAgICB0aGlzLmZyYW1lc1Byb2Nlc3NlZCsrOwogICAgICAgICAgICB0aGlzLm1heWJlUmVwb3J0KGlucHV0Lmxlbmd0aCwgIm9rIiwgaW5wdXQsIG91dHB1dCk7CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgICAgICBtYXliZVJlcG9ydChjaGFubmVscywga2luZCwgaW5wdXQsIG91dHB1dCkgewogICAgICAgICAgICBpZiAodGhpcy5mcmFtZXNQcm9jZXNzZWQgLSB0aGlzLmxhc3RSZXBvcnRGcmFtZSA8IDc1MCkKICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgdGhpcy5sYXN0UmVwb3J0RnJhbWUgPSB0aGlzLmZyYW1lc1Byb2Nlc3NlZDsKICAgICAgICAgICAgbGV0IHN0YXRzID0gIiI7CiAgICAgICAgICAgIGlmIChpbnB1dCAmJiBpbnB1dC5sZW5ndGggPj0gMiAmJiBvdXRwdXQgJiYgb3V0cHV0Lmxlbmd0aCA+PSA0KSB7CiAgICAgICAgICAgICAgICBjb25zdCBwZWFrID0gKGEpID0+IHsKICAgICAgICAgICAgICAgICAgICBsZXQgbSA9IDA7CiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHYgPSBNYXRoLmFicyhhW2ldKTsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYgPiBtKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbSA9IHY7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiBtOwogICAgICAgICAgICAgICAgfTsKICAgICAgICAgICAgICAgIHN0YXRzID0KICAgICAgICAgICAgICAgICAgICBgIHBlYWtMPSR7cGVhayhpbnB1dFswXSkudG9GaXhlZCg0KX1gICsKICAgICAgICAgICAgICAgICAgICAgICAgYCBwZWFrUj0ke3BlYWsoaW5wdXRbMV0pLnRvRml4ZWQoNCl9YCArCiAgICAgICAgICAgICAgICAgICAgICAgIGAgcGVha1c9JHtwZWFrKG91dHB1dFswXSkudG9GaXhlZCg0KX1gICsKICAgICAgICAgICAgICAgICAgICAgICAgYCBwZWFrWT0ke3BlYWsob3V0cHV0WzFdKS50b0ZpeGVkKDQpfWAgKwogICAgICAgICAgICAgICAgICAgICAgICBgIHBlYWtaPSR7cGVhayhvdXRwdXRbMl0pLnRvRml4ZWQoNCl9YCArCiAgICAgICAgICAgICAgICAgICAgICAgIGAgcGVha1g9JHtwZWFrKG91dHB1dFszXSkudG9GaXhlZCg0KX1gOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHByb2Nlc3MgdGljayBmcmFtZXNUb3RhbD0ke3RoaXMuZnJhbWVzUHJvY2Vzc2VkfSBzaG9ydElucHV0PSR7dGhpcy5mcmFtZXNTaG9ydElucHV0fSBjaGFubmVsc1RoaXNUaWNrPSR7Y2hhbm5lbHN9IGtpbmQ9JHtraW5kfSR7c3RhdHN9YCk7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3Igc3RhcnRgKTsKICAgICAgICByZWdpc3RlclByb2Nlc3Nvcigic3RlcmVvLXRvLWJmb3JtYXQiLCBTdGVyZW9Ub0JGb3JtYXRQcm9jZXNzb3IpOwogICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGNvbXBsZXRlYCk7CiAgICB9CiAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICBjb25zb2xlLmVycm9yKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgZmFpbGVkYCwgZXJyb3IpOwogICAgICAgIHRocm93IGVycm9yOwogICAgfQoKICAgIGV4cG9ydHMuU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yID0gU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zdGVyZW8tdG8tYmZvcm1hdC1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHN0ZXJlb1dpZGVuZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICBmdW5jdGlvbiBzZWVkZWRSYW5kb20oc2VlZCkgewogICAgICAgIGxldCBzdGF0ZSA9IHNlZWQgPj4+IDA7CiAgICAgICAgcmV0dXJuICgpID0+IHsKICAgICAgICAgICAgc3RhdGUgPSAoMTY2NDUyNSAqIHN0YXRlICsgMTAxMzkwNDIyMykgPj4+IDA7CiAgICAgICAgICAgIHJldHVybiBzdGF0ZSAvIDB4MV8wMDAwXzAwMDA7CiAgICAgICAgfTsKICAgIH0KICAgIC8qKiBFeHBvbmVudGlhbGx5IGRlY2F5aW5nIHNwYXJzZSB2ZWx2ZXQtbm9pc2UgRklSIChTY2hsZWNodCBldCBhbC4sIERBRngtMTgpLiAqLwogICAgZnVuY3Rpb24gYnVpbGREZWNvcnJlbGF0b3JUYXBzKHNhbXBsZVJhdGUsIHNlZWQpIHsKICAgICAgICBjb25zdCByYW5kb20gPSBzZWVkZWRSYW5kb20oc2VlZCk7CiAgICAgICAgY29uc3QgbGVuZ3RoID0gTWF0aC5tYXgoMzIsIE1hdGgucm91bmQoc2FtcGxlUmF0ZSAqIDAuMDIpKTsKICAgICAgICBjb25zdCBzcGFjaW5nID0gTWF0aC5tYXgoMywgTWF0aC5yb3VuZChzYW1wbGVSYXRlIC8gMjQwMCkpOwogICAgICAgIGNvbnN0IHRhcHMgPSBbXTsKICAgICAgICBsZXQgZW5lcmd5ID0gMDsKICAgICAgICBmb3IgKGxldCBzdGFydCA9IHNwYWNpbmc7IHN0YXJ0IDwgbGVuZ3RoOyBzdGFydCArPSBzcGFjaW5nKSB7CiAgICAgICAgICAgIGNvbnN0IGRlbGF5ID0gTWF0aC5taW4obGVuZ3RoIC0gMSwgc3RhcnQgKyBNYXRoLmZsb29yKHJhbmRvbSgpICogc3BhY2luZykpOwogICAgICAgICAgICBjb25zdCBzaWduID0gcmFuZG9tKCkgPCAwLjUgPyAtMSA6IDE7CiAgICAgICAgICAgIGNvbnN0IGdhaW4gPSBzaWduICogTWF0aC5leHAoKC0zICogZGVsYXkpIC8gbGVuZ3RoKTsKICAgICAgICAgICAgdGFwcy5wdXNoKHsgZGVsYXksIGdhaW4gfSk7CiAgICAgICAgICAgIGVuZXJneSArPSBnYWluICogZ2FpbjsKICAgICAgICB9CiAgICAgICAgY29uc3Qgbm9ybSA9IGVuZXJneSA+IDAgPyAxIC8gTWF0aC5zcXJ0KGVuZXJneSkgOiAxOwogICAgICAgIGZvciAoY29uc3QgdGFwIG9mIHRhcHMpCiAgICAgICAgICAgIHRhcC5nYWluICo9IG5vcm07CiAgICAgICAgcmV0dXJuIHRhcHM7CiAgICB9CiAgICBjbGFzcyBTcGFyc2VGaXIgewogICAgICAgIHRhcHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGUgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHRhcHMpIHsKICAgICAgICAgICAgdGhpcy50YXBzID0gdGFwczsKICAgICAgICAgICAgY29uc3QgbWF4RGVsYXkgPSB0YXBzLnJlZHVjZSgobWF4LCB0YXApID0+IE1hdGgubWF4KG1heCwgdGFwLmRlbGF5KSwgMSk7CiAgICAgICAgICAgIGNvbnN0IHNpemUgPSAyICoqIE1hdGguY2VpbChNYXRoLmxvZzIobWF4RGVsYXkgKyAyKSk7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyID0gbmV3IEZsb2F0MzJBcnJheShzaXplKTsKICAgICAgICAgICAgdGhpcy5tYXNrID0gc2l6ZSAtIDE7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZV0gPSBpbnB1dDsKICAgICAgICAgICAgbGV0IG91dHB1dCA9IDA7CiAgICAgICAgICAgIGZvciAoY29uc3QgdGFwIG9mIHRoaXMudGFwcykKICAgICAgICAgICAgICAgIG91dHB1dCArPSB0YXAuZ2FpbiAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlIC0gdGFwLmRlbGF5KSAmIHRoaXMubWFza107CiAgICAgICAgICAgIHRoaXMud3JpdGUgPSAodGhpcy53cml0ZSArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgIH0KICAgIC8qKiBNb25vL3N0ZXJlbyBpbiB0byBzdGVyZW8gb3V0IHZlbHZldC1ub2lzZSB3aWRlbmVyIHdpdGggdHJhbnNpZW50IGJ5cGFzcy4gKi8KICAgIGNsYXNzIFN0ZXJlb1dpZGVuZXJDb3JlIHsKICAgICAgICBsZWZ0RmlyOwogICAgICAgIHJpZ2h0RmlyOwogICAgICAgIGVudmVsb3BlID0gMDsKICAgICAgICB0cmFuc2llbnQgPSAwOwogICAgICAgIHRyYW5zaWVudERlY2F5OwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5sZWZ0RmlyID0gbmV3IFNwYXJzZUZpcihidWlsZERlY29ycmVsYXRvclRhcHMoc2FtcGxlUmF0ZSwgMHg1MWYxNWUpKTsKICAgICAgICAgICAgdGhpcy5yaWdodEZpciA9IG5ldyBTcGFyc2VGaXIoYnVpbGREZWNvcnJlbGF0b3JUYXBzKHNhbXBsZVJhdGUsIDB4YzBmZmVlKSk7CiAgICAgICAgICAgIHRoaXMudHJhbnNpZW50RGVjYXkgPSBNYXRoLmV4cCgtMSAvIChzYW1wbGVSYXRlICogMC4wMTIpKTsKICAgICAgICB9CiAgICAgICAgcHJvY2Vzc1NhbXBsZShsZWZ0LCByaWdodCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IGxldmVsID0gTWF0aC5tYXgoTWF0aC5hYnMobGVmdCksIE1hdGguYWJzKHJpZ2h0KSk7CiAgICAgICAgICAgIGlmIChsZXZlbCA+IHRoaXMuZW52ZWxvcGUgKiAzICsgMC4wNCkKICAgICAgICAgICAgICAgIHRoaXMudHJhbnNpZW50ID0gMTsKICAgICAgICAgICAgdGhpcy5lbnZlbG9wZSA9IE1hdGgubWF4KGxldmVsLCB0aGlzLmVudmVsb3BlICogMC45OTUpOwogICAgICAgICAgICB0aGlzLnRyYW5zaWVudCAqPSB0aGlzLnRyYW5zaWVudERlY2F5OwogICAgICAgICAgICBjb25zdCBwcm90ZWN0aW9uID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgcGFyYW1zLnRyYW5zaWVudFByb3RlY3Rpb24pKTsKICAgICAgICAgICAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBwYXJhbXMud2lkdGgpKSAqICgxIC0gcHJvdGVjdGlvbiAqIHRoaXMudHJhbnNpZW50KTsKICAgICAgICAgICAgY29uc3QgZGVjb3JyZWxhdGlvbiA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEsIHBhcmFtcy5kZWNvcnJlbGF0aW9uKSk7CiAgICAgICAgICAgIGNvbnN0IGZpckxlZnQgPSB0aGlzLmxlZnRGaXIucHJvY2VzcyhsZWZ0KTsKICAgICAgICAgICAgY29uc3QgZmlyUmlnaHQgPSB0aGlzLnJpZ2h0RmlyLnByb2Nlc3MocmlnaHQpOwogICAgICAgICAgICBjb25zdCBkZWNvckxlZnQgPSBsZWZ0ICogKDEgLSBkZWNvcnJlbGF0aW9uKSArIGZpckxlZnQgKiBkZWNvcnJlbGF0aW9uOwogICAgICAgICAgICBjb25zdCBkZWNvclJpZ2h0ID0gcmlnaHQgKiAoMSAtIGRlY29ycmVsYXRpb24pICsgZmlyUmlnaHQgKiBkZWNvcnJlbGF0aW9uOwogICAgICAgICAgICBjb25zdCBkcnlHYWluID0gTWF0aC5jb3MoKE1hdGguUEkgLyAyKSAqIHdpZHRoKTsKICAgICAgICAgICAgY29uc3Qgd2V0R2FpbiA9IE1hdGguc2luKChNYXRoLlBJIC8gMikgKiB3aWR0aCk7CiAgICAgICAgICAgIHJldHVybiBbbGVmdCAqIGRyeUdhaW4gKyBkZWNvckxlZnQgKiB3ZXRHYWluLCByaWdodCAqIGRyeUdhaW4gKyBkZWNvclJpZ2h0ICogd2V0R2Fpbl07CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIFN0ZXJlb1dpZGVuZXJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlID0gbmV3IFN0ZXJlb1dpZGVuZXJDb3JlKHNhbXBsZVJhdGUpOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJ3aWR0aCIsIGRlZmF1bHRWYWx1ZTogMC42NSwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAxLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKICAgICAgICAgICAgICAgIHsgbmFtZTogImRlY29ycmVsYXRpb24iLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJ0cmFuc2llbnRQcm90ZWN0aW9uIiwgZGVmYXVsdFZhbHVlOiAwLjc1LCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDEsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICBdOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgaWYgKG91dHB1dC5sZW5ndGggPCAyKQogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIGNvbnN0IGxlZnRJbnB1dCA9IGlucHV0WzBdOwogICAgICAgICAgICBjb25zdCByaWdodElucHV0ID0gaW5wdXRbMV0gPz8gbGVmdElucHV0OwogICAgICAgICAgICBjb25zdCBsZWZ0T3V0cHV0ID0gb3V0cHV0WzBdOwogICAgICAgICAgICBjb25zdCByaWdodE91dHB1dCA9IG91dHB1dFsxXTsKICAgICAgICAgICAgaWYgKCFsZWZ0SW5wdXQgfHwgIXJpZ2h0SW5wdXQpIHsKICAgICAgICAgICAgICAgIGxlZnRPdXRwdXQuZmlsbCgwKTsKICAgICAgICAgICAgICAgIHJpZ2h0T3V0cHV0LmZpbGwoMCk7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICB3aWR0aDogcGFyYW1ldGVycy53aWR0aFtwYXJhbWV0ZXJzLndpZHRoLmxlbmd0aCAtIDFdLAogICAgICAgICAgICAgICAgZGVjb3JyZWxhdGlvbjogcGFyYW1ldGVycy5kZWNvcnJlbGF0aW9uW3BhcmFtZXRlcnMuZGVjb3JyZWxhdGlvbi5sZW5ndGggLSAxXSwKICAgICAgICAgICAgICAgIHRyYW5zaWVudFByb3RlY3Rpb246IHBhcmFtZXRlcnMudHJhbnNpZW50UHJvdGVjdGlvbltwYXJhbWV0ZXJzLnRyYW5zaWVudFByb3RlY3Rpb24ubGVuZ3RoIC0gMV0sCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVmdE91dHB1dC5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgW2xlZnQsIHJpZ2h0XSA9IHRoaXMuY29yZS5wcm9jZXNzU2FtcGxlKGxlZnRJbnB1dFtpXSwgcmlnaHRJbnB1dFtpXSwgcGFyYW1zKTsKICAgICAgICAgICAgICAgIGxlZnRPdXRwdXRbaV0gPSBsZWZ0OwogICAgICAgICAgICAgICAgcmlnaHRPdXRwdXRbaV0gPSByaWdodDsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3Nvcigic3RlcmVvLXdpZGVuZXIiLCBTdGVyZW9XaWRlbmVyV29ya2xldFByb2Nlc3Nvcik7CgogICAgZXhwb3J0cy5TdGVyZW9XaWRlbmVyV29ya2xldFByb2Nlc3NvciA9IFN0ZXJlb1dpZGVuZXJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zdGVyZW8td2lkZW5lci1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHRyZW1vbG8gPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKgogICAgICogVHJlbW9sbyBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciBMRk8tZHJpdmVuIGFtcGxpdHVkZQogICAgICogbW9kdWxhdGlvbiAoYSBWQ0Egd2hvc2UgZ2FpbiBpcyBzd3VuZyBieSBhIGxvdy1mcmVxdWVuY3kgb3NjaWxsYXRvcikuCiAgICAgKgogICAgICogVHJlbW9sbyBpcyB0aGUgbW9zdCBFTEVNRU5UQVJZIG1vZHVsYXRpb24gZWZmZWN0IOKAlCBwZXItc2FtcGxlIGFtcGxpdHVkZQogICAgICogbW9kdWxhdGlvbiDigJQgYW5kIGhhcyBubyBkZWRpY2F0ZWQgcGVlci1yZXZpZXdlZCBEU1AgcGFwZXIuIFRoaXMgaW1wbGVtZW50YXRpb24KICAgICAqIGlzIGFuY2hvcmVkIGhvbmVzdGx5IHRvOgogICAgICogICAtIFN0YW5kYXJkIGFtcGxpdHVkZS1tb2R1bGF0aW9uIHRoZW9yeSBmb3IgdGhlIGdhaW4gbGF3IGFuZCBpdHMgc2lkZWJhbmQKICAgICAqICAgICBzdHJ1Y3R1cmU6IGEgY2FycmllciBhdCBmX2MgbXVsdGlwbGllZCBieSAoMSArIG0qY29zKDIqcGkqZl9tKnQpKSBnYWlucyB0d28KICAgICAqICAgICBzaWRlYmFuZHMgYXQgZl9jICsvLSBmX20gb2YgYW1wbGl0dWRlIG0vMi4KICAgICAqICAgLSBKLiBEYXR0b3JybywgIkVmZmVjdCBEZXNpZ24sIFBhcnQgMiIgKEpBRVMgMTk5NyksIHAuNzc2LCBmb3IgdGhlIHF1YWRyYXR1cmUKICAgICAqICAgICAoOTAtZGVnIHBlci1jaGFubmVsKSBzdGVyZW8gTEZPIHVzZWQgZm9yIHN0ZXJlbyB0cmVtb2xvIC8gYXV0by1wYW4uCiAgICAgKiAgIC0gQy4gTWl0Y2hlbHRyZWUsIEMuIEouIFN0ZWlubWV0eiwgTS4gQ29tdW5pdGEsIEouIEQuIFJlaXNzLCAiTW9kdWxhdGlvbgogICAgICogICAgIEV4dHJhY3Rpb24gZm9yIExGTy1kcml2ZW4gQXVkaW8gRWZmZWN0cyIgKERBRngyMyksIFNlYy4gMi4xLCBmb3IgdGhlCiAgICAgKiAgICAgTEZPLWRyaXZlbi1lZmZlY3QgZnJhbWluZyAoYW4gTEZPIGRyaXZpbmcgYSBWQ0EgZ2FpbikgYW5kIHRoZSBzdGFuZGFyZCBMRk8KICAgICAqICAgICBzaGFwZXMgKHNpbmUgLyB0cmlhbmdsZSAvIHNxdWFyZSkuIFRoZSBwYXBlcidzIHRlY2huaWNhbCBjb3JlIChuZXVyYWwgTEZPCiAgICAgKiAgICAgZXh0cmFjdGlvbikgaXMgTk9UIHVzZWQgaGVyZTsgb25seSBpdHMgZnJhbWluZy4KICAgICAqCiAgICAgKiBBTSBnYWluIGxhdyAoZyA+PSAwIGFsd2F5cyDigJQgYSB0cnVlIHRyZW1vbG8gbmV2ZXIgaW52ZXJ0cyB0aGUgc2lnbmFsIHBoYXNlOwogICAgICogYWxsb3dpbmcgZyA8IDAgd291bGQgYmUgcmluZyBtb2R1bGF0aW9uIC8gY2FycmllciBzdXBwcmVzc2lvbiwgYSBkaWZmZXJlbnQKICAgICAqIGVmZmVjdCk6CiAgICAgKiAgIGxmbyA9IHNoYXBlKHBoYXNlICsgY2hhbm5lbEluZGV4ICogc3RlcmVvUGhhc2VSYWQpICAgaW4gWy0xLCAxXQogICAgICogICB1ICAgPSAwLjUgKiAoMSArIGxmbykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlwb2xhciBbMCwgMV0KICAgICAqICAgZyAgID0gKDEgLSBkZXB0aCkgKyBkZXB0aCAqIHUgICAgICAgICAgICAgICAgICAgICAgICAgc3dpbmdzICgxLWRlcHRoKSAuLiAxCiAgICAgKiAgIHkgICA9IHggKiBnCiAgICAgKiBUaGUgZ2FpbiBpcyBjb21wdXRlZCBQRVIgU0FNUExFIGZyb20gYSBjb250aW51b3VzbHktYWR2YW5jZWQgTEZPIHBoYXNlLCBzbwogICAgICogdGhlcmUgYXJlIG5vIGJsb2NrLXJhdGUgZ2FpbiBzdGVwcyAobm8gInppcHBlciIgY2xpY2tzKS4KICAgICAqCiAgICAgKiBTdGVyZW86IGVhY2ggY2hhbm5lbCdzIGNvcmUgaXMgY29uc3RydWN0ZWQgd2l0aCBpdHMgY2hhbm5lbEluZGV4OyB0aGUgTEZPCiAgICAgKiBvZmZzZXQgY2hhbm5lbEluZGV4ICogc3RlcmVvUGhhc2VSYWQgaXMgYXBwbGllZCBMSVZFIGVhY2ggYmxvY2ssIHNvIHN0ZXJlb1BoYXNlCiAgICAgKiBpcyBhbiBhZGp1c3RhYmxlIEF1ZGlvUGFyYW0gKHVubGlrZSB0aGUgbW9kdWxhdGVkLWRlbGF5L3BoYXNlciBmaXhlZCBjaCpwaS8yCiAgICAgKiBzZWVkKS4gc3RlcmVvUGhhc2UgPSAwIC0+IG1vbm8gdHJlbW9sbyAoYm90aCBjaGFubmVscyBpbiBwaGFzZSk7IDkwIC0+CiAgICAgKiBxdWFkcmF0dXJlOyAxODAgLT4gaGFyZCBhdXRvLXBhbiAoY2hhbm5lbHMgYW50aS1waGFzZSkuCiAgICAgKgogICAgICogVGhpcyBmaWxlIGhvbGRzIE9OTFkgcHVyZSBudW1lcmljIG1hdGggKHBsYWluIG51bWJlcnMgKyBGbG9hdDMyQXJyYXkpLiBJdCBoYXMKICAgICAqIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMsIHNvIGl0IGlzIHVuaXQtdGVzdGFibGUgZGlyZWN0bHk7IHRoZQogICAgICogd29ya2xldCBzaGVsbCBpbiB0cmVtb2xvLnRzIGRlbGVnYXRlcyB0byBpdC4KICAgICAqLwogICAgLyoqCiAgICAgKiBEZWZhdWx0IEF1ZGlvUGFyYW0gdmFsdWVzIGZvciB0aGUgdHJlbW9sbyB3b3JrbGV0LCBpbiB0aGUgd29ya2xldCdzIG93biB1bml0cy4KICAgICAqIFNJTkdMRSBTT1VSQ0UgT0YgVFJVVEgg4oCUIHRoZSB3b3JrbGV0J3MgYHBhcmFtZXRlckRlc2NyaXB0b3JzYCAodHJlbW9sby50cykKICAgICAqIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdCBuZWVkIHRoZSBzaGlwcGVkCiAgICAgKiBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgbW9kZXJhdGUgfjUgSHosIGhhbGYtZGVwdGggc2luZSB0cmVtb2xvLCBtb25vIChzdGVyZW9QaGFzZSAwKS4KICAgICAqLwogICAgY29uc3QgVFJFTU9MT19ERUZBVUxUUyA9IHsKICAgICAgICByYXRlOiA1LAogICAgICAgIGRlcHRoOiAwLjUsCiAgICAgICAgc3RlcmVvUGhhc2U6IDAsCiAgICB9OwogICAgLyoqCiAgICAgKiBFdmFsdWF0ZSBhbiBMRk8gd2F2ZWZvcm0gYXQgYHBoYXNlYCAocmFkaWFucykg4oCUIGJpcG9sYXIsIHJhbmdlIFstMSwgMV0sCiAgICAgKiAyKnBpIHBlcmlvZGljLiBFeHBvc2VkIHB1cmUgZm9yIHRlc3RpbmcgdGhlIHdhdmVmb3JtIHNoYXBlcyBpbiBpc29sYXRpb24uCiAgICAgKiAgIC0gc2luZTogICAgIHNpbihwaGFzZSkuCiAgICAgKiAgIC0gdHJpYW5nbGU6IDAgYXQgcGhhc2UgMC9waSwgKzEgYXQgcGkvMiwgLTEgYXQgM3BpLzIgKGNvbnRpbnVvdXMsIHBlYWtzICsvLTEpLgogICAgICogICAtIHNxdWFyZTogICArMSBvbiB0aGUgZmlyc3QgaGFsZi1jeWNsZSBbMCwgcGkpLCAtMSBvbiB0aGUgc2Vjb25kIFtwaSwgMnBpKS4KICAgICAqLwogICAgZnVuY3Rpb24gbGZvU2hhcGUocGhhc2UsIG1vZGUpIHsKICAgICAgICAvLyBXcmFwIHRvIFswLCAyKnBpKSBmb3IgdGhlIHBpZWNld2lzZSBzaGFwZXMuCiAgICAgICAgbGV0IHAgPSBwaGFzZSAlICgyICogTWF0aC5QSSk7CiAgICAgICAgaWYgKHAgPCAwKQogICAgICAgICAgICBwICs9IDIgKiBNYXRoLlBJOwogICAgICAgIGlmIChtb2RlID09PSAidHJpYW5nbGUiKSB7CiAgICAgICAgICAgIC8vIFJpc2luZyAwLT4rMSBvdmVyIFswLCBwaS8yXSwgKzEtPi0xIG92ZXIgW3BpLzIsIDNwaS8yXSwgLTEtPjAgb3ZlciBbM3BpLzIsIDJwaV0uCiAgICAgICAgICAgIC8vIEVxdWl2YWxlbnQgY2xvc2VkIGZvcm0gYW5jaG9yZWQgdG8gc2luJ3MgcGVha3M6IGEgdW5pdCB0cmlhbmdsZSBpbiBwaGFzZS4KICAgICAgICAgICAgY29uc3QgdCA9IHAgLyAoMiAqIE1hdGguUEkpOyAvLyBbMCwgMSkKICAgICAgICAgICAgLy8gVHJpYW5nbGUgdGhhdCBpcyAwIGF0IHQ9MCwgKzEgYXQgdD0wLjI1LCAwIGF0IHQ9MC41LCAtMSBhdCB0PTAuNzUsIDAgYXQgdD0xLgogICAgICAgICAgICBpZiAodCA8IDAuMjUpCiAgICAgICAgICAgICAgICByZXR1cm4gNCAqIHQ7CiAgICAgICAgICAgIGlmICh0IDwgMC43NSkKICAgICAgICAgICAgICAgIHJldHVybiAyIC0gNCAqIHQ7CiAgICAgICAgICAgIHJldHVybiA0ICogdCAtIDQ7CiAgICAgICAgfQogICAgICAgIGlmIChtb2RlID09PSAic3F1YXJlIikgewogICAgICAgICAgICByZXR1cm4gcCA8IE1hdGguUEkgPyAxIDogLTE7CiAgICAgICAgfQogICAgICAgIC8vIHNpbmUKICAgICAgICByZXR1cm4gTWF0aC5zaW4ocCk7CiAgICB9CiAgICAvKioKICAgICAqIFN0YXRlZnVsIHRyZW1vbG8gcHJvY2Vzc29yIGZvciBPTkUgY2hhbm5lbC4gT3ducyBvbmx5IHRoZSBMRk8gcGhhc2UsIGFuZCBpcwogICAgICogcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvIHRoZSBMRk8gYWR2YW5jZXMgY29udGludW91c2x5IGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMKICAgICAqIOKAlCBjb25zdHJ1Y3RpbmcgYSBmcmVzaCBpbnN0YW5jZSBwZXIgYmxvY2sgd291bGQgcmVzZXQgdGhlIExGTyBwaGFzZSBhbmQgcHJvZHVjZQogICAgICogYW4gYXVkaWJsZSBkaXNjb250aW51aXR5IChzYW1lIGhhemFyZCBkb2N1bWVudGVkIGZvciB0aGUgbW9kdWxhdGVkLWRlbGF5IGFuZAogICAgICogcGhhc2VyIGNvcmVzKS4KICAgICAqCiAgICAgKiBgY2hhbm5lbEluZGV4YCBpcyBmaXhlZCBhdCBjb25zdHJ1Y3Rpb24gYW5kIGNvbWJpbmVkIHdpdGggdGhlIChsaXZlKSBzdGVyZW9QaGFzZQogICAgICogcGFyYW0gdG8gZ2l2ZSBlYWNoIGNoYW5uZWwgaXRzIHBoYXNlIG9mZnNldCwgc28gYSBzdGVyZW8gcGFpciBjYW4gYmUgcGFubmVkIGJ5CiAgICAgKiBhZGp1c3Rpbmcgc3RlcmVvUGhhc2Ugd2l0aG91dCByZWNvbnN0cnVjdGluZyB0aGUgY29yZXMuCiAgICAgKi8KICAgIGNsYXNzIFRyZW1vbG9Qcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgY2hhbm5lbEluZGV4OwogICAgICAgIC8qKiBDdXJyZW50IExGTyBwaGFzZSAocmFkaWFucykuICovCiAgICAgICAgbGZvUGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIGNoYW5uZWxJbmRleCkgewogICAgICAgICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlOwogICAgICAgICAgICB0aGlzLmNoYW5uZWxJbmRleCA9IGNoYW5uZWxJbmRleDsKICAgICAgICB9CiAgICAgICAgLyoqIFJlc2V0IHRoZSBMRk8gcGhhc2UgdG8gaXRzIGNvbnN0cnVjdGlvbiB2YWx1ZSAoMCkuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2suIGBpbnB1dGAgYW5kIGBvdXRwdXRgIG1heSBhbGlhcyAoaW4tcGxhY2UgaXMgZmluZSkuCiAgICAgICAgICoKICAgICAgICAgKiBQZXIgc2FtcGxlIChBTSBsYXcpOgogICAgICAgICAqICAgMS4gbGZvID0gc2hhcGUobGZvUGhhc2UgKyBjaGFubmVsSW5kZXggKiBzdGVyZW9QaGFzZVJhZCkgICAoYmlwb2xhciBbLTEsIDFdKQogICAgICAgICAqICAgMi4gZyAgID0gKDEgLSBkZXB0aCkgKyBkZXB0aCAqIDAuNSAqICgxICsgbGZvKSAgICAgICAgICAgICAoaW4gWzEtZGVwdGgsIDFdKQogICAgICAgICAqICAgMy4geSAgID0geCAqIGcKICAgICAgICAgKiBUaGUgTEZPIHBoYXNlIGFkdmFuY2VzIGJ5IDIqcGkqcmF0ZS9zYW1wbGVSYXRlIGVhY2ggc2FtcGxlIChwZXItc2FtcGxlIGdhaW4sCiAgICAgICAgICogc28gbm8gemlwcGVyKS4KICAgICAgICAgKi8KICAgICAgICBwcm9jZXNzKGlucHV0LCBvdXRwdXQsIHBhcmFtcykgewogICAgICAgICAgICAvLyBrLXJhdGUtc3R5bGUgY29lZmZpY2llbnRzIGNvbXB1dGVkIG9uY2UgcGVyIGJsb2NrLgogICAgICAgICAgICBjb25zdCBkZXB0aCA9IHBhcmFtcy5kZXB0aDsKICAgICAgICAgICAgY29uc3Qgc2hhcGUgPSBwYXJhbXMuc2hhcGU7CiAgICAgICAgICAgIGNvbnN0IHBoYXNlSW5jID0gKDIgKiBNYXRoLlBJICogcGFyYW1zLnJhdGUpIC8gdGhpcy5zYW1wbGVSYXRlOwogICAgICAgICAgICBjb25zdCBjaGFubmVsT2Zmc2V0ID0gdGhpcy5jaGFubmVsSW5kZXggKiAoKHBhcmFtcy5zdGVyZW9QaGFzZSAqIE1hdGguUEkpIC8gMTgwKTsKICAgICAgICAgICAgY29uc3QgbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBsZm8gPSBsZm9TaGFwZSh0aGlzLmxmb1BoYXNlICsgY2hhbm5lbE9mZnNldCwgc2hhcGUpOwogICAgICAgICAgICAgICAgLy8gZyA+PSAwIGFsd2F5czogdSA9IDAuNSooMStsZm8pIGluIFswLDFdLCBnID0gKDEtZGVwdGgpICsgZGVwdGgqdS4KICAgICAgICAgICAgICAgIGNvbnN0IGcgPSAxIC0gZGVwdGggKyBkZXB0aCAqIDAuNSAqICgxICsgbGZvKTsKICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IGlucHV0W2ldICogZzsKICAgICAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgKz0gcGhhc2VJbmM7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5sZm9QaGFzZSA+PSAyICogTWF0aC5QSSkKICAgICAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlIC09IDIgKiBNYXRoLlBJOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBUcmVtb2xvIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMIERTUAogICAgICogbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFRyZW1vbG9Qcm9jZXNzb3IgaW4gdHJlbW9sby1jb3JlLnRzLiBNaXJyb3JzIHRoZQogICAgICogbW9kdWxhdGVkLWRlbGF5LnRzIC8gcGhhc2VyLnRzIGNvcmUvc2hlbGwgc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlCiAgICAgKiB3b3JrbGV0IHBsdW1iaW5nIChwYXJhbWV0ZXJEZXNjcmlwdG9ycywgcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IKICAgICAqIGNhbGwpOyB0aGUgYWxnb3JpdGhtIGxpdmVzIGluIHRoZSB1bml0LXRlc3RlZCBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogTEZPLWRyaXZlbiBhbXBsaXR1ZGUgbW9kdWxhdGlvbiAoYSBWQ0Egc3d1bmcgYnkgYSBsb3ctZnJlcXVlbmN5CiAgICAgKiBvc2NpbGxhdG9yKS4gU2VlIHRyZW1vbG8tY29yZS50cyBoZWFkZXIgZm9yIHRoZSBBTSBnYWluIGxhdywgaXRzIHNpZGViYW5kCiAgICAgKiBzdHJ1Y3R1cmUsIHRoZSBob25lc3QgcGFwZXIgYW5jaG9yaW5nIChBTSB0aGVvcnkgKyBEYXR0b3JybyAxOTk3IHAuNzc2CiAgICAgKiBxdWFkcmF0dXJlIExGTyArIE1pdGNoZWx0cmVlIGV0IGFsLiBEQUZ4MjMgTEZPIGZyYW1pbmcpLCBhbmQgdGhlIHBlci1zYW1wbGUKICAgICAqICh6aXBwZXItZnJlZSkgZ2Fpbi4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDp0cmVtb2xvXSI7CiAgICAvKioKICAgICAqIFNoYXBlIHNlbGVjdGlvbiByaWRlcyBvbiBhbiBBdWRpb1BhcmFtICh3aGljaCBjYW4gb25seSBjYXJyeSBudW1iZXJzKSwgc28gdGhlCiAgICAgKiAic2hhcGUiIHBhcmFtIGlzIGFuIGVudW0gaW5kZXg6IDAgPSBzaW5lLCAxID0gdHJpYW5nbGUsIDIgPSBzcXVhcmUuCiAgICAgKi8KICAgIGNvbnN0IFNIQVBFX0JZX0lOREVYID0gWyJzaW5lIiwgInRyaWFuZ2xlIiwgInNxdWFyZSJdOwogICAgZnVuY3Rpb24gc2hhcGVGcm9tSW5kZXgoaW5kZXgpIHsKICAgICAgICBjb25zdCBpID0gTWF0aC5yb3VuZChpbmRleCk7CiAgICAgICAgcmV0dXJuIFNIQVBFX0JZX0lOREVYW2ldID8/ICJzaW5lIjsKICAgIH0KICAgIGNsYXNzIFRyZW1vbG9Xb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBMRk8gcGhhc2UgYWNyb3NzCiAgICAgICAgLy8gcHJvY2VzcygpIGJsb2Nrcy4gRWFjaCBjb3JlIGlzIGNvbnN0cnVjdGVkIHdpdGggaXRzIGNoYW5uZWxJbmRleCBzbyB0aGUKICAgICAgICAvLyAobGl2ZSkgc3RlcmVvUGhhc2UgcGFyYW0gY2FuIHBhbiB0aGUgY2hhbm5lbHMgYXBhcnQgKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBUUkVNT0xPX0RFRkFVTFRTICh0cmVtb2xvLWNvcmUudHMpIOKAlCB0aGUgc2luZ2xlCiAgICAgICAgICAgIC8vIHNvdXJjZSBvZiB0cnV0aCBzaGFyZWQgd2l0aCB0aGUgY29yZSB0ZXN0cy4gUmFuZ2VzIGxpdmUgaGVyZS4gYHNoYXBlYCBpcyBhbgogICAgICAgICAgICAvLyBlbnVtIGluZGV4ICgwPXNpbmUsIDE9dHJpYW5nbGUsIDI9c3F1YXJlKTsgc3RlcmVvUGhhc2UgaXMgaW4gZGVncmVlcy4KICAgICAgICAgICAgcmV0dXJuIFsKICAgICAgICAgICAgICAgIFsicmF0ZSIsIFRSRU1PTE9fREVGQVVMVFMucmF0ZSwgMCwgMjAsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVwdGgiLCBUUkVNT0xPX0RFRkFVTFRTLmRlcHRoLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbInNoYXBlIiwgMCwgMCwgMiwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJzdGVyZW9QaGFzZSIsIFRSRU1PTE9fREVGQVVMVFMuc3RlcmVvUGhhc2UsIDAsIDE4MCwgImstcmF0ZSJdLAogICAgICAgICAgICBdLm1hcCgoW25hbWUsIGRlZmF1bHRWYWx1ZSwgbWluVmFsdWUsIG1heFZhbHVlLCBhdXRvbWF0aW9uUmF0ZV0pID0+ICh7CiAgICAgICAgICAgICAgICBuYW1lOiBuYW1lLAogICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUsCiAgICAgICAgICAgICAgICBtaW5WYWx1ZTogbWluVmFsdWUsCiAgICAgICAgICAgICAgICBtYXhWYWx1ZTogbWF4VmFsdWUsCiAgICAgICAgICAgICAgICBhdXRvbWF0aW9uUmF0ZTogYXV0b21hdGlvblJhdGUsCiAgICAgICAgICAgIH0pKTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF07CiAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICAgICAgICAgIGlmICghaW5wdXQgfHwgaW5wdXQubGVuZ3RoID09PSAwIHx8ICFvdXRwdXQgfHwgb3V0cHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gay1yYXRlIHBhcmFtczogdGFrZSB0aGUgZmlyc3QgKGNvbnN0YW50LW92ZXItYmxvY2spIHZhbHVlLgogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICByYXRlOiBwYXJhbWV0ZXJzLnJhdGVbMF0sCiAgICAgICAgICAgICAgICBkZXB0aDogcGFyYW1ldGVycy5kZXB0aFswXSwKICAgICAgICAgICAgICAgIHNoYXBlOiBzaGFwZUZyb21JbmRleChwYXJhbWV0ZXJzLnNoYXBlWzBdKSwKICAgICAgICAgICAgICAgIHN0ZXJlb1BoYXNlOiBwYXJhbWV0ZXJzLnN0ZXJlb1BoYXNlWzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gUGFzcyB0aGUgY2hhbm5lbCBpbmRleCBzbyBzdGVyZW9QaGFzZSBvZmZzZXRzIHRoZSBwZXItY2hhbm5lbCBMRk8KICAgICAgICAgICAgICAgICAgICAvLyBsaXZlIChxdWFkcmF0dXJlL2F1dG8tcGFuIHN0ZXJlbyBmaWVsZCwgRGF0dG9ycm8gcC43NzYpLgogICAgICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdID0gbmV3IFRyZW1vbG9Qcm9jZXNzb3Ioc2FtcGxlUmF0ZSwgY2gpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoInRyZW1vbG8iLCBUcmVtb2xvV29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5UcmVtb2xvV29ya2xldFByb2Nlc3NvciA9IFRyZW1vbG9Xb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD10cmVtb2xvLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIHdhdmVzaGFwZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKgogICAgICogV2F2ZXNoYXBlciBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciBhbiBhbnRpYWxpYXNlZAogICAgICogd2F2ZXNoYXBlciAvIGRpc3RvcnRpb24gdXNpbmcgZmlyc3Qtb3JkZXIgQW50aWRlcml2YXRpdmUgQW50aWFsaWFzaW5nIChBREFBKS4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBmaXJzdC1vcmRlciAocmVjdGFuZ3VsYXIta2VybmVsKSBtZXRob2QgZnJvbToKICAgICAqICAgSi4gRC4gUGFya2VyLCBWLiBaYXZhbGlzaGluLCBFLiBMZSBCaXZpYywKICAgICAqICAgIlJlZHVjaW5nIHRoZSBBbGlhc2luZyBvZiBOb25saW5lYXIgV2F2ZXNoYXBpbmcgVXNpbmcgQ29udGludW91cy1UaW1lCiAgICAgKiAgICBDb252b2x1dGlvbiIsIFByb2MuIERBRngtMTYsIEJybm8sIEN6ZWNoIFJlcHVibGljLCBTZXB0LiAyMDE2LCBwcC4gMTM3LTE0NC4KICAgICAqCiAgICAgKiBDb3JlIGlkZWEgKFBhcmtlciAyMDE2LCBlcS45KTogcmF0aGVyIHRoYW4gb3ZlcnNhbXBsZSBhIG1lbW9yeWxlc3MKICAgICAqIG5vbmxpbmVhcml0eSB5W25dID0gZih4W25dKSAod2hvc2UgaGFybW9uaWNzIGZvbGQgYmFjayBhcyBhbGlhc2luZyksCiAgICAgKiByZWNvbnN0cnVjdCB0aGUgaW5wdXQgYnkgbGluZWFyIGludGVycG9sYXRpb24sIGFwcGx5IGYgaW4gY29udGludW91cyB0aW1lLAogICAgICogYW5kIGNvbnZvbHZlIHdpdGggYSB1bml0LXdpZHRoIHJlY3Rhbmd1bGFyIGxvd3Bhc3Mga2VybmVsLiBUaGF0IGFuYWx5dGljCiAgICAgKiBjb252b2x1dGlvbiBjb2xsYXBzZXMgdG8gYSBmaXJzdC1vcmRlciBkaWZmZXJlbmNlIG9mIGYncyBhbnRpZGVyaXZhdGl2ZSBGMDoKICAgICAqCiAgICAgKiAgICAgeVtuXSA9IChGMCh4X24pIC0gRjAoeF97bi0xfSkpIC8gKHhfbiAtIHhfe24tMX0pICAgICAgICAgICAgICAoZXEuOSkKICAgICAqCiAgICAgKiB3aGVyZSBGMCcgPSBmIGFuZCB0aGUgaW50ZWdyYXRpb24gY29uc3RhbnQgaXMgY2hvc2VuIHNvIEYwKDApID0gMCAodGhpcwogICAgICogbG9jYWxpemVzIGZpbml0ZS1wcmVjaXNpb24gbG9zcyB0byB0aGUgZGVub21pbmF0b3Igb25seTsgUGFya2VyIDIwMTYgcC4yLTMpLgogICAgICoKICAgICAqIFRoZSBlcS45IHF1b3RpZW50IGlzIDAvMCB3aGVuIHhfbiB+PSB4X3tuLTF9IChjYXRhc3Ryb3BoaWMgY2FuY2VsbGF0aW9uIGluCiAgICAgKiB0aGUgbnVtZXJhdG9yLCBkaXZpc2lvbiBieSB+MCBpbiB0aGUgZGVub21pbmF0b3IpLiBQYXJrZXIgMjAxNiBlcS4xMCBnaXZlcwogICAgICogdGhlIHdlbGwtY29uZGl0aW9uZWQgbGltaXQgdmlhIHRoZSBtZWFuIHZhbHVlIHRoZW9yZW06CiAgICAgKgogICAgICogICAgIChGMCh4X24pIC0gRjAoeF97bi0xfSkpIC8gKHhfbiAtIHhfe24tMX0pCiAgICAgKiAgICAgICAgIC0+IGYoKHhfbiArIHhfe24tMX0pIC8gMikgKyBPKCh4X24gLSB4X3tuLTF9KV4yKSAgICAgICAgICAoZXEuMTApCiAgICAgKgogICAgICogaS5lLiB3aGVuIHx4X24gLSB4X3tuLTF9fCA8IGVwcywgZmFsbCBiYWNrIHRvIGV2YWx1YXRpbmcgZiBhdCB0aGUgbWlkcG9pbnQKICAgICAqIG9mIHRoZSB0d28gY29uc2VjdXRpdmUgc2FtcGxlcy4gVGhpcyBhbHNvIG1ha2VzIGEgY29uc3RhbnQgaW5wdXQgcHJvZHVjZQogICAgICogZih4KSBleGFjdGx5LgogICAgICoKICAgICAqIEdST1VQIERFTEFZOiBmaXJzdC1vcmRlciBBREFBIGlzIGVxdWl2YWxlbnQgKGF0IGxvdyBzaWduYWwgbGV2ZWxzLCBmKHgpfj14KQogICAgICogdG8geVtuXSA9ICh4X24gKyB4X3tuLTF9KS8yLCBhIDAuNS1zYW1wbGUgZnJhY3Rpb25hbCBkZWxheSAoUGFya2VyIDIwMTYgZXEuMTcsCiAgICAgKiBwLjMpLiBDYWxsZXJzIHBsYWNpbmcgdGhpcyBpbiBhIGZlZWRiYWNrIGxvb3AgbXVzdCBjb21wZW5zYXRlIGZvciB0aGF0CiAgICAgKiBpbmhlcmVudCBoYWxmLXNhbXBsZSBncm91cCBkZWxheTsgaW4gYSBwbGFpbiBmb3J3YXJkIGVmZmVjdHMgY2hhaW4gaXQgaXMKICAgICAqIGhhcm1sZXNzLiBUaGUgbWV0aG9kIGFsc28gaW1wb3NlcyBhIG1pbGQgSEYgcm9sbG9mZiAobm9uLWJyaWNrd2FsbCBrZXJuZWwpLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzICsgRmxvYXQzMkFycmF5KS4gSXQgaGFzCiAgICAgKiBubyBBdWRpb1dvcmtsZXQgLyBnbG9iYWwgZGVwZW5kZW5jaWVzLCBzbyBpdCBpcyB1bml0LXRlc3RhYmxlIGRpcmVjdGx5OyB0aGUKICAgICAqIHdvcmtsZXQgc2hlbGwgaW4gd2F2ZXNoYXBlci50cyBkZWxlZ2F0ZXMgdG8gaXQuCiAgICAgKi8KICAgIC8qKgogICAgICogVGhyZXNob2xkIG9uIHx4X24gLSB4X3tuLTF9fCBiZWxvdyB3aGljaCBlcS45IGlzIGlsbC1jb25kaXRpb25lZCBhbmQgd2UgdXNlCiAgICAgKiB0aGUgZXEuMTAgbWlkcG9pbnQgZmFsbGJhY2suIFNtYWxsIHJlbGF0aXZlIHRvIHR5cGljYWwgZHJpdmUgbGV2ZWxzIGJ1dCBsYXJnZQogICAgICogZW5vdWdoIHRvIHN0YXkgY2xlYXIgb2YgY2F0YXN0cm9waGljIGNhbmNlbGxhdGlvbiBpbiBzaW5nbGUgcHJlY2lzaW9uLgogICAgICovCiAgICBjb25zdCBBREFBX0VQUyA9IDFlLTU7CiAgICAvLyAtLS0gdGFuaCBub25saW5lYXJpdHkgKFBhcmtlciAyMDE2IFNlYyA1LjEsIGVxcyAxOS0yMCkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAvKiogZih4KSA9IHRhbmgoeCkgIChQYXJrZXIgMjAxNiBlcS4xOSkuIEJvdW5kZWQsIG1vbm90b25lLCBvZGQ7IHJhbmdlICgtMSwgMSkuICovCiAgICBmdW5jdGlvbiBmVGFuaCh4KSB7CiAgICAgICAgcmV0dXJuIE1hdGgudGFuaCh4KTsKICAgIH0KICAgIC8qKgogICAgICogRjAoeCkgPSBsb2coY29zaCh4KSkgIChQYXJrZXIgMjAxNiBlcS4yMCksIHRoZSBhbnRpZGVyaXZhdGl2ZSBvZiB0YW5oIHdpdGgKICAgICAqIEYwKDApID0gbG9nKGNvc2goMCkpID0gbG9nKDEpID0gMCwgc28gdGhlIEYwKDApPTAgY29uc3RhbnQgY29uZGl0aW9uIGhvbGRzLgogICAgICoKICAgICAqIENvbXB1dGVkIGluIHRoZSBvdmVyZmxvdy1zdGFibGUgZm9ybQogICAgICogICAgIGxvZyhjb3NoKHgpKSA9IHx4fCArIGxvZygoMSArIGVeey0yfHh8fSkgLyAyKQogICAgICogd2hpY2ggYXZvaWRzIGNvc2goeCkgb3ZlcmZsb3dpbmcgdG8gK0luZiBmb3IgbGFyZ2UgfHh8IChhIG5haXZlCiAgICAgKiBNYXRoLmxvZyhNYXRoLmNvc2goeCkpIHJldHVybnMgSW5mIHBhc3QgfHh8IH49IDcxMCkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGYwVGFuaCh4KSB7CiAgICAgICAgY29uc3QgYXggPSBNYXRoLmFicyh4KTsKICAgICAgICByZXR1cm4gYXggKyBNYXRoLmxvZygoMSArIE1hdGguZXhwKC0yICogYXgpKSAvIDIpOwogICAgfQogICAgLy8gLS0tIGhhcmQgY2xpcHBlciBub25saW5lYXJpdHkgKFBhcmtlciAyMDE2IFNlYyA1LjIsIGVxcyAyNC0yNSkgLS0tLS0tLS0tLS0tLQogICAgLyoqCiAgICAgKiBmKHgpID0geCBmb3IgLTEgPD0geCA8PSAxLCBlbHNlIHNnbih4KSAgKFBhcmtlciAyMDE2IGVxLjI0KS4gU2F0dXJhdGVzIHRvCiAgICAgKiArLy0xIGJleW9uZCB0aGUgdW5pdCB0aHJlc2hvbGQuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGZIYXJkQ2xpcCh4KSB7CiAgICAgICAgaWYgKHggPiAxKQogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICBpZiAoeCA8IC0xKQogICAgICAgICAgICByZXR1cm4gLTE7CiAgICAgICAgcmV0dXJuIHg7CiAgICB9CiAgICAvKioKICAgICAqIEYwKHgpLCBhbnRpZGVyaXZhdGl2ZSBvZiB0aGUgaGFyZCBjbGlwcGVyIChQYXJrZXIgMjAxNiBlcS4yNSk6CiAgICAgKiAgICAgRjAoeCkgPSAoMS8yKSB4XjIgICAgICAgICAgICAgICAgICBmb3IgLTEgPD0geCA8PSAxCiAgICAgKiAgICAgRjAoeCkgPSB4KnNnbih4KSAtIDEvMiAgPSB8eHwgLSAxLzIgICBvdGhlcndpc2UKICAgICAqIFRoZSBpbnRlZ3JhdGlvbiBjb25zdGFudCBpcyBjaG9zZW4gc28gRjAoMCkgPSAwIGFuZCBGMCBpcyBjb250aW51b3VzIGF0CiAgICAgKiB4ID0gKy8tMSAoYm90aCBicmFuY2hlcyBnaXZlIDEvMiB0aGVyZSksIHBlciBQYXJrZXIgMjAxNiBwLjQuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGYwSGFyZENsaXAoeCkgewogICAgICAgIGNvbnN0IGF4ID0gTWF0aC5hYnMoeCk7CiAgICAgICAgaWYgKGF4IDw9IDEpCiAgICAgICAgICAgIHJldHVybiAwLjUgKiB4ICogeDsKICAgICAgICByZXR1cm4gYXggLSAwLjU7CiAgICB9CiAgICAvKiogUmVzb2x2ZSBhIHNoYXBlIG5hbWUgdG8gaXRzIG1hdGNoZWQgKGYsIEYwKSBwYWlyLiAqLwogICAgZnVuY3Rpb24gbm9ubGluZWFyaXR5KHNoYXBlKSB7CiAgICAgICAgaWYgKHNoYXBlID09PSAidGFuaCIpCiAgICAgICAgICAgIHJldHVybiB7IGY6IGZUYW5oLCBmMDogZjBUYW5oIH07CiAgICAgICAgcmV0dXJuIHsgZjogZkhhcmRDbGlwLCBmMDogZjBIYXJkQ2xpcCB9OwogICAgfQogICAgLyoqCiAgICAgKiBTdGF0ZWZ1bCBmaXJzdC1vcmRlciBBREFBIHdhdmVzaGFwZXIgZm9yIE9ORSBjaGFubmVsLiBIb2xkcyB0aGUgcHJldmlvdXMKICAgICAqIGlucHV0IHNhbXBsZSB4X3tuLTF9IGFuZCBpdHMgYW50aWRlcml2YXRpdmUgdmFsdWUgRjAoeF97bi0xfSkgYWNyb3NzCiAgICAgKiBwcm9jZXNzKCkgYmxvY2tzIHNvIHRoZSBlcS45IGRpZmZlcmVuY2UgaXMgY29udGludW91cyBvdmVyIGJsb2NrIGJvdW5kYXJpZXMuCiAgICAgKi8KICAgIGNsYXNzIFdhdmVzaGFwZXJQcm9jZXNzb3IgewogICAgICAgIC8qKiB4X3tuLTF9OiBwcmV2aW91cyAoZHJpdmVuKSBpbnB1dCBzYW1wbGUuICovCiAgICAgICAgeFByZXYgPSAwOwogICAgICAgIC8qKiBGMCh4X3tuLTF9KTogcHJldmlvdXMgYW50aWRlcml2YXRpdmUgdmFsdWUsIGNhY2hlZCBzbyBvbmx5IG9uZSBGMCBldmFsIHBlciBzYW1wbGUuICovCiAgICAgICAgZjBQcmV2ID0gMDsKICAgICAgICAvKiogU2hhcGUgd2hvc2UgRjAgY2FjaGUgKGYwUHJldikgaXMgY3VycmVudGx5IHZhbGlkOyByZS1wcmltZXMgZjBQcmV2IG9uIGNoYW5nZS4gKi8KICAgICAgICBjYWNoZWRTaGFwZSA9ICJoYXJkY2xpcCI7CiAgICAgICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgICAgIC8vIGYwUHJldiA9IEYwKDApID0gMCBmb3IgYm90aCBzaGFwZXMgKEYwKDApPTAgYnkgY29uc3RydWN0aW9uKS4KICAgICAgICAgICAgdGhpcy5mMFByZXYgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBBcHBseSBmaXJzdC1vcmRlciBBREFBIHdhdmVzaGFwaW5nIHRvIGBpbnB1dGAsIHdyaXRpbmcgdG8gYG91dHB1dGAuCiAgICAgICAgICogYGlucHV0YCBhbmQgYG91dHB1dGAgbWF5IGJlIHRoZSBzYW1lIEZsb2F0MzJBcnJheSAoaW4tcGxhY2Ugc2FmZSDigJQgdGhlCiAgICAgICAgICogZHJpdmVuIHNhbXBsZSBpcyByZWFkIGludG8gYSBsb2NhbCBiZWZvcmUgb3V0cHV0W2ldIGlzIHdyaXR0ZW4pLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IHsgZiwgZjAgfSA9IG5vbmxpbmVhcml0eShwYXJhbXMuc2hhcGUpOwogICAgICAgICAgICBjb25zdCBkcml2ZSA9IHBhcmFtcy5kcml2ZTsKICAgICAgICAgICAgY29uc3Qgb3V0ID0gcGFyYW1zLm91dHB1dDsKICAgICAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1zLm1peDsKICAgICAgICAgICAgLy8gSWYgdGhlIHNoYXBlIGNoYW5nZWQgc2luY2UgdGhlIGxhc3QgYmxvY2ssIHRoZSBjYWNoZWQgRjAoeF97bi0xfSkgYmVsb25ncwogICAgICAgICAgICAvLyB0byB0aGUgb2xkIGFudGlkZXJpdmF0aXZlIOKAlCByZS1ldmFsdWF0ZSBpdCB1bmRlciB0aGUgbmV3IHNoYXBlIHNvIGVxLjkgaXMKICAgICAgICAgICAgLy8gY29uc2lzdGVudC4KICAgICAgICAgICAgaWYgKHBhcmFtcy5zaGFwZSAhPT0gdGhpcy5jYWNoZWRTaGFwZSkgewogICAgICAgICAgICAgICAgdGhpcy5mMFByZXYgPSBmMCh0aGlzLnhQcmV2KTsKICAgICAgICAgICAgICAgIHRoaXMuY2FjaGVkU2hhcGUgPSBwYXJhbXMuc2hhcGU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBkcnkgPSBpbnB1dFtpXTsKICAgICAgICAgICAgICAgIGNvbnN0IHhuID0gZHJ5ICogZHJpdmU7IC8vIHByZS1nYWluIChkcml2ZSkgaW50byB0aGUgbm9ubGluZWFyaXR5CiAgICAgICAgICAgICAgICBjb25zdCBmMG4gPSBmMCh4bik7IC8vIHNpbmdsZSBGMCBldmFsdWF0aW9uIHBlciBzYW1wbGUgKFBhcmtlciAyMDE2IHAuMikKICAgICAgICAgICAgICAgIGNvbnN0IGRlbm9tID0geG4gLSB0aGlzLnhQcmV2OwogICAgICAgICAgICAgICAgbGV0IHNoYXBlZDsKICAgICAgICAgICAgICAgIGlmIChNYXRoLmFicyhkZW5vbSkgPCBBREFBX0VQUykgewogICAgICAgICAgICAgICAgICAgIC8vIGVxLjEwIG1pZHBvaW50IGZhbGxiYWNrOiBlcS45IGlzIDAvMCBoZXJlOyB0aGUgd2VsbC1jb25kaXRpb25lZAogICAgICAgICAgICAgICAgICAgIC8vIGxpbWl0IGlzIGYgYXQgdGhlIG1pZHBvaW50IG9mIHRoZSB0d28gY29uc2VjdXRpdmUgc2FtcGxlcy4KICAgICAgICAgICAgICAgICAgICBzaGFwZWQgPSBmKCh4biArIHRoaXMueFByZXYpIC8gMik7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvLyBlcS45IGZpcnN0LW9yZGVyIEFEQUE6IGRpZmZlcmVuY2Ugb2YgdGhlIGFudGlkZXJpdmF0aXZlIG92ZXIgdGhlCiAgICAgICAgICAgICAgICAgICAgLy8gaW5wdXQgaW5jcmVtZW50LgogICAgICAgICAgICAgICAgICAgIHNoYXBlZCA9IChmMG4gLSB0aGlzLmYwUHJldikgLyBkZW5vbTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIC8vIGFkdmFuY2UgcGVyLWNoYW5uZWwgQURBQSBzdGF0ZQogICAgICAgICAgICAgICAgdGhpcy54UHJldiA9IHhuOwogICAgICAgICAgICAgICAgdGhpcy5mMFByZXYgPSBmMG47CiAgICAgICAgICAgICAgICAvLyB3ZXQvZHJ5IG1peCwgdGhlbiBvdXRwdXQgZ2FpbgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0gKG1peCAqIHNoYXBlZCArICgxIC0gbWl4KSAqIGRyeSkgKiBvdXQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIFJlc2V0IHBlci1jaGFubmVsIEFEQUEgaGlzdG9yeSAoeF97bi0xfSwgRjAoeF97bi0xfSkpIHRvIHRoZSBvcmlnaW4uICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMueFByZXYgPSAwOwogICAgICAgICAgICB0aGlzLmYwUHJldiA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBXYXZlc2hhcGVyIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMCiAgICAgKiBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFdhdmVzaGFwZXJQcm9jZXNzb3IgaW4gd2F2ZXNoYXBlci1jb3JlLnRzLgogICAgICogTWlycm9ycyB0aGUgZHluYW1pY3MudHMgLyBkYXR0b3Jyby1yZXZlcmIudHMgY29yZS9zaGVsbCBzcGxpdDogdGhpcyBmaWxlIG93bnMKICAgICAqIG9ubHkgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKHBhcmFtZXRlckRlc2NyaXB0b3JzLCBwcm9jZXNzKCksIHRoZQogICAgICogcmVnaXN0ZXJQcm9jZXNzb3IgY2FsbCk7IHRoZSBhbGdvcml0aG0gbGl2ZXMgaW4gdGhlIHVuaXQtdGVzdGVkIGNvcmUuCiAgICAgKgogICAgICogQWxnb3JpdGhtOiBhbnRpYWxpYXNlZCB3YXZlc2hhcGluZyAvIGRpc3RvcnRpb24gdmlhIGZpcnN0LW9yZGVyIEFudGlkZXJpdmF0aXZlCiAgICAgKiBBbnRpYWxpYXNpbmcgKEFEQUEpLCBQYXJrZXIsIFphdmFsaXNoaW4gJiBMZSBCaXZpYyAyMDE2IChEQUZ4LTE2KS4gU2VlCiAgICAgKiB3YXZlc2hhcGVyLWNvcmUudHMgaGVhZGVyIGZvciBlcS45ICh0aGUgQURBQSBkaWZmZXJlbmNlKSwgZXEuMTAgKHRoZSBtaWRwb2ludAogICAgICogc2luZ3VsYXJpdHkgZmFsbGJhY2spLCB0aGUgRjAgYW50aWRlcml2YXRpdmVzLCBhbmQgdGhlIGluaGVyZW50IDAuNS1zYW1wbGUKICAgICAqIGdyb3VwIGRlbGF5LgogICAgICovCiAgICBjb25zdCBXT1JLTEVUX0xPR19QUkVGSVggPSAiW2NhY29waG9ueS93b3JrbGV0OndhdmVzaGFwZXJdIjsKICAgIC8qKgogICAgICogU2hhcGUgc2VsZWN0aW9uIHJpZGVzIG9uIGFuIEF1ZGlvUGFyYW0gKHdoaWNoIGNhbiBvbmx5IGNhcnJ5IG51bWJlcnMpLCBzbyB0aGUKICAgICAqICJzaGFwZSIgcGFyYW0gaXMgYW4gZW51bSBpbmRleDogMCA9IGhhcmRjbGlwIChwb2x5bm9taWFsIEYwLCBQYXJrZXIgMjAxNgogICAgICogZXEuMjUpLCAxID0gdGFuaCBzb2Z0IGNsaXAgKEYwID0gbG9nIGNvc2gsIFBhcmtlciAyMDE2IGVxLjIwKS4KICAgICAqLwogICAgY29uc3QgU0hBUEVfQllfSU5ERVggPSBbImhhcmRjbGlwIiwgInRhbmgiXTsKICAgIGZ1bmN0aW9uIHNoYXBlRnJvbUluZGV4KGluZGV4KSB7CiAgICAgICAgY29uc3QgaSA9IE1hdGgucm91bmQoaW5kZXgpOwogICAgICAgIHJldHVybiBTSEFQRV9CWV9JTkRFWFtpXSA/PyAiaGFyZGNsaXAiOwogICAgfQogICAgY2xhc3MgV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIC8vIE9uZSBzdGF0ZWZ1bCBBREFBIGNvcmUgcGVyIGNoYW5uZWwgc28gZWFjaCBjaGFubmVsIGtlZXBzIGl0cyBvd24KICAgICAgICAvLyB4X3tuLTF9IC8gRjAoeF97bi0xfSkgaGlzdG9yeSAoUGFya2VyIDIwMTYgZXEuOSkgYWNyb3NzIHByb2Nlc3MoKSBibG9ja3MuCiAgICAgICAgY29yZXMgPSBbXTsKICAgICAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewogICAgICAgICAgICAvLyBkcml2ZTogcHJlLWdhaW4gaW50byB0aGUgbm9ubGluZWFyaXR5ICgxID0gdW5pdHksIGhpZ2hlciA9IG1vcmUKICAgICAgICAgICAgLy8gICBzYXR1cmF0aW9uL2hhcm1vbmljcykuIHNoYXBlOiAwPWhhcmRjbGlwLCAxPXRhbmguIG1peDogd2V0L2RyeSAoMT13ZXQpLgogICAgICAgICAgICAvLyBvdXRwdXQ6IHBvc3QgZ2Fpbi4gQWxsIGstcmF0ZSAoY29uc3RhbnQgb3ZlciB0aGUgMTI4LXNhbXBsZSBibG9jaykuCiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbImRyaXZlIiwgMSwgMCwgMTAwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbInNoYXBlIiwgMCwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJtaXgiLCAxLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbIm91dHB1dCIsIDEsIDAsIDQsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCB8fCAhb3V0cHV0IHx8IG91dHB1dC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIGstcmF0ZSBwYXJhbXM6IHRha2UgdGhlIGZpcnN0IChjb25zdGFudC1vdmVyLWJsb2NrKSB2YWx1ZS4KICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgZHJpdmU6IHBhcmFtZXRlcnMuZHJpdmVbMF0sCiAgICAgICAgICAgICAgICBzaGFwZTogc2hhcGVGcm9tSW5kZXgocGFyYW1ldGVycy5zaGFwZVswXSksCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICAgICAgb3V0cHV0OiBwYXJhbWV0ZXJzLm91dHB1dFswXSwKICAgICAgICAgICAgfTsKICAgICAgICAgICAgY29uc3QgY2hhbm5lbENvdW50ID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IGNoYW5uZWxDb3VudDsgY2grKykgewogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmNvcmVzW2NoXSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdID0gbmV3IFdhdmVzaGFwZXJQcm9jZXNzb3IoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJ3YXZlc2hhcGVyIiwgV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IpOwogICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGNvbXBsZXRlYCk7CiAgICB9CiAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICBjb25zb2xlLmVycm9yKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgZmFpbGVkYCwgZXJyb3IpOwogICAgICAgIHRocm93IGVycm9yOwogICAgfQoKICAgIGV4cG9ydHMuV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IgPSBXYXZlc2hhcGVyV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9d2F2ZXNoYXBlci1idW5kbGUuanMubWFwCg==\"","// Single source of truth for the library's AudioWorklet modules.\n//\n// Each entry pairs the `registerProcessor` name (the string passed to\n// `AudioWorkletNode` construction and to `audioWorklet.addModule`) with the\n// bundle URL. The URLs are imported with Vite's `?url` suffix; in the library\n// build Vite currently inlines each bundle as a base64 `data:` URI (see the\n// audit notes — splitting them into sibling files is deferred until a real\n// browser smoke test exists). This module has NO logic and NO dependency on\n// `cacophony.ts` or `effects.ts`, so all three can import it without a cycle.\n\nimport barberpoleBundleUrl from \"./bundles/barberpole-bundle.js?url\";\nimport dattorroReverbBundleUrl from \"./bundles/dattorro-reverb-bundle.js?url\";\nimport dynamicsBundleUrl from \"./bundles/dynamics-bundle.js?url\";\nimport fdnReverbBundleUrl from \"./bundles/fdn-reverb-bundle.js?url\";\nimport frequencyShifterBundleUrl from \"./bundles/frequency-shifter-bundle.js?url\";\nimport harmonizerBundleUrl from \"./bundles/harmonizer-bundle.js?url\";\nimport loudnessMeterBundleUrl from \"./bundles/loudness-meter-bundle.js?url\";\nimport modulatedDelayBundleUrl from \"./bundles/modulated-delay-bundle.js?url\";\nimport pcmStreamBundleUrl from \"./bundles/pcm-stream-bundle.js?url\";\nimport phaseVocoderBundleUrl from \"./bundles/phase-vocoder-bundle.js?url\";\nimport phaserBundleUrl from \"./bundles/phaser-bundle.js?url\";\nimport spectralFreezeBundleUrl from \"./bundles/spectral-freeze-bundle.js?url\";\nimport stereoToBFormatBundleUrl from \"./bundles/stereo-to-bformat-bundle.js?url\";\nimport stereoWidenerBundleUrl from \"./bundles/stereo-widener-bundle.js?url\";\nimport tremoloBundleUrl from \"./bundles/tremolo-bundle.js?url\";\nimport waveshaperBundleUrl from \"./bundles/waveshaper-bundle.js?url\";\n\n/**\n * A registrable AudioWorklet module: the processor `name` and the `url` of the\n * bundle that calls `registerProcessor(name, ...)`.\n */\nexport interface WorkletModule {\n /** The `registerProcessor` name — also the per-context load-dedup key. */\n readonly name: string;\n /** The worklet bundle URL passed to `audioWorklet.addModule`. */\n readonly url: string;\n}\n\n/**\n * The library's worklet modules, keyed by a stable identifier. Effects and the\n * pitch-shift path reference these instead of repeating name/url literals.\n */\nexport const WORKLETS = {\n phaseVocoder: { name: \"phase-vocoder\", url: phaseVocoderBundleUrl },\n harmonizer: { name: \"harmonizer\", url: harmonizerBundleUrl },\n spectralFreeze: { name: \"spectral-freeze\", url: spectralFreezeBundleUrl },\n frequencyShifter: { name: \"frequency-shifter\", url: frequencyShifterBundleUrl },\n barberpole: { name: \"barberpole\", url: barberpoleBundleUrl },\n stereoWidener: { name: \"stereo-widener\", url: stereoWidenerBundleUrl },\n stereoToBFormat: { name: \"stereo-to-bformat\", url: stereoToBFormatBundleUrl },\n dattorroReverb: { name: \"dattorro-reverb\", url: dattorroReverbBundleUrl },\n dynamics: { name: \"dynamics\", url: dynamicsBundleUrl },\n fdnReverb: { name: \"fdn-reverb\", url: fdnReverbBundleUrl },\n waveshaper: { name: \"waveshaper\", url: waveshaperBundleUrl },\n modulatedDelay: { name: \"modulated-delay\", url: modulatedDelayBundleUrl },\n pcmStream: { name: \"pcm-stream\", url: pcmStreamBundleUrl },\n phaser: { name: \"phaser\", url: phaserBundleUrl },\n tremolo: { name: \"tremolo\", url: tremoloBundleUrl },\n loudnessMeter: { name: \"loudness-meter\", url: loudnessMeterBundleUrl },\n} satisfies Record<string, WorkletModule>;\n\n/** Every worklet module, for eager preload (see `Cacophony.loadWorklets`). */\nexport const ALL_WORKLETS: readonly WorkletModule[] = Object.values(WORKLETS);\n","/**\n * Cacophony Effects: a thin abstraction over Web Audio nodes that lets a Bus\n * carry shared processing or a playback build an independent source effect.\n *\n * A {@link CacophonyEffect} is responsible for producing a live AudioNode or\n * endpoint graph when its `build` method is invoked. Single-node effects return\n * the node directly. Multi-node effects return a {@link BuiltEffectGraph} with\n * distinct `input` and `output` endpoints so effect chains can connect into and out\n * of the graph without pretending the head node is also the tail.\n *\n * The `build` return is async-capable so worklet-backed effects (e.g.\n * DattorroReverb) can `await` their AudioWorklet module load before\n * constructing the node, while pure-DOM effects (BiquadFilter, simple GainNode\n * wrappers, ConvolverNode graphs) can return synchronously.\n */\n\nimport type { AudioBuffer, AudioNode, AudioParam, AudioWorkletNode, BaseContext, BiquadFilterNode } from \"./context\";\nimport type { WorkletModule } from \"./worklets\";\nimport { WORKLETS } from \"./worklets\";\n\n/**\n * The single structural surface every worklet-backed {@link CacophonyEffect}\n * needs from its host (a {@link Cacophony} instance). Declared locally so this\n * module avoids a circular import on cacophony.ts. `buildWorkletEffect`\n * idempotently registers the worklet module on `context` (or the host's own\n * context when omitted — the cross-context contract `build(context)` promises)\n * and constructs the node with the supplied `parameterData`.\n */\nexport interface WorkletEffectHost {\n buildWorkletEffect(\n worklet: WorkletModule,\n parameterData: Record<string, number>,\n context?: BaseContext,\n nodeOptions?: AudioWorkletNodeOptions,\n ): Promise<AudioWorkletNode>;\n}\n\n/**\n * Minimal structural interface for the Cacophony surface {@link FoaDecoder} needs.\n * Declared locally (like {@link ReverbHost}) so this module avoids a circular\n * import on cacophony.ts. `loadFoaHrir` resolves (and per-context memoizes) the\n * bundled order-1 SH-HRIR `AudioBuffer`; the decoder slices it into the two\n * stereo ConvolverNode buffers.\n */\ninterface FoaDecoderHost {\n loadFoaHrir(context?: BaseContext): Promise<AudioBuffer>;\n /** The host's own default audio context, used when no context is supplied. */\n defaultContext(): BaseContext;\n}\n\n/**\n * Minimal structural interface for loading URL-backed impulse responses without\n * importing `Cacophony` into this module.\n */\ninterface ImpulseResponseHost {\n loadImpulseResponseBuffer(url: string, context: BaseContext, signal?: AbortSignal): Promise<AudioBuffer>;\n}\n\n/**\n * A built effect graph with distinct endpoints. `input` is where the previous\n * Bus chain entry connects, and `output` is where the next entry connects.\n * `handle` is the public AudioNode identity returned by `Bus.addFilter()` and\n * exposed through `Bus.filters`; when omitted the bus uses `input`.\n */\nexport interface BuiltEffectGraph {\n input: AudioNode;\n output: AudioNode;\n handle?: AudioNode;\n params?: Readonly<Record<string, AudioParam>>;\n dispose?: () => void;\n}\n\nexport type BuiltEffect = AudioNode | BuiltEffectGraph;\n\n/**\n * Public surface every Cacophony effect implements. `build` materializes the\n * effect's live node graph against a specific audio context. A Bus consumes an\n * effect as shared live state; a source consumes it as a recipe and calls\n * `build` once for every playback. Cacophony itself does not cache.\n */\nexport interface CacophonyEffect {\n build(context: BaseContext): Promise<BuiltEffect> | BuiltEffect;\n}\n\n/**\n * Base class for every worklet-backed effect. Holds the {@link WorkletModule}\n * to build and the construction-time options, and resolves the options into the\n * `parameterData` the worklet node is constructed with. `build` honors the\n * cross-context contract — it builds against the bus's `context`, not the\n * host's own — by forwarding `context` to {@link WorkletEffectHost.buildWorkletEffect}.\n *\n * Subclasses supply a {@link WorkletModule} and, when a field needs translation\n * before it can ride as an AudioParam (e.g. a string mode alias → integer index),\n * override {@link toParameterData}. The default passes the options through as a\n * numeric record, exactly as the per-effect classes did before.\n */\nexport abstract class WorkletEffect<O extends object> implements CacophonyEffect {\n protected constructor(\n protected readonly host: WorkletEffectHost,\n private readonly worklet: WorkletModule,\n protected readonly options: O,\n ) {}\n\n /**\n * Translate the construction options into worklet `parameterData`. Default:\n * pass through as a numeric record (the worklet validates/clamps downstream).\n */\n protected toParameterData(options: O): Record<string, number> {\n return { ...options } as Record<string, number>;\n }\n\n build(context: BaseContext): Promise<AudioWorkletNode> {\n return this.host.buildWorkletEffect(this.worklet, this.toParameterData(this.options), context);\n }\n}\n\n/**\n * Tracks AudioNodes that were produced by Cacophony's own factories so the\n * Bus filter-chain admission policy can distinguish a Cacophony-built node\n * (admit directly) from a raw third-party node (reject — user must wrap via\n * `cacophony.shareEffect`). WeakSet membership is GC-safe and identity-keyed.\n */\nconst cacophonyBuiltBiquads = new WeakSet<BiquadFilterNode>();\n\n/**\n * Mark a BiquadFilterNode as Cacophony-built. Called by\n * {@link Cacophony.createBiquadFilter} on the freshly constructed node.\n */\nexport function markAsCacophonyBiquad(node: BiquadFilterNode): void {\n cacophonyBuiltBiquads.add(node);\n}\n\n/**\n * Test whether a node was produced by {@link Cacophony.createBiquadFilter}.\n * When a context is supplied, the node must also belong to that context so a\n * factory-built biquad cannot be admitted to a foreign audio graph.\n */\nexport function isCacophonyBuiltBiquad(node: unknown, context?: BaseContext): node is BiquadFilterNode {\n return (\n typeof node === \"object\" &&\n node !== null &&\n cacophonyBuiltBiquads.has(node as BiquadFilterNode) &&\n (context === undefined || (node as BiquadFilterNode).context === context)\n );\n}\n\n/**\n * Effect wrapper for a pre-constructed BiquadFilterNode. `build` returns\n * the same node every time, so the same Biquad is shared across every bus\n * that adds this effect. Use when you want explicit shared filter state\n * across multiple buses.\n */\nexport class BiquadEffect implements CacophonyEffect {\n constructor(private readonly node: BiquadFilterNode) {}\n\n build(_context: BaseContext): AudioNode {\n return this.node;\n }\n}\n\n/**\n * Treats a BiquadFilterNode as a per-playback recipe. Unlike BiquadEffect,\n * which intentionally shares its node, every build creates an independent\n * node and copies the template's public filter state.\n */\nexport class BiquadRecipeEffect implements CacophonyEffect {\n constructor(private readonly template: BiquadFilterNode) {}\n\n build(context: BaseContext): BiquadFilterNode {\n const node = context.createBiquadFilter();\n node.type = this.template.type;\n node.frequency.value = this.template.frequency.value;\n node.detune.value = this.template.detune.value;\n node.Q.value = this.template.Q.value;\n node.gain.value = this.template.gain.value;\n return node;\n }\n}\n\n/**\n * Explicit \"I know what I'm doing\" wrapper around a raw AudioNode the user\n * built outside of Cacophony's factories. `Bus.addFilter` rejects raw\n * AudioNodes by default to surface the shared-state implication; wrapping\n * the node in a `ShareEffect` (via `cacophony.shareEffect(node)`) is the\n * documented opt-in. The same node instance is returned from every `build`\n * call, so all buses using the effect feed the same Web Audio node.\n */\nexport class ShareEffect implements CacophonyEffect {\n constructor(private readonly node: AudioNode) {}\n\n build(_context: BaseContext): AudioNode {\n return this.node;\n }\n}\n\nexport type ImpulseResponseSource = AudioBuffer | string;\n\nexport interface ImpulseResponseOptions {\n /** Web Audio convolver normalization. Default false to preserve measured IR gain. */\n normalize?: boolean;\n /** Dry gain for an inline dry/wet graph. Default 0 for wet-only send-bus use. */\n dry?: number;\n /** Wet gain for the convolved path. Default 1. */\n wet?: number;\n /** Optional abort signal for URL-backed impulse-response loading. */\n signal?: AbortSignal;\n}\n\n/**\n * Native ConvolverNode impulse-response effect. Wet-only construction returns a\n * single ConvolverNode and is intended for send buses. Supplying `dry` or a\n * non-unity `wet` builds an owned dry/wet graph with exposed `dry` and `wet`\n * gain AudioParams.\n */\nexport class ImpulseResponseEffect implements CacophonyEffect {\n constructor(\n private readonly host: ImpulseResponseHost,\n private readonly source: ImpulseResponseSource,\n private readonly options: ImpulseResponseOptions = {},\n ) {}\n\n async build(context: BaseContext): Promise<BuiltEffect> {\n if (!context.createConvolver) {\n throw new Error(\"ImpulseResponseEffect requires createConvolver\");\n }\n\n const buffer =\n typeof this.source === \"string\"\n ? await this.host.loadImpulseResponseBuffer(this.source, context, this.options.signal)\n : this.source;\n const convolver = context.createConvolver();\n convolver.normalize = this.options.normalize ?? false;\n convolver.buffer = buffer;\n\n const dry = this.options.dry ?? 0;\n const wet = this.options.wet ?? 1;\n if (dry === 0 && wet === 1) {\n return convolver;\n }\n\n const input = context.createGain();\n const dryGain = context.createGain();\n const wetGain = context.createGain();\n const output = context.createGain();\n dryGain.gain.value = dry;\n wetGain.gain.value = wet;\n\n input.connect(dryGain);\n dryGain.connect(output);\n input.connect(convolver);\n convolver.connect(wetGain);\n wetGain.connect(output);\n\n return {\n input,\n output,\n handle: input,\n params: { dry: dryGain.gain, wet: wetGain.gain },\n dispose: () => {\n try {\n input.disconnect(dryGain);\n } catch {}\n try {\n dryGain.disconnect(output);\n } catch {}\n try {\n input.disconnect(convolver);\n } catch {}\n try {\n convolver.disconnect(wetGain);\n } catch {}\n try {\n wetGain.disconnect(output);\n } catch {}\n },\n };\n }\n}\n\n/**\n * Subset of the {@link import('./processors/dattorro-reverb').DattorroReverbProcessor}\n * AudioWorkletProcessor's AudioParam set that we expose for construction-time\n * configuration via {@link Cacophony.createReverb}. All values are optional\n * and clamped to the worklet's documented ranges (0..1 for most params).\n *\n * Every field corresponds to a `parameterData` entry passed to the\n * AudioWorkletNode constructor; the worklet handles validation downstream.\n */\nexport interface ReverbOptions {\n /** Pre-delay in samples (0..sampleRate-1). Default 0. */\n preDelay?: number;\n /** Input bandwidth (0..1). Default 0.9999. */\n bandwidth?: number;\n /** First input diffusion stage (0..1). Default 0.75. */\n inputDiffusion1?: number;\n /** Second input diffusion stage (0..1). Default 0.625. */\n inputDiffusion2?: number;\n /** Tail decay (0..1). Default 0.5. */\n decay?: number;\n /** First decay diffusion stage (0..0.999999). Default 0.7. */\n decayDiffusion1?: number;\n /** Second decay diffusion stage (0..0.999999). Default 0.5. */\n decayDiffusion2?: number;\n /** Frequency damping (0..1). Default 0.005. */\n damping?: number;\n /** Modulation rate (0..2). Default 0.5. */\n excursionRate?: number;\n /** Modulation depth (0..2). Default 0.7. */\n excursionDepth?: number;\n /** Wet mix (0..1). Default 0.3. */\n wet?: number;\n /** Dry mix (0..1). Default 0.6. */\n dry?: number;\n}\n\n/**\n * CacophonyEffect that builds a DattorroReverb AudioWorkletNode. Calls\n * `cacophony.loadDattorroReverb(undefined, context)` first to idempotently\n * ensure the worklet module is registered on the supplied context, then\n * constructs the worklet node on the same context with the supplied options\n * as `parameterData`. The returned node is the head AND tail of the\n * subgraph (single-node wet/dry handled internally by the processor).\n *\n * Cross-context support: `build(context)` honors the {@link CacophonyEffect}\n * contract — when the effect is added to a Bus whose context differs from\n * the creating Cacophony's own context, the worklet is loaded and the\n * AudioWorkletNode is constructed against the bus's context, not the host's.\n */\nexport class ReverbEffect extends WorkletEffect<ReverbOptions> {\n constructor(host: WorkletEffectHost, options: ReverbOptions = {}) {\n super(host, WORKLETS.dattorroReverb, options);\n }\n}\n\n/**\n * Construction-time configuration for a {@link DynamicsEffect}, mirroring the\n * `dynamics` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/dynamics').DynamicsWorkletProcessor}). All fields\n * are optional; the worklet clamps to its documented ranges downstream.\n *\n * A single parameter set drives compressor, limiter, expander and gate\n * (Giannoulis 2012): ratio > 1 compresses, a very large ratio limits, and\n * ratio < 1 is downward expansion (an extreme low ratio is a gate). The\n * `createLimiter` / `createGate` factories are presets over these same params.\n */\nexport interface DynamicsOptions {\n /** Threshold T (dB), level above which compression starts. Default -24. Range -100..0. */\n threshold?: number;\n /** Ratio R (reciprocal of slope above T). Default 4. >1 compress, large=limit, <1 expand. Range 0.05..1000. */\n ratio?: number;\n /** Knee width W (dB), soft-knee transition centered on T (0 = hard). Default 6. Range 0..40. */\n knee?: number;\n /** Attack time tau_A (s). Default 0.003. Range 0..1. */\n attack?: number;\n /** Release time tau_R (s). Default 0.25. Range 0..5. */\n release?: number;\n /** Make-up gain M (dB), constant output boost. Default 0. Range -24..24. */\n makeup?: number;\n}\n\n/**\n * CacophonyEffect that builds a `dynamics` AudioWorkletNode — a feed-forward\n * dynamics processor (compressor/limiter/expander/gate) implementing Giannoulis,\n * Massberg & Reiss 2012. Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node\n * with the supplied {@link DynamicsOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class DynamicsEffect extends WorkletEffect<DynamicsOptions> {\n constructor(host: WorkletEffectHost, options: DynamicsOptions = {}) {\n super(host, WORKLETS.dynamics, options);\n }\n}\n\n/**\n * Construction-time configuration for an {@link FdnReverbEffect}, mirroring the\n * `fdn-reverb` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/fdn-reverb').FdnReverbWorkletProcessor}). All\n * fields are optional; the worklet clamps to its documented ranges downstream.\n *\n * The reverb is a Feedback Delay Network: a lossless paraunitary Hadamard\n * feedback core keeps it stable (Schlecht & Habets 2019), per-line absorption\n * filters set the decay (Jot & Chaigne 1991), and a sparse velvet-noise FIR\n * adds early echo density at no multiply cost (Fagerström et al. 2020).\n */\nexport interface FdnReverbOptions {\n /** Reverberation time T60 in seconds (−60 dB decay). Default 1.5. Range 0.001..20. */\n decayTime?: number;\n /** Pre-delay before the wet path in seconds. Default 0. Range 0..1. */\n preDelay?: number;\n /** High-frequency damping (0..1); higher shortens the HF tail. Default 0.3. */\n damping?: number;\n /** Velvet-noise diffusion amount (0..1); 0 bypasses. Default 0.5. */\n diffusion?: number;\n /** Wet/dry mix (0..1); 0 = dry, 1 = wet. Default 0.3. */\n mix?: number;\n}\n\n/**\n * CacophonyEffect that builds an `fdn-reverb` AudioWorkletNode — a Feedback\n * Delay Network reverberator (lossless paraunitary Hadamard feedback, Schlecht\n * & Habets 2019; Jot 1991 absorption-filter decay; Fagerström 2020 velvet-noise\n * diffusion). Mirrors {@link ReverbEffect}: `build` idempotently loads the\n * worklet module on the supplied context, then constructs the node with the\n * supplied {@link FdnReverbOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class FdnReverbEffect extends WorkletEffect<FdnReverbOptions> {\n constructor(host: WorkletEffectHost, options: FdnReverbOptions = {}) {\n super(host, WORKLETS.fdnReverb, options);\n }\n}\n\n/**\n * String aliases for the integer mode indices that {@link WaveshaperOptions},\n * {@link TremoloOptions} and {@link ModulatedDelayOptions} flow straight through\n * as `parameterData`. An AudioParam can only carry a number, so these maps are\n * applied inside each effect's `build()` to translate a string mode into its\n * integer index BEFORE the options become `parameterData`. The index assignments\n * mirror the worklet shells' `*_BY_INDEX` arrays exactly\n * (`processors/waveshaper.ts`, `processors/tremolo.ts`,\n * `processors/modulated-delay.ts`). Declared locally so this module does not\n * import from the worklet/processor modules (keeping that boundary intact).\n */\nconst WAVESHAPER_SHAPE_TO_INDEX = { hardclip: 0, tanh: 1 } as const;\ntype WaveshaperShapeAlias = keyof typeof WAVESHAPER_SHAPE_TO_INDEX;\n\nconst TREMOLO_SHAPE_TO_INDEX = { sine: 0, triangle: 1, square: 2 } as const;\ntype TremoloShapeAlias = keyof typeof TREMOLO_SHAPE_TO_INDEX;\n\nconst MODULATED_DELAY_INTERPOLATION_TO_INDEX = { cubic: 0, linear: 1 } as const;\ntype ModulatedDelayInterpolationAlias = keyof typeof MODULATED_DELAY_INTERPOLATION_TO_INDEX;\n\n/**\n * Resolve a mode field that may be a number (pass through unchanged, exactly as\n * today) or a string alias (translate to its integer index). An unknown/invalid\n * string maps to index 0, mirroring the worklet's `?? first` fallback. Returns\n * `undefined` when the field is absent so the worklet keeps its own default.\n */\nfunction resolveModeIndex(value: number | string | undefined, table: Record<string, number>): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value === \"number\") {\n return value;\n }\n return table[value] ?? 0;\n}\n\n/**\n * Construction-time configuration for a {@link WaveshaperEffect}, mirroring the\n * `waveshaper` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/waveshaper').WaveshaperWorkletProcessor}). All\n * fields are optional; the worklet clamps to its documented ranges downstream.\n *\n * The effect is an antialiased distortion/waveshaper using first-order\n * Antiderivative Antialiasing (Parker, Zavalishin & Le Bivic 2016, DAFx-16):\n * y[n] = (F0(x_n) - F0(x_{n-1})) / (x_n - x_{n-1}) (eq.9), with an f(midpoint)\n * fallback at the 0/0 singularity (eq.10). It carries an inherent 0.5-sample\n * group delay (first-order ADAA, eq.17).\n */\nexport interface WaveshaperOptions {\n /** Pre-gain (drive) into the nonlinearity. Default 1. Range 0..100; >1 = harder saturation. */\n drive?: number;\n /**\n * Nonlinearity: 0 = hard clip (polynomial F0, eq.25), 1 = tanh soft clip\n * (F0 = log cosh, eq.20). Default 0. Accepts either the integer index or the\n * matching string alias (`\"hardclip\"` = 0, `\"tanh\"` = 1), translated to the\n * index in `build()`.\n */\n shape?: number | WaveshaperShapeAlias;\n /** Wet/dry mix (0..1); 0 = dry bypass, 1 = fully shaped. Default 1. */\n mix?: number;\n /** Post-nonlinearity output gain (linear). Default 1. Range 0..4. */\n output?: number;\n}\n\n/**\n * CacophonyEffect that builds a `waveshaper` AudioWorkletNode — an antialiased\n * distortion/waveshaper implementing first-order Antiderivative Antialiasing\n * (Parker, Zavalishin & Le Bivic 2016, DAFx-16). Mirrors {@link ReverbEffect}:\n * `build` idempotently loads the worklet module on the supplied context, then\n * constructs the node with the supplied {@link WaveshaperOptions} as\n * `parameterData`. Honors the cross-context contract (builds against the bus's\n * context, not the host's own).\n */\nexport class WaveshaperEffect extends WorkletEffect<WaveshaperOptions> {\n constructor(host: WorkletEffectHost, options: WaveshaperOptions = {}) {\n super(host, WORKLETS.waveshaper, options);\n }\n\n protected override toParameterData(options: WaveshaperOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const shapeIndex = resolveModeIndex(options.shape, WAVESHAPER_SHAPE_TO_INDEX);\n if (shapeIndex !== undefined) {\n parameterData.shape = shapeIndex;\n }\n return parameterData;\n }\n}\n\n/**\n * Construction-time configuration for a {@link ModulatedDelayEffect}, mirroring\n * the `modulated-delay` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/modulated-delay').ModulatedDelayWorkletProcessor}).\n * All fields are optional; the worklet clamps to its documented ranges\n * downstream. Field NAMES match the AudioParam names exactly (they flow straight\n * through as `parameterData`).\n *\n * The effect is Dattorro's unified modulated-delay circuit (JAES 1997, Fig. 36)\n * with Lagrange FIR fractional-delay interpolation (Laakso 1996). One topology —\n * dry (`blend`) + a modulated wet tap (`feedforward`) + feedback on a FIXED\n * center tap — yields delay/echo, chorus, flanger, vibrato and doubling from\n * knob presets (Table 6). The `createDelay`/`createChorus`/`createFlanger`/\n * `createVibrato`/`createDoubling` factories are presets over these params.\n */\nexport interface ModulatedDelayOptions {\n /** Nominal (center) delay in ms — the fixed feedback tap center. Default 5. Range 0..1000. */\n delayTime?: number;\n /** Peak LFO delay excursion in ms (CHORUS_WIDTH); 0 = pure delay. Default 0. Range 0..50. */\n depth?: number;\n /** LFO rate f_e in Hz. Default 0.5. Range 0..20. */\n rate?: number;\n /** Feedback gain on the fixed center tap. Default 0. Range -0.9999999..0.9999999. */\n feedback?: number;\n /** Dry path gain (blend). Default 1. Range 0..1. */\n blend?: number;\n /** Wet (modulated tap) gain (feedforward). Default 0.7071. Range 0..1. */\n feedforward?: number;\n /**\n * Interpolation: 0 = cubic (4-tap Lagrange N=3), 1 = linear (2-tap N=1).\n * Default 0. Accepts either the integer index or the matching string alias\n * (`\"cubic\"` = 0, `\"linear\"` = 1), translated to the index in `build()`.\n */\n interpolation?: number | ModulatedDelayInterpolationAlias;\n}\n\n/**\n * CacophonyEffect that builds a `modulated-delay` AudioWorkletNode — Dattorro's\n * unified modulated-delay circuit (JAES 1997, Fig. 36) backing\n * delay/chorus/flanger/vibrato/doubling, with Lagrange FIR fractional-delay\n * interpolation (Laakso 1996). Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node with\n * the supplied {@link ModulatedDelayOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class ModulatedDelayEffect extends WorkletEffect<ModulatedDelayOptions> {\n constructor(host: WorkletEffectHost, options: ModulatedDelayOptions = {}) {\n super(host, WORKLETS.modulatedDelay, options);\n }\n\n protected override toParameterData(options: ModulatedDelayOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const interpolationIndex = resolveModeIndex(options.interpolation, MODULATED_DELAY_INTERPOLATION_TO_INDEX);\n if (interpolationIndex !== undefined) {\n parameterData.interpolation = interpolationIndex;\n }\n return parameterData;\n }\n}\n\n/**\n * Construction-time configuration for a {@link PhaserEffect}, mirroring the\n * `phaser` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/phaser').PhaserWorkletProcessor}). All fields are\n * optional; the worklet clamps to its documented ranges downstream. Field NAMES\n * match the AudioParam names exactly (they flow straight through as\n * `parameterData`).\n *\n * The effect is a classic MXR/Univibe-style phaser: a cascade of first-order\n * allpass sections at a common LFO-swept break frequency, summed additively with\n * the dry signal to sweep notches through the spectrum (Smith STAN-M-21; PASP\n * §8.9). Two allpass sections make one notch.\n */\nexport interface PhaserOptions {\n /** Center break frequency of the allpass sections, in Hz. Default 500. Range 20..10000. */\n frequency?: number;\n /** LFO rate in Hz. Default 0.5. Range 0..20. */\n rate?: number;\n /** Log sweep depth in octaves (break freq *= 2^(depth*lfo)). Default 1.5. Range 0..4. */\n depth?: number;\n /** Number of first-order allpass sections (2 per notch). Default 4. Range 2..12. */\n stages?: number;\n /** Feedback (regeneration/resonance). Default 0. Range -0.95..0.95. */\n feedback?: number;\n /** Additive wet gain (notch depth); y = x + mix*cascade. Default 0.5. Range 0..1. */\n mix?: number;\n}\n\n/**\n * CacophonyEffect that builds a `phaser` AudioWorkletNode — a classic\n * MXR/Univibe-style allpass-cascade phase shifter (Smith STAN-M-21; PASP §8.9).\n * Mirrors {@link ReverbEffect}: `build` idempotently loads the worklet module on\n * the supplied context, then constructs the node with the supplied\n * {@link PhaserOptions} as `parameterData`. Honors the cross-context contract\n * (builds against the bus's context, not the host's own).\n */\nexport class PhaserEffect extends WorkletEffect<PhaserOptions> {\n constructor(host: WorkletEffectHost, options: PhaserOptions = {}) {\n super(host, WORKLETS.phaser, options);\n }\n}\n\n/**\n * Construction-time configuration for a {@link TremoloEffect}, mirroring the\n * `tremolo` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/tremolo').TremoloWorkletProcessor}). All fields are\n * optional; the worklet clamps to its documented ranges downstream. Field NAMES\n * match the AudioParam names exactly (they flow straight through as\n * `parameterData`).\n *\n * The effect is LFO-driven amplitude modulation (a VCA swung by an LFO) — the\n * gain swings between (1 - depth) and 1, never inverting (a true tremolo, not\n * ring modulation). Anchored to standard AM theory, the quadrature stereo LFO of\n * Dattorro 1997 (p.776), and the LFO-driven-effect framing of Mitcheltree et al.\n * (DAFx23). `stereoPhase` offsets the per-channel LFO (0 = mono, 90 = quadrature,\n * 180 = hard auto-pan).\n */\nexport interface TremoloOptions {\n /** LFO rate in Hz. Default 5. Range 0..20. */\n rate?: number;\n /** Modulation depth (0 = bypass, 1 = full 0..1 swing). Default 0.5. Range 0..1. */\n depth?: number;\n /**\n * LFO shape: 0 = sine, 1 = triangle, 2 = square. Default 0. Accepts either\n * the integer index or the matching string alias (`\"sine\"` = 0,\n * `\"triangle\"` = 1, `\"square\"` = 2), translated to the index in `build()`.\n */\n shape?: number | TremoloShapeAlias;\n /** Per-channel LFO phase offset in degrees (0 = mono, 90 = quadrature, 180 = auto-pan). Default 0. Range 0..180. */\n stereoPhase?: number;\n}\n\n/**\n * CacophonyEffect that builds a `tremolo` AudioWorkletNode — LFO-driven amplitude\n * modulation (AM theory; Dattorro 1997 p.776 quadrature stereo LFO; Mitcheltree\n * et al. DAFx23 LFO framing). Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node with\n * the supplied {@link TremoloOptions} as `parameterData`. Honors the cross-context\n * contract (builds against the bus's context, not the host's own).\n */\nexport class TremoloEffect extends WorkletEffect<TremoloOptions> {\n constructor(host: WorkletEffectHost, options: TremoloOptions = {}) {\n super(host, WORKLETS.tremolo, options);\n }\n\n protected override toParameterData(options: TremoloOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const shapeIndex = resolveModeIndex(options.shape, TREMOLO_SHAPE_TO_INDEX);\n if (shapeIndex !== undefined) {\n parameterData.shape = shapeIndex;\n }\n return parameterData;\n }\n}\n\n/** Options for Wardle's Hilbert-transform single-sideband frequency shifter. */\nexport interface FrequencyShifterOptions {\n /** Signed frequency translation in Hz. Positive shifts up; negative shifts down. Default 100. */\n frequency?: number;\n /** Wet amount (0 = latency-aligned dry, 1 = shifted). Default 1. */\n mix?: number;\n}\n\n/** Equal-Hz frequency translation that deliberately breaks harmonic ratios. */\nexport class FrequencyShifterEffect extends WorkletEffect<FrequencyShifterOptions> {\n constructor(host: WorkletEffectHost, options: FrequencyShifterOptions = {}) {\n super(host, WORKLETS.frequencyShifter, options);\n }\n}\n\n/** Options for the SSB spectral-delay barberpole phaser (DAFx-15 Fig. 12). */\nexport interface BarberpoleOptions {\n /** Signed notch travel rate and direction in Hz. Default 0.1. */\n rate?: number;\n /** Cascaded allpass count; two stages produce one notch. Default 32. */\n stages?: number;\n /** Spectral-delay allpass coefficient. -0.5 approximates octave spacing. Default -0.5. */\n coefficient?: number;\n /** Effect mix (0 = aligned dry, 1 = barberpole sum). Default 1. */\n mix?: number;\n}\n\n/** Endlessly rising/falling, warped-notch SSB barberpole phaser. */\nexport class BarberpoleEffect extends WorkletEffect<BarberpoleOptions> {\n constructor(host: WorkletEffectHost, options: BarberpoleOptions = {}) {\n super(host, WORKLETS.barberpole, options);\n }\n}\n\n/** Two-voice Laroche-Dolson identity-phase-locked harmonizer options. */\nexport interface HarmonizerOptions {\n semitonesA?: number;\n semitonesB?: number;\n gainA?: number;\n gainB?: number;\n dry?: number;\n}\n\n/** Adds two independently shifted voices in one shared STFT analysis pass. */\nexport class HarmonizerEffect extends WorkletEffect<HarmonizerOptions> {\n constructor(host: WorkletEffectHost, options: HarmonizerOptions = {}) {\n super(host, WORKLETS.harmonizer, options);\n }\n}\n\n/** Spectral-frame capture and indefinite phase-coherent sustain options. */\nexport interface SpectralFreezeOptions {\n /** >=0.5 captures/holds; automate through rampEffectParam. Default 0. */\n freeze?: number;\n /** Blend captured magnitude with the preceding three frames. Default 0.5. */\n smear?: number;\n /** Frozen wet mix. Default 1. */\n mix?: number;\n}\n\n/** Phase-continuing spectral freeze that avoids looping one static FFT frame. */\nexport class SpectralFreezeEffect extends WorkletEffect<SpectralFreezeOptions> {\n constructor(host: WorkletEffectHost, options: SpectralFreezeOptions = {}) {\n super(host, WORKLETS.spectralFreeze, options);\n }\n}\n\n/** Sparse velvet-noise stereo-width options. */\nexport interface StereoWidenerOptions {\n width?: number;\n decorrelation?: number;\n transientProtection?: number;\n}\n\n/** Mono/stereo to explicit stereo decorrelator with transient protection. */\nexport class StereoWidenerEffect implements CacophonyEffect {\n constructor(\n private readonly host: WorkletEffectHost,\n private readonly options: StereoWidenerOptions = {},\n ) {}\n\n build(context: BaseContext): Promise<AudioWorkletNode> {\n return this.host.buildWorkletEffect(\n WORKLETS.stereoWidener,\n { ...this.options } as Record<string, number>,\n context,\n {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [2],\n channelCount: 2,\n channelCountMode: \"explicit\",\n channelInterpretation: \"speakers\",\n },\n );\n }\n}\n\n/**\n * Construction-time configuration for a {@link FoaDecoder}.\n */\nexport interface FoaDecoderOptions {\n /**\n * Caller-supplied order-1 SH-HRIR. When omitted, the decoder resolves the\n * bundled Omnitone `sh_hrir_order_1.wav` (Apache-2.0) via\n * {@link FoaDecoderHost.loadFoaHrir}. Supply a 4-channel `AudioBuffer` (ACN\n * rows W,Y,Z,X) to override — e.g. in tests, or to ship a different\n * measured SH-HRIR. If you substitute a different file the WY/ZX row\n * grouping and the Y right-ear sign inversion must still match Omnitone's\n * convention (see {@link FoaDecoder}).\n */\n hrir?: AudioBuffer;\n}\n\n/**\n * Standalone 4-channel-in / 2-channel-out FOA → binaural FORMAT CONVERTER,\n * built entirely from native Web Audio nodes (NO worklet).\n *\n * This standalone construct exposes the two endpoints explicitly:\n * - {@link input} — the 4-channel `ChannelSplitterNode` you feed FOA into.\n * - {@link output} — the 2-channel binaural `GainNode` you route downstream.\n *\n * Wire it explicitly when building custom FOA graphs:\n * ```ts\n * const decoder = await cacophony.createFoaDecoder();\n * foaSourceNode.connect(decoder.input); // 4-ch FOA in\n * decoder.output.connect(bus.input /* or context.destination *\\/); // stereo out\n * ```\n *\n * For the common bus-filter case use {@link FoaDecoderEffect}, created via\n * `cacophony.createFoaDecoderEffect()`, which returns the same endpoint graph\n * through the standard `CacophonyEffect.build()` contract.\n *\n * ## Math (Ahrens 2022, eq. 31)\n * Under a real (SN3D/ACN) SH basis the binaural decode collapses to a single\n * per-EAR multiply-accumulate over ALL the SH channels:\n * B^{L,R}(w) = sum_{n,m} S_{n,m}(w) * H_{n,m}^{L,R}(w)\n * (Ahrens, \"Binaural Audio Rendering in the Spherical Harmonic Domain\", 2022,\n * eq. 31 — the real-basis form where the conjugation and m -> -m degree flip\n * vanish). EACH ear sums the contribution of EVERY FOA channel (W, Y, Z, X)\n * convolved with that ear's HRIR — neither ear may drop a channel. With a\n * single stored SH-HRIR the L/R-symmetric channels (W, Z, X) share the same\n * coefficient for both ears while the L/R-ANTISYMMETRIC channel (Y) is the\n * sign-flip for the right ear, so:\n * B^L = W*H_W + Y*H_Y + Z*H_Z + X*H_X\n * B^R = W*H_W - Y*H_Y + Z*H_Z + X*H_X (only the Y term differs, by sign)\n *\n * ## Topology (Omnitone WY/ZX packing — GoogleChrome/omnitone foa-convolver.js)\n * Rather than 8 mono convolvers, the four SH channels are grouped into two\n * STEREO `ConvolverNode`s — W+Y into one, Z+X into the other — each convolved\n * against a 2-row slice of the 4-row SH-HRIR. A 4-channel ConvolverNode would\n * do unwanted cross-channel convolution per the Web Audio spec; the stereo\n * packing is Omnitone's production graph and is mirrored here VERBATIM (the\n * `.connect` calls below are line-for-line Omnitone's `_buildAudioGraph`):\n *\n * input (ChannelSplitter, 4ch)\n * ch0(W),ch1(Y) -> mergerWY(2ch) -> convolverWY (HRIR rows {W,Y}, stereo)\n * ch2(Z),ch3(X) -> mergerZX(2ch) -> convolverZX (HRIR rows {Z,X}, stereo)\n * convolverWY -> splitterWY(2ch); convolverZX -> splitterZX(2ch)\n * splitterWY.ch0 (W) -> L AND R\n * splitterWY.ch1 (Y) -> L, and (via -1 inverter) -> R\n * splitterZX.ch0 (Z) -> L AND R\n * splitterZX.ch1 (X) -> L AND R\n * mergerBinaural(2ch) -> output (stereo)\n *\n * The result is exactly eq.31: BOTH ears receive ALL four SH channels; only the\n * Y channel's right-ear contribution is sign-flipped by a `GainNode(-1)`,\n * because Y is the sole left/right-antisymmetric FOA channel. (This is the fix\n * for the prior broken graph, which sent only W+Z to the left ear and only\n * -Y+X to the right — each ear missing two channels.) `convolver.normalize` is\n * set `false` on both: the SH-HRIR is already correctly scaled and Web Audio\n * convolver normalization would corrupt it.\n *\n * ## Normalization (SN3D end-to-end — NO sqrt(3) rescale)\n * The decoder, the bundled Omnitone HRIR, and {@link encodeMonoToFoaSN3D} are\n * ALL SN3D. The decode is the plain MAC of two SN3D-matched coefficient sets,\n * so NO per-channel sqrt(3) (N3D<->SN3D) rescale is applied — inserting one\n * would double-normalize.\n *\n * NOTE on the resurrection path: the dormant `StereoToFoaUpmixer`\n * (`createStereoToBFormatNode`) emits ACN `[W,Y,Z,X]` but is a perceptual,\n * frequency-banded, coherence-gated mix with per-channel non-constant gain — it\n * is NEITHER N3D nor SN3D and no single normalization bridge exists. Its 4-ch\n * output plugs straight into {@link input} (ACN ordering already lines up), and\n * the resulting binaural is a documented PERCEPTUAL APPROXIMATION, not a\n * physically-correct sound field. The clean, physically-correct path is\n * {@link encodeMonoToFoaSN3D} -> this decoder.\n */\nexport class FoaDecoder {\n /** The 4-channel FOA `[W, Y, Z, X]` (ACN) entry node. Feed FOA into this. */\n readonly input: AudioNode;\n /** The 2-channel binaural stereo node. Route THIS downstream. */\n readonly output: AudioNode;\n private readonly ownedNodes: readonly AudioNode[];\n\n private constructor(input: AudioNode, output: AudioNode, ownedNodes: readonly AudioNode[]) {\n this.input = input;\n this.output = output;\n this.ownedNodes = ownedNodes;\n }\n\n /** Disconnects the decoder's owned native nodes. Safe to call more than once. */\n dispose(): void {\n for (const node of this.ownedNodes) {\n try {\n node.disconnect();\n } catch {}\n }\n }\n\n /**\n * Builds the live decode graph against `context`, resolving the SH-HRIR\n * (caller-supplied or bundled Omnitone) and wiring the Omnitone WY/ZX graph.\n * Async because the bundled HRIR is fetched + `decodeAudioData`-d.\n */\n static async create(\n host: FoaDecoderHost,\n options: FoaDecoderOptions = {},\n context?: BaseContext,\n ): Promise<FoaDecoder> {\n const ctx = context ?? host.defaultContext();\n if (!ctx.createChannelSplitter || !ctx.createChannelMerger || !ctx.createConvolver || !ctx.createBuffer) {\n throw new Error(\n \"FoaDecoder requires createChannelSplitter, createChannelMerger, createConvolver and createBuffer\",\n );\n }\n\n // SN3D SH-HRIR (4 rows: ACN W,Y,Z,X). Caller override or bundled Omnitone.\n const hrir = options.hrir ?? (await host.loadFoaHrir(ctx));\n if (hrir.numberOfChannels < 4) {\n throw new Error(`FoaDecoder requires an HRIR with at least 4 channels; received ${hrir.numberOfChannels}`);\n }\n\n // Input: 4-channel FOA [W, Y, Z, X] (ACN). Externally-visible input node.\n const input = ctx.createChannelSplitter(4);\n\n // --- WY / ZX stereo grouping (Omnitone foa-convolver.js) ---\n const mergerWY = ctx.createChannelMerger(2);\n const mergerZX = ctx.createChannelMerger(2);\n // W(ch0)->mergerWY in0, Y(ch1)->mergerWY in1\n input.connect(mergerWY, 0, 0);\n input.connect(mergerWY, 1, 1);\n // Z(ch2)->mergerZX in0, X(ch3)->mergerZX in1\n input.connect(mergerZX, 2, 0);\n input.connect(mergerZX, 3, 1);\n\n const convolverWY = ctx.createConvolver();\n const convolverZX = ctx.createConvolver();\n // The SH-HRIR is already correctly scaled; Web Audio convolver\n // normalization would corrupt it (Omnitone sets disableNormalization=true).\n convolverWY.normalize = false;\n convolverZX.normalize = false;\n convolverWY.buffer = FoaDecoder.sliceHrirRows(ctx, hrir, 0, 1); // {W, Y} rows\n convolverZX.buffer = FoaDecoder.sliceHrirRows(ctx, hrir, 2, 3); // {Z, X} rows\n mergerWY.connect(convolverWY);\n mergerZX.connect(convolverZX);\n\n // --- Per-ear sum (Ahrens eq.31 MAC, executed by the convolver graph) ---\n // This is Omnitone foa-convolver.js _buildAudioGraph VERBATIM: both convolved\n // SH-channel pairs fan into BOTH ears, so each ear sums all four SH channels;\n // only the Y (splitterWY ch1) right-ear path is inverted.\n const splitterWY = ctx.createChannelSplitter(2);\n const splitterZX = ctx.createChannelSplitter(2);\n convolverWY.connect(splitterWY);\n convolverZX.connect(splitterZX);\n\n const mergerBinaural = ctx.createChannelMerger(2);\n const yRightInverter = ctx.createGain();\n yRightInverter.gain.value = -1;\n\n // W (splitterWY ch0) -> L (in0) and R (in1)\n splitterWY.connect(mergerBinaural, 0, 0);\n splitterWY.connect(mergerBinaural, 0, 1);\n // Y (splitterWY ch1) -> L (in0) directly; -> R (in1) through the -1 inverter\n splitterWY.connect(mergerBinaural, 1, 0);\n splitterWY.connect(yRightInverter, 1, 0);\n yRightInverter.connect(mergerBinaural, 0, 1);\n // Z (splitterZX ch0) -> L (in0) and R (in1)\n splitterZX.connect(mergerBinaural, 0, 0);\n splitterZX.connect(mergerBinaural, 0, 1);\n // X (splitterZX ch1) -> L (in0) and R (in1)\n splitterZX.connect(mergerBinaural, 1, 0);\n splitterZX.connect(mergerBinaural, 1, 1);\n\n const output = ctx.createGain();\n mergerBinaural.connect(output);\n\n return new FoaDecoder(input, output, [\n input,\n mergerWY,\n mergerZX,\n convolverWY,\n convolverZX,\n splitterWY,\n splitterZX,\n yRightInverter,\n mergerBinaural,\n output,\n ]);\n }\n\n /**\n * Builds a 2-channel `AudioBuffer` from rows `rowA`/`rowB` of the 4-row\n * SH-HRIR — the stereo buffer a WY or ZX `ConvolverNode` convolves against.\n */\n private static sliceHrirRows(context: BaseContext, hrir: AudioBuffer, rowA: number, rowB: number): AudioBuffer {\n const stereo = context.createBuffer!(2, hrir.length, hrir.sampleRate);\n stereo.copyToChannel(hrir.getChannelData(rowA), 0);\n stereo.copyToChannel(hrir.getChannelData(rowB), 1);\n return stereo;\n }\n}\n\n/**\n * `CacophonyEffect` wrapper for a FOA decoder endpoint graph. Use only on a\n * dedicated 4-channel ACN/SN3D FOA bus where the decoder is the first/only\n * filter converting the bus from FOA to stereo binaural.\n */\nexport class FoaDecoderEffect implements CacophonyEffect {\n constructor(\n private readonly host: FoaDecoderHost,\n private readonly options: FoaDecoderOptions = {},\n ) {}\n\n async build(context: BaseContext): Promise<BuiltEffectGraph> {\n const decoder = await FoaDecoder.create(this.host, this.options, context);\n return {\n input: decoder.input,\n output: decoder.output,\n handle: decoder.input,\n dispose: () => decoder.dispose(),\n };\n }\n}\n\nfunction isAudioNodeLike(value: unknown): value is AudioNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AudioNode).connect === \"function\" &&\n typeof (value as AudioNode).disconnect === \"function\"\n );\n}\n\n/**\n * Type guard for the endpoint-graph shape returned by multi-node effects.\n */\nexport function isBuiltEffectGraph(value: unknown): value is BuiltEffectGraph {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"input\" in value &&\n \"output\" in value &&\n isAudioNodeLike((value as { input: unknown }).input) &&\n isAudioNodeLike((value as { output: unknown }).output)\n );\n}\n\n/**\n * Type guard for values returned by {@link CacophonyEffect.build}.\n */\nexport function isBuiltEffect(value: unknown): value is BuiltEffect {\n return isAudioNodeLike(value) || isBuiltEffectGraph(value);\n}\n\n/**\n * Type guard for the structural shape `CacophonyEffect`. A value qualifies if\n * it exposes a callable `build` property.\n */\nexport function isCacophonyEffect(value: unknown): value is CacophonyEffect {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"build\" in value &&\n typeof (value as { build: unknown }).build === \"function\"\n );\n}\n","import type { FadeType } from \"./cacophony\";\nimport type { AudioNode, AudioParam, AudioWorkletNode } from \"./context\";\nimport type { BuiltEffect } from \"./effects\";\nimport { isBuiltEffect, isBuiltEffectGraph } from \"./effects\";\n\ninterface EffectChainEntry {\n handle: AudioNode;\n input: AudioNode;\n output: AudioNode;\n params?: Readonly<Record<string, AudioParam>>;\n dispose?: () => void;\n}\n\ninterface EffectChainReservation {\n readonly reservation: symbol;\n}\n\ntype EffectChainSlot = EffectChainEntry | EffectChainReservation;\n\n/**\n * Incrementally reconciles a series of built effects between two caller-owned\n * endpoint nodes.\n */\nexport class EffectChain {\n private readonly entries: EffectChainSlot[] = [];\n private readonly bypassed = new Set<AudioNode>();\n private readonly edges: Array<readonly [AudioNode, AudioNode]> = [];\n private destroyed = false;\n\n constructor(\n private input: AudioNode,\n private output: AudioNode,\n private readonly diagnosticScope = \"EffectChain\",\n ) {\n this.connectEdge(input, output);\n }\n\n get nodes(): readonly AudioNode[] {\n return this.entries.filter(this.isEntry).map((entry) => entry.handle);\n }\n\n has(node: AudioNode): boolean {\n return this.entries.some((entry) => this.isEntry(entry) && entry.handle === node);\n }\n\n setEndpoints(input: AudioNode, output: AudioNode): void {\n if (input === this.input && output === this.output) {\n return;\n }\n this.disconnectEdges();\n this.input = input;\n this.output = output;\n this.refresh();\n }\n\n add(built: BuiltEffect, index = this.entries.length): AudioNode {\n if (this.destroyed) {\n throw this.guardError(\"cannot add an effect to a destroyed chain\");\n }\n const entry = this.normalize(built);\n if (this.has(entry.handle)) {\n entry.dispose?.();\n throw this.guardError(\"cannot add the same effect node twice\");\n }\n this.entries.splice(index, 0, entry);\n this.refresh();\n return entry.handle;\n }\n\n /** Reserve a declaration-order position for an asynchronously built effect. */\n reserve(): symbol {\n if (this.destroyed) {\n throw this.guardError(\"cannot reserve an effect on a destroyed chain\");\n }\n const reservation = Symbol(\"effect-chain-reservation\");\n this.entries.push({ reservation });\n return reservation;\n }\n\n /** Replace a reservation with its built effect while retaining its position. */\n resolve(reservation: symbol, built: BuiltEffect): AudioNode {\n const entry = this.normalize(built);\n if (this.destroyed) {\n entry.dispose?.();\n return entry.handle;\n }\n const index = this.entries.findIndex(\n (candidate) => !this.isEntry(candidate) && candidate.reservation === reservation,\n );\n if (index === -1) {\n entry.dispose?.();\n throw this.guardError(\"cannot resolve an unknown effect-chain reservation\");\n }\n if (this.has(entry.handle)) {\n this.entries.splice(index, 1);\n entry.dispose?.();\n throw this.guardError(\"cannot add the same effect node twice\");\n }\n this.entries[index] = entry;\n this.refresh();\n return entry.handle;\n }\n\n cancel(reservation: symbol): void {\n const index = this.entries.findIndex(\n (candidate) => !this.isEntry(candidate) && candidate.reservation === reservation,\n );\n if (index !== -1) {\n this.entries.splice(index, 1);\n }\n }\n\n remove(node: AudioNode): void {\n const index = this.entries.findIndex((entry) => this.isEntry(entry) && entry.handle === node);\n if (index === -1) {\n throw this.guardError(\"cannot remove an effect that was never added\");\n }\n const [entry] = this.entries.splice(index, 1);\n this.bypassed.delete(node);\n this.refresh();\n if (entry && this.isEntry(entry)) {\n entry.dispose?.();\n }\n }\n\n setOrder(nodes: readonly AudioNode[]): void {\n const isPermutation =\n nodes.length === this.nodes.length &&\n new Set(nodes).size === nodes.length &&\n nodes.every((node) => this.has(node));\n if (!isPermutation) {\n throw this.guardError(\"effect order must be a permutation of the current nodes\");\n }\n const ordered = nodes.map((node) => this.entries.find((entry) => this.isEntry(entry) && entry.handle === node)!);\n let orderedIndex = 0;\n for (let index = 0; index < this.entries.length; index += 1) {\n if (this.isEntry(this.entries[index]!)) {\n this.entries[index] = ordered[orderedIndex++]!;\n }\n }\n this.refresh();\n }\n\n setBypassed(node: AudioNode, bypassed: boolean): void {\n if (!this.has(node)) {\n throw this.guardError(\"cannot bypass an effect that was never added\");\n }\n const alreadyBypassed = this.bypassed.has(node);\n if (bypassed === alreadyBypassed) {\n return;\n }\n if (bypassed) {\n this.bypassed.add(node);\n } else {\n this.bypassed.delete(node);\n }\n this.refresh();\n }\n\n isBypassed(node: AudioNode): boolean {\n return this.bypassed.has(node);\n }\n\n rampParam(node: AudioNode, paramName: string, value: number, options?: { duration?: number; type?: FadeType }): void {\n const entry = this.entries.filter(this.isEntry).find((candidate) => candidate.handle === node);\n if (!entry) {\n console.warn(\n `${this.diagnosticScope}.rampFilterParam: node is not a filter on this ${this.diagnosticScope.toLowerCase()}; ignoring automation of '${paramName}'.`,\n );\n return;\n }\n\n const param = entry.params?.[paramName] ?? this.resolveAudioParam(node, paramName);\n if (!param) {\n console.warn(\n `${this.diagnosticScope}.rampFilterParam: could not resolve AudioParam '${paramName}' on the given node; ignoring.`,\n );\n return;\n }\n\n const now = node.context.currentTime;\n const duration = options?.duration;\n if (duration === undefined || duration <= 0) {\n param.setValueAtTime(value, now);\n return;\n }\n\n const endTime = now + duration / 1000;\n param.setValueAtTime(param.value, now);\n if (options?.type === \"exponential\") {\n param.exponentialRampToValueAtTime(value === 0 ? 0.0001 : value, endTime);\n } else {\n param.linearRampToValueAtTime(value, endTime);\n }\n }\n\n destroy(): void {\n this.destroyed = true;\n this.disconnectEdges();\n for (const entry of this.entries) {\n if (this.isEntry(entry)) {\n try {\n entry.dispose?.();\n } catch {}\n }\n }\n this.entries.length = 0;\n this.bypassed.clear();\n }\n\n private refresh(): void {\n const desired = this.desiredEdges();\n for (const [source, destination] of this.edges) {\n const stillPresent = desired.some(([desiredSource, desiredDestination]) => {\n return desiredSource === source && desiredDestination === destination;\n });\n if (!stillPresent) {\n try {\n source.disconnect(destination);\n } catch {}\n }\n }\n for (const [source, destination] of desired) {\n const alreadyConnected = this.edges.some(([currentSource, currentDestination]) => {\n return currentSource === source && currentDestination === destination;\n });\n if (!alreadyConnected) {\n source.connect(destination);\n }\n }\n this.edges.length = 0;\n this.edges.push(...desired);\n }\n\n private desiredEdges(): Array<readonly [AudioNode, AudioNode]> {\n const active = this.entries.filter(this.isEntry).filter((entry) => !this.bypassed.has(entry.handle));\n if (active.length === 0) {\n return [[this.input, this.output]];\n }\n const desired: Array<readonly [AudioNode, AudioNode]> = [];\n let previous = this.input;\n for (const entry of active) {\n desired.push([previous, entry.input]);\n previous = entry.output;\n }\n desired.push([previous, this.output]);\n return desired;\n }\n\n private normalize(built: BuiltEffect): EffectChainEntry {\n if (!isBuiltEffect(built)) {\n throw this.guardError(\"effect build must return an AudioNode or BuiltEffectGraph\");\n }\n if (isBuiltEffectGraph(built)) {\n return {\n handle: built.handle ?? built.input,\n input: built.input,\n output: built.output,\n params: built.params,\n dispose: built.dispose,\n };\n }\n return {\n handle: built,\n input: built,\n output: built,\n };\n }\n\n private isEntry(slot: EffectChainSlot): slot is EffectChainEntry {\n return \"handle\" in slot;\n }\n\n private guardError(message: string): Error {\n return new Error(`${this.diagnosticScope}: ${message}`);\n }\n\n private resolveAudioParam(node: AudioNode, paramName: string): AudioParam | undefined {\n const parameters = (node as AudioWorkletNode).parameters;\n if (parameters && typeof parameters.get === \"function\") {\n const workletParam = parameters.get(paramName);\n if (this.isAudioParam(workletParam)) {\n return workletParam;\n }\n }\n\n const nativeParam = (node as unknown as Record<string, unknown>)[paramName];\n if (this.isAudioParam(nativeParam)) {\n return nativeParam;\n }\n\n return undefined;\n }\n\n private isAudioParam(value: unknown): value is AudioParam {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AudioParam).setValueAtTime === \"function\" &&\n typeof (value as AudioParam).linearRampToValueAtTime === \"function\" &&\n typeof (value as AudioParam).exponentialRampToValueAtTime === \"function\"\n );\n }\n\n private connectEdge(source: AudioNode, destination: AudioNode): void {\n source.connect(destination);\n this.edges.push([source, destination]);\n }\n\n private disconnectEdges(): void {\n for (const [source, destination] of this.edges) {\n try {\n source.disconnect(destination);\n } catch {}\n }\n this.edges.length = 0;\n }\n}\n","/**\n * Bus — a named summing node with its own filter chain and per-edge gain.\n *\n * A Bus is a first-class routing primitive. Sounds and synths route into a\n * bus via `routeTo`; the bus mixes its inputs through an optional filter\n * chain and emits to one or more downstream targets (other buses or the\n * master bus). Connections out can be either unity-gain (direct) or\n * attenuated through an allocated GainNode (per-edge gain).\n *\n * Internal topology:\n * input → [filter1 → ... → filterN] → output → (sends to targets)\n *\n * `input` and `output` are separate GainNodes so the chain has a stable\n * external interface even as the internal filter chain is rebuilt.\n *\n * Buses are SHARED LIVE — there is no per-playback clone. The filter chain\n * holds the live node instances added via `addFilter`. Adding the same\n * filter to two buses means the same Web Audio node is used; mutating its\n * parameters affects both.\n */\n\nimport type { FadeType } from \"./cacophony\";\nimport type { AudioNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { EffectChain } from \"./effectChain\";\nimport type { BuiltEffect, CacophonyEffect } from \"./effects\";\nimport { isCacophonyBuiltBiquad, isCacophonyEffect } from \"./effects\";\n\n/**\n * Connection target for {@link Bus.connect} / {@link Bus.disconnect}. Either\n * another Bus (we connect into its `input`) or a raw AudioNode (we connect\n * directly to it — escape hatch for advanced wiring).\n */\nexport type BusConnectionTarget = Bus | AudioNode;\n\n/**\n * Structural contract a routed source (e.g. a {@link Sound}) implements so a\n * Bus can move it off itself before teardown. Declared here — rather than\n * importing `Sound` — to avoid an import cycle (`sound.ts` already imports\n * `Bus`). A Bus only ever needs this one method on its inbound sources.\n */\nexport interface BusRoutedSource {\n /**\n * Called by {@link Bus.drainTo} to move this source off the draining bus\n * onto `target`. The source reroutes its primary route and/or any send that\n * targeted `bus`.\n */\n _onBusDrained(bus: Bus, target: Bus): void;\n}\n\n/**\n * A named summing node with a filter chain and per-edge send gain. See\n * module-level docstring for topology.\n */\nexport class Bus {\n /** Stable name for registry lookup, or null for anonymous buses. */\n readonly name: string | null;\n\n /**\n * Entry node — connect upstream sources here (sound playbacks, other bus\n * outputs).\n */\n readonly input: GainNode;\n\n /**\n * Exit node — connected to downstream targets (other bus inputs, master,\n * raw nodes) via {@link connect}.\n */\n readonly output: GainNode;\n\n private readonly _context: BaseContext;\n private readonly _effectChain: EffectChain;\n private readonly _sendGains: Map<BusConnectionTarget, GainNode> = new Map();\n private readonly _directConnections: Set<BusConnectionTarget> = new Set();\n /**\n * Inbound sources currently routed to this bus (primary route and/or sends).\n * A Web Audio node cannot enumerate its own inputs, so sources register\n * themselves here (via {@link _registerRoutedSource}) when they route to\n * this bus and unregister on reroute/cleanup. {@link drainTo} walks this set\n * to move live sounds off the bus before it is torn down.\n */\n private readonly _routedSources = new Set<BusRoutedSource>();\n /**\n * Hook invoked by the owning Cacophony instance to remove this bus from\n * the named-bus registry on destroy. Anonymous buses leave this undefined.\n */\n private readonly _onDestroy?: () => void;\n private readonly _destroyable: boolean;\n private _destroyed = false;\n\n /**\n * @param context Web Audio context the bus's nodes live on.\n * @param name Name to register under, or null for an anonymous bus.\n * @param input Optional pre-existing GainNode to use as the input. Used by\n * the `master` bus to alias `cacophony.globalGainNode`. If omitted, a\n * fresh GainNode is allocated.\n * @param onDestroy Optional registry-cleanup hook fired by destroy().\n * @param destroyable Whether destroy() may tear down this bus.\n */\n constructor(\n context: BaseContext,\n name: string | null = null,\n input?: GainNode,\n onDestroy?: () => void,\n destroyable = true,\n ) {\n this._context = context;\n this.name = name;\n this.input = input ?? context.createGain();\n this.output = context.createGain();\n this._onDestroy = onDestroy;\n this._destroyable = destroyable;\n this._effectChain = new EffectChain(this.input, this.output, \"Bus\");\n }\n\n /** True after {@link destroy} has been called. */\n get destroyed(): boolean {\n return this._destroyed;\n }\n\n /** Output node gain — controls the overall level the bus sends downstream. */\n get gain(): number {\n return this.output.gain.value;\n }\n\n set gain(v: number) {\n this.output.gain.value = v;\n }\n\n /** Live filter chain (read-only view). */\n get filters(): readonly AudioNode[] {\n return this._effectChain.nodes;\n }\n\n /**\n * Add a filter node to the bus's chain. Accepts:\n *\n * - A Cacophony-built BiquadFilterNode (created via\n * `cacophony.createBiquadFilter`) → added directly to the chain.\n * - A {@link CacophonyEffect} → `build(context)` is awaited; the resulting\n * node is added to the chain.\n * - A raw third-party AudioNode → REJECTED. Wrap it with\n * `cacophony.shareEffect(node)` (or a proper CacophonyEffect class) to\n * make the shared-state intent explicit.\n *\n * @returns the built AudioNode that was added to the chain. For a biquad this\n * is the argument itself; for a {@link CacophonyEffect} it is the node\n * produced by `build`. The returned handle can be passed to\n * {@link rampFilterParam} to automate the node's parameters. Existing\n * callers that ignore the result keep working unchanged.\n * @throws if the bus has been destroyed, or if the argument is a raw\n * AudioNode that is not a Cacophony-built biquad.\n */\n addFilter(arg: BiquadFilterNode | CacophonyEffect | AudioNode): Promise<AudioNode> {\n if (!isCacophonyBuiltBiquad(arg, this._context) && !isCacophonyEffect(arg)) {\n throw new Error(\n \"Bus.addFilter rejects raw AudioNodes. Wrap with cacophony.shareEffect(node) or a CacophonyEffect to make the shared-state intent explicit.\",\n );\n }\n\n let reservation: symbol;\n try {\n this._throwIfDestroyed();\n reservation = this._effectChain.reserve();\n } catch (error) {\n return Promise.reject(error);\n }\n\n let built: BuiltEffect | Promise<BuiltEffect>;\n try {\n built = isCacophonyBuiltBiquad(arg, this._context) ? arg : arg.build(this._context);\n } catch (error) {\n this._effectChain.cancel(reservation);\n return Promise.reject(error);\n }\n\n if (built instanceof Promise || (typeof built === \"object\" && built !== null && \"then\" in built)) {\n return Promise.resolve(built).then(\n (resolved) => this._effectChain.resolve(reservation, resolved),\n (error) => {\n this._effectChain.cancel(reservation);\n throw error;\n },\n );\n }\n\n try {\n return Promise.resolve(this._effectChain.resolve(reservation, built));\n } catch (error) {\n this._effectChain.cancel(reservation);\n return Promise.reject(error);\n }\n }\n\n /**\n * Remove a filter node from the bus's chain. The node must have been added\n * via {@link addFilter}; the same object identity is used to match.\n *\n * @throws if the bus has been destroyed or if the node was never added.\n */\n removeFilter(node: AudioNode): void {\n this._throwIfDestroyed();\n this._effectChain.remove(node);\n }\n\n /**\n * Reorder the existing filter chain. `nodes` must be a PERMUTATION of the\n * current filters — the same set of node objects (matched by identity), the\n * same length, with no duplicates — just in a new order. Because the owned\n * {@link EffectChain} reconciles incrementally, only the edges that actually\n * move are reconnected; unchanged edges are left untouched.\n *\n * @throws if the bus has been destroyed, or if `nodes` is not a permutation\n * of the current filters.\n */\n setFilterOrder(nodes: readonly AudioNode[]): void {\n this._throwIfDestroyed();\n this._effectChain.setOrder(nodes);\n }\n\n /**\n * Bypass (or un-bypass) a filter without removing it from the chain. A\n * bypassed filter stays in {@link filters} — its order, identity, and live\n * AudioParams are preserved (so an automation target survives a bypass) — but\n * it is skipped in the audible series chain: the signal is wired around it.\n * Un-bypassing wires it back in at its original position.\n *\n * The reconnect goes through the incremental {@link EffectChain}, so only the\n * seam around `node` is touched — the rest of the chain is left connected.\n *\n * @param node A filter node currently on this bus (from {@link addFilter} or\n * {@link filters}).\n * @param bypassed `true` to skip the node, `false` to wire it back in. A no-op\n * if the node is already in the requested state.\n * @throws if the bus has been destroyed, or if `node` was never added to this\n * bus.\n */\n setFilterBypassed(node: AudioNode, bypassed: boolean): void {\n this._throwIfDestroyed();\n this._effectChain.setBypassed(node, bypassed);\n }\n\n /**\n * Whether `node` is currently bypassed (skipped in the audible chain). Returns\n * `false` for nodes that were never added to this bus.\n */\n isFilterBypassed(node: AudioNode): boolean {\n return this._effectChain.isBypassed(node);\n }\n\n /**\n * Ramp an effect node's parameter to a target value over time. This is the\n * uniform automation handle for filter-chain effects: pass a node obtained\n * from {@link addFilter} (or the {@link filters} getter) and the name of the\n * parameter to drive.\n *\n * Parameter resolution:\n * - If `node` exposes a worklet-style `parameters` AudioParamMap, the param\n * is resolved via `parameters.get(paramName)` (e.g. a worklet effect's\n * named params).\n * - Otherwise, if `node[paramName]` is itself an AudioParam (native nodes\n * such as a biquad expose `.frequency` / `.Q` / `.gain` directly), that is\n * used.\n *\n * Ramp shape (mirrors the codebase fade convention): the target time base is\n * `node.context.currentTime`. With no `duration` (or `duration <= 0`) the\n * value is set immediately via `setValueAtTime(value, now)`. Otherwise the\n * start is pinned with `setValueAtTime(param.value, now)` and the value ramps\n * to `now + duration / 1000` (milliseconds) using `linearRampToValueAtTime`\n * (default) or `exponentialRampToValueAtTime` when `type` is `\"exponential\"`\n * (an exponential target of 0 is floored to 0.0001, matching `fadeTo`).\n *\n * Automation degrades gracefully: if `node` is not on this bus, or the\n * parameter cannot be resolved to an AudioParam, a warning is logged and the\n * call is a no-op. The only condition that throws is a destroyed bus.\n *\n * @param node A filter node currently on this bus (from {@link addFilter}).\n * @param paramName The name of the parameter to automate.\n * @param value The target value.\n * @param options.duration Ramp duration in milliseconds. Absent/`<= 0` sets\n * the value immediately.\n * @param options.type Ramp curve, `\"linear\"` (default) or `\"exponential\"`.\n * @throws if the bus has been destroyed.\n */\n rampFilterParam(\n node: AudioNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n this._throwIfDestroyed();\n this._effectChain.rampParam(node, paramName, value, options);\n }\n\n /**\n * Connect this bus's output to another bus or to a raw AudioNode.\n *\n * If `gain` is omitted or equal to 1, connect directly (output →\n * targetInput). If `gain` is provided, allocate an internal GainNode for\n * per-edge attenuation: output → sendGain → targetInput. The sendGain is\n * tracked so {@link disconnect} can tear it down cleanly.\n *\n * Re-connecting a target that is already wired is a no-op for the direct\n * case; for a gained connection, the existing sendGain's `gain.value` is\n * updated in place (no new edge is allocated).\n *\n * @throws if the bus has been destroyed.\n */\n connect(target: BusConnectionTarget, gain?: number): void {\n this._throwIfDestroyed();\n const targetNode = target instanceof Bus ? target.input : target;\n if (gain === undefined || gain === 1) {\n if (this._directConnections.has(target)) {\n return;\n }\n // If a gained connection exists, tear it down first so the connection\n // mode is unambiguous.\n const existingSend = this._sendGains.get(target);\n if (existingSend) {\n try {\n this.output.disconnect(existingSend);\n } catch {}\n try {\n existingSend.disconnect();\n } catch {}\n this._sendGains.delete(target);\n }\n this.output.connect(targetNode);\n this._directConnections.add(target);\n return;\n }\n const existingSend = this._sendGains.get(target);\n if (existingSend) {\n existingSend.gain.value = gain;\n return;\n }\n // Tear down any direct edge first (so the routing topology to this\n // target stays unambiguous).\n if (this._directConnections.has(target)) {\n try {\n this.output.disconnect(targetNode);\n } catch {}\n this._directConnections.delete(target);\n }\n const sendGain = this._context.createGain();\n sendGain.gain.value = gain;\n this.output.connect(sendGain);\n sendGain.connect(targetNode);\n this._sendGains.set(target, sendGain);\n }\n\n /**\n * Disconnect this bus's output from a target previously connected with\n * {@link connect}. Tears down the allocated sendGain (if any). No-op if\n * the target was never connected.\n *\n * @throws if the bus has been destroyed.\n */\n disconnect(target: BusConnectionTarget): void {\n this._throwIfDestroyed();\n const targetNode = target instanceof Bus ? target.input : target;\n const sendGain = this._sendGains.get(target);\n if (sendGain) {\n try {\n this.output.disconnect(sendGain);\n } catch {}\n try {\n sendGain.disconnect();\n } catch {}\n this._sendGains.delete(target);\n }\n if (this._directConnections.has(target)) {\n try {\n this.output.disconnect(targetNode);\n } catch {}\n this._directConnections.delete(target);\n }\n }\n\n /**\n * Register an inbound source that routes to this bus (primary and/or send).\n * Called by the source when it begins routing here. Idempotent (Set). Safe\n * to call without a destroyed guard — registration during normal routing\n * must never throw — but a destroyed bus has nothing to drain, so this\n * early-returns once destroyed.\n *\n * @internal\n */\n _registerRoutedSource(source: BusRoutedSource): void {\n if (this._destroyed) {\n return;\n }\n this._routedSources.add(source);\n }\n\n /**\n * Unregister an inbound source (it rerouted away or was cleaned up). No-op\n * if the source was never registered.\n *\n * @internal\n */\n _unregisterRoutedSource(source: BusRoutedSource): void {\n this._routedSources.delete(source);\n }\n\n /**\n * Move every source currently routed to this bus onto `target`, so live\n * sounds keep feeding a live bus instead of the dead `input` after this bus\n * is torn down. Each registered source's {@link BusRoutedSource._onBusDrained}\n * reroutes its primary route and/or the send that targeted this bus.\n *\n * @throws if this bus has been destroyed, or if `target` is this bus.\n */\n drainTo(target: Bus): void {\n this._throwIfDestroyed();\n if (target === this) {\n throw new Error(\"Cannot drain a bus to itself\");\n }\n for (const source of [...this._routedSources]) {\n source._onBusDrained(this, target);\n }\n this._routedSources.clear();\n }\n\n /**\n * Tear down the bus — disconnects input, output, every send-gain, every\n * filter, then deregisters from the owner Cacophony's named-bus map.\n * Subsequent `addFilter`/`removeFilter`/`connect`/`disconnect` calls throw.\n *\n * If `options.drainTo` is provided, every source routed to this bus is first\n * rerouted onto that bus (via {@link drainTo}) so live sounds keep playing\n * through a live bus. With no options the default teardown is unchanged:\n * sounds routed to the destroyed bus fall back to master on their next\n * playback (the routeTo machinery checks `destroyed` at preplay).\n */\n destroy(options?: { drainTo?: Bus }): void {\n if (!this._destroyable) {\n throw new Error(\"The master bus cannot be destroyed\");\n }\n if (this._destroyed) {\n return;\n }\n if (options?.drainTo) {\n this.drainTo(options.drainTo);\n }\n this._destroyed = true;\n // Tear down all outgoing send-gain allocations.\n for (const [, sendGain] of this._sendGains) {\n try {\n sendGain.disconnect();\n } catch {}\n }\n this._sendGains.clear();\n this._directConnections.clear();\n // Disconnect only this bus's internal chain edges. Filter nodes may be\n // shared with other buses, so broad node.disconnect() would steal their\n // routes.\n this._effectChain.destroy();\n try {\n this.input.disconnect();\n } catch {}\n try {\n this.output.disconnect();\n } catch {}\n this._onDestroy?.();\n }\n\n private _throwIfDestroyed(): void {\n if (this._destroyed) {\n throw new Error(`Bus '${this.name ?? \"<anonymous>\"}' has been destroyed`);\n }\n }\n}\n","import type { BaseContext } from \"./context\";\nimport type { AudioEventCallbacks, CacheHitEvent, LoadingProgressEvent } from \"./events\";\n\n/**\n * Subset of {@link AudioEventCallbacks} relevant to {@link AudioCache} public API.\n * Lets callers opt in to any combination of loading/cache events without\n * having to import the full union.\n */\nexport type CacheCallbacks = Pick<\n AudioEventCallbacks,\n | \"onLoadingStart\"\n | \"onLoadingProgress\"\n | \"onLoadingComplete\"\n | \"onLoadingError\"\n | \"onCacheHit\"\n | \"onCacheMiss\"\n | \"onCacheError\"\n>;\n\n/**\n * Coerce an unknown caught value into an `Error`. Use at the boundary\n * of `catch (error: unknown)` blocks where downstream typing requires a real\n * `Error` (e.g. `LoadingErrorEvent.error`, `CacheErrorEvent.error`).\n */\nfunction toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\n/**\n * Build the metadata cache key for a given URL. Centralised so the\n * \":meta\" suffix never drifts across writers/readers/cleanup paths.\n */\nfunction metaKey(url: string): string {\n return `${url}:meta`;\n}\n\nclass ByteBoundedLRUCache<K, V> {\n private maxBytes: number;\n private cachedBytes = 0;\n private cache: Map<K, { value: V; bytes: number }>;\n private estimateBytes: (value: V) => number;\n\n constructor(maxBytes: number, estimateBytes: (value: V) => number) {\n this.maxBytes = maxBytes;\n this.estimateBytes = estimateBytes;\n this.cache = new Map();\n }\n\n get(key: K): V | undefined {\n const entry = this.cache.get(key);\n if (entry === undefined) return undefined;\n this.cache.delete(key);\n this.cache.set(key, entry);\n return entry.value;\n }\n\n set(key: K, value: V): void {\n const bytes = this.estimateBytes(value);\n\n if (this.cache.has(key)) {\n this.cachedBytes -= this.cache.get(key)?.bytes ?? 0;\n this.cache.delete(key);\n }\n\n if (bytes > this.maxBytes) {\n return;\n }\n\n while (this.cachedBytes + bytes > this.maxBytes) {\n const first = this.cache.keys().next();\n if (first.done) {\n break;\n }\n const firstKey = first.value;\n const oldest = this.cache.get(firstKey);\n this.cachedBytes -= oldest?.bytes ?? 0;\n this.cache.delete(firstKey);\n }\n\n this.cache.set(key, { value, bytes });\n this.cachedBytes += bytes;\n }\n}\n\ninterface CacheMetadata {\n url: string;\n etag?: string;\n lastModified?: string;\n cacheControl?: string;\n timestamp: number;\n}\n\n/**\n * Validator tokens carried in cache metadata, used to build conditional\n * requests (`If-None-Match`, `If-Modified-Since`).\n */\ntype CacheValidators = Pick<CacheMetadata, \"etag\" | \"lastModified\">;\n\nconst DEFAULT_DECODED_BUFFER_CACHE_BYTES = 64 * 1024 * 1024;\nconst BYTES_PER_FLOAT32_SAMPLE = 4;\n\nfunction estimateAudioBufferBytes(audioBuffer: AudioBuffer): number {\n return audioBuffer.length * audioBuffer.numberOfChannels * BYTES_PER_FLOAT32_SAMPLE;\n}\n\n/**\n * Parse the max-age value from a Cache-Control header.\n *\n * Accepts unquoted (`max-age=3600`) and properly-quoted (`max-age=\"3600\"`)\n * forms only; rejects malformed half-quoted variants like `max-age=\"3600`\n * or `max-age=3600\"`.\n *\n * @returns max-age in seconds, or null if not found / malformed\n */\nfunction parseMaxAge(cacheControlHeader: string | undefined): number | null {\n if (!cacheControlHeader) {\n return null;\n }\n // Either both quotes or neither; (?:...) groups the alternatives.\n const match = cacheControlHeader.match(/max-age\\s*=\\s*(?:\"(\\d+)\"|(\\d+))/i);\n if (!match) return null;\n const captured = match[1] ?? match[2];\n if (captured === undefined) return null;\n const parsed = Number.parseInt(captured, 10);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Check if Cache-Control header contains directives that require revalidation\n * @param cacheControlHeader - The Cache-Control header value\n * @returns true if revalidation is required regardless of age\n */\nfunction requiresRevalidation(cacheControlHeader: string | undefined): boolean {\n if (!cacheControlHeader) {\n return false;\n }\n return /(?:^|,)\\s*(no-cache|no-store|must-revalidate)\\s*(?:,|$)/i.test(cacheControlHeader);\n}\n\nfunction getNetworkErrorType(error: unknown): \"network\" | \"abort\" | \"unknown\" {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return \"abort\";\n }\n if (error instanceof Error) {\n return \"network\";\n }\n return \"unknown\";\n}\n\n/**\n * Parse a `data:` URL into its mime type and decoded bytes.\n * Returns `null` if the URL is malformed (no comma, undecodable base64, etc.)\n * so callers can fire a typed `onLoadingError` instead of crashing.\n */\nfunction parseDataUrl(url: string): { mime: string; bytes: Uint8Array } | null {\n const commaIndex = url.indexOf(\",\");\n if (commaIndex < 0) return null;\n const header = url.slice(5, commaIndex); // strip \"data:\"\n const payload = url.slice(commaIndex + 1);\n const isBase64 = /;base64$/i.test(header);\n const mime = (isBase64 ? header.replace(/;base64$/i, \"\") : header) || \"text/plain\";\n try {\n if (isBase64) {\n const binary = atob(payload);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return { mime, bytes };\n }\n const decoded = decodeURIComponent(payload);\n const bytes = new Uint8Array(decoded.length);\n for (let i = 0; i < decoded.length; i++) {\n bytes[i] = decoded.charCodeAt(i);\n }\n return { mime, bytes };\n } catch {\n return null;\n }\n}\n\nexport interface ICache {\n getAudioBuffer(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n callbacks?: CacheCallbacks,\n ): Promise<AudioBuffer>;\n clearMemoryCache(): void;\n}\n\ntype PendingProgressCallbacks = Pick<CacheCallbacks, \"onLoadingProgress\">;\n\ninterface PendingAudioRequest {\n promise: Promise<AudioBuffer>;\n controller: AbortController;\n callers: number;\n}\n\n/**\n * AudioCache provides efficient caching of audio resources using HTTP caching standards.\n *\n * Features:\n * - Three-layer caching: Memory (LRU) → Browser Cache API → Network\n * - HTTP conditional requests with ETag and Last-Modified support\n * - Robust error handling with cache inconsistency recovery\n *\n * Caching Strategy:\n * - Always makes conditional requests when validation tokens (ETag/Last-Modified) are available\n * - Uses TTL as fallback only when no validation tokens exist\n * - Conditional requests are lightweight (304 responses have no body)\n *\n * @example\n * ```typescript\n * const cache = new AudioCache();\n * const audioBuffer = await cache.getAudioBuffer(audioContext, 'audio.mp3');\n *\n * // Optional: Configure TTL for when no validation tokens exist\n * AudioCache.setCacheExpirationTime(60 * 60 * 1000); // 1 hour\n * ```\n */\nexport class AudioCache implements ICache {\n private pendingRequests = new WeakMap<BaseContext, Map<string, PendingAudioRequest>>();\n private pendingCallbacks = new WeakMap<BaseContext, Map<string, PendingProgressCallbacks[]>>();\n private decodedBuffers = new WeakMap<BaseContext, ByteBoundedLRUCache<string, AudioBuffer>>();\n private static cacheExpirationTime: number = 24 * 60 * 60 * 1000; // 24 hours in milliseconds\n\n public static setCacheExpirationTime(time: number): void {\n AudioCache.cacheExpirationTime = time;\n }\n\n /**\n * Whether the browser Cache API (`caches`) global is available in the\n * current runtime. Uses `typeof` so it never throws when the global is\n * entirely absent (e.g. Node). When this returns `false`, the URL path\n * degrades to fetch + decode (still using the in-memory LRU) instead of\n * touching the persistent Cache API.\n */\n private static isCacheApiAvailable(): boolean {\n return typeof caches !== \"undefined\" && caches != null;\n }\n\n private static async openCache(): Promise<Cache> {\n try {\n return await caches.open(\"audio-cache\");\n } catch (error) {\n console.error(\"Failed to open cache:\", error);\n throw error;\n }\n }\n\n /**\n * Calls all registered callbacks for a specific event type on a URL.\n * Generic over the callback name so the payload type is checked against\n * the canonical {@link CacheCallbacks} shape rather than `any`.\n */\n private static callAllCallbacks<K extends keyof CacheCallbacks>(\n pendingCallbacks: Map<string, PendingProgressCallbacks[]> | undefined,\n url: string,\n callbackName: K,\n eventData: Parameters<NonNullable<CacheCallbacks[K]>>[0],\n ): void {\n const callbacks = pendingCallbacks?.get(url);\n if (callbacks) {\n callbacks.forEach((callbackSet) => {\n const callback = callbackSet[callbackName as \"onLoadingProgress\"];\n if (callback) {\n try {\n // Cast eventData here: the surrounding generic guarantees the\n // payload type matches the chosen callback name.\n (callback as (e: typeof eventData) => void)(eventData);\n } catch (error) {\n console.error(`Error in ${String(callbackName)} callback:`, error);\n }\n }\n });\n }\n }\n\n private async getOrCreatePendingRequest(\n context: BaseContext,\n url: string,\n createRequest: (signal: AbortSignal) => Promise<AudioBuffer | undefined>,\n signal?: AbortSignal,\n callbacks?: PendingProgressCallbacks,\n ): Promise<AudioBuffer> {\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n let pendingRequests = this.pendingRequests.get(context);\n if (pendingRequests === undefined) {\n pendingRequests = new Map();\n this.pendingRequests.set(context, pendingRequests);\n }\n let pendingCallbacks = this.pendingCallbacks.get(context);\n if (pendingCallbacks === undefined) {\n pendingCallbacks = new Map();\n this.pendingCallbacks.set(context, pendingCallbacks);\n }\n\n // Add callbacks to aggregation if provided\n if (callbacks) {\n const existingCallbacks = pendingCallbacks.get(url) || [];\n existingCallbacks.push(callbacks);\n pendingCallbacks.set(url, existingCallbacks);\n }\n\n let pendingRequest = pendingRequests.get(url);\n if (!pendingRequest) {\n const controller = new AbortController();\n const requestPromise = (async () => {\n try {\n const result = await createRequest(controller.signal);\n if (result === undefined) {\n throw new Error(\"Failed to create audio buffer.\");\n }\n return result;\n } finally {\n if (pendingRequests.get(url) === pendingRequest) {\n pendingRequests.delete(url);\n pendingCallbacks.delete(url);\n }\n }\n })();\n\n pendingRequest = { promise: requestPromise, controller, callers: 0 };\n pendingRequests.set(url, pendingRequest);\n }\n\n pendingRequest.callers++;\n return new Promise((resolve, reject) => {\n let settled = false;\n const release = (aborted: boolean) => {\n if (settled) return;\n settled = true;\n signal?.removeEventListener(\"abort\", handleAbort);\n pendingRequest.callers--;\n\n if (aborted && callbacks) {\n const registeredCallbacks = pendingCallbacks.get(url);\n const callbackIndex = registeredCallbacks?.indexOf(callbacks) ?? -1;\n if (callbackIndex >= 0) {\n registeredCallbacks?.splice(callbackIndex, 1);\n }\n }\n\n if (aborted && pendingRequest.callers === 0 && pendingRequests.get(url) === pendingRequest) {\n pendingRequests.delete(url);\n pendingCallbacks.delete(url);\n pendingRequest.controller.abort();\n }\n };\n const handleAbort = () => {\n release(true);\n reject(new DOMException(\"Operation was aborted\", \"AbortError\"));\n };\n\n signal?.addEventListener(\"abort\", handleAbort, { once: true });\n pendingRequest.promise.then(\n (buffer) => {\n if (settled) return;\n release(false);\n resolve(buffer);\n },\n (error: unknown) => {\n if (settled) return;\n release(false);\n reject(error);\n },\n );\n });\n }\n\n private static async updateMetadata(cache: Cache, url: string, data: Partial<CacheMetadata>): Promise<void> {\n const metadata: CacheMetadata = {\n url,\n timestamp: Date.now(),\n ...data,\n };\n\n await cache.put(\n metaKey(url),\n new Response(JSON.stringify(metadata), {\n headers: { \"Content-Type\": \"application/json\" },\n }),\n );\n }\n\n private static async getBufferFromCache(url: string, cache: Cache): Promise<ArrayBuffer | null> {\n try {\n const response = await cache.match(url);\n if (response?.ok) {\n return await response.arrayBuffer();\n }\n return null;\n } catch (error) {\n console.error(\"Failed to get data from cache:\", error);\n return null;\n }\n }\n\n private async fetchAndCacheBuffer(\n context: BaseContext,\n url: string,\n cache: Cache,\n validators?: CacheValidators,\n signal?: AbortSignal,\n callbacks?: Pick<CacheCallbacks, \"onCacheHit\">,\n ): Promise<ArrayBuffer> {\n const etag = validators?.etag;\n const lastModified = validators?.lastModified;\n const headers = new Headers();\n if (etag) headers.append(\"If-None-Match\", etag);\n if (lastModified) headers.append(\"If-Modified-Since\", lastModified);\n\n console.debug(`[AudioCache] Fetching ${url}`, {\n headers: Object.fromEntries(headers.entries()),\n hasEtag: !!etag,\n hasLastModified: !!lastModified,\n });\n\n const fetchResponse = await fetch(url, { headers, signal });\n\n console.debug(`[AudioCache] Response ${url}`, {\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n etag: fetchResponse.headers?.get(\"ETag\"),\n lastModified: fetchResponse.headers?.get(\"Last-Modified\"),\n cacheControl: fetchResponse.headers?.get(\"Cache-Control\"),\n });\n\n if (fetchResponse.status === 304) {\n const cachedResponse = await cache.match(url);\n if (cachedResponse) {\n // Emit cache hit for 304 responses\n if (callbacks?.onCacheHit) {\n const event: CacheHitEvent = {\n url,\n cacheType: \"conditional\",\n timestamp: Date.now(),\n };\n callbacks.onCacheHit(event);\n }\n\n // Update metadata timestamp on revalidation\n const timestamp = Date.now();\n const newCacheControl = fetchResponse.headers?.get(\"Cache-Control\");\n await AudioCache.updateMetadata(cache, url, {\n timestamp,\n etag,\n lastModified,\n // Only update cacheControl if present in response, otherwise preserve existing\n ...(newCacheControl ? { cacheControl: newCacheControl } : {}),\n });\n return await cachedResponse.arrayBuffer();\n } else {\n // Cache inconsistency: 304 response but no cached body\n // This can happen if cache was partially corrupted or cleared\n // Fall back to re-fetching without validation headers\n console.warn(`Cache inconsistency detected for ${url}: 304 response but no cached body. Re-fetching.`);\n\n // Re-fetch without validation headers to get fresh content\n const freshResponse = await fetch(url, { signal });\n if (freshResponse.status === 200) {\n const responseClone = freshResponse.clone();\n const newEtag = freshResponse.headers.get(\"ETag\");\n const newLastModified = freshResponse.headers.get(\"Last-Modified\");\n const newCacheControl = freshResponse.headers.get(\"Cache-Control\");\n\n await AudioCache.writeBufferAndMetadata(cache, url, responseClone, {\n timestamp: Date.now(),\n etag: newEtag || undefined,\n lastModified: newLastModified || undefined,\n cacheControl: newCacheControl || undefined,\n });\n\n // Use progress tracking for cache recovery scenario if body exists\n if (freshResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, freshResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n } else {\n // Fallback for mock responses without body (testing scenario)\n return await freshResponse.arrayBuffer();\n }\n } else {\n throw new Error(\n `Failed to fetch resource after cache inconsistency: ${freshResponse.status} ${freshResponse.statusText}`,\n );\n }\n }\n }\n\n if (fetchResponse.status === 200) {\n const responseClone = fetchResponse.clone();\n const newEtag = fetchResponse.headers.get(\"ETag\");\n const newLastModified = fetchResponse.headers.get(\"Last-Modified\");\n const newCacheControl = fetchResponse.headers.get(\"Cache-Control\");\n\n await AudioCache.writeBufferAndMetadata(cache, url, responseClone, {\n timestamp: Date.now(),\n etag: newEtag || undefined,\n lastModified: newLastModified || undefined,\n cacheControl: newCacheControl || undefined,\n });\n }\n\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n // Use progress tracking for the main response if body exists\n if (fetchResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, fetchResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n } else {\n // Fallback for mock responses without body (testing scenario)\n return await fetchResponse.arrayBuffer();\n }\n }\n\n /**\n * Atomically write a response body and its metadata into the cache.\n * On failure, deletes both partial entries (best-effort via `allSettled`)\n * and rethrows the original error.\n */\n private static async writeBufferAndMetadata(\n cache: Cache,\n url: string,\n response: Response,\n metadata: Partial<CacheMetadata>,\n ): Promise<void> {\n try {\n await Promise.all([cache.put(url, response), AudioCache.updateMetadata(cache, url, metadata)]);\n } catch (error) {\n // Clean up partial cache entries on error; both deletes run regardless.\n await Promise.allSettled([cache.delete(url), cache.delete(metaKey(url))]);\n throw error;\n }\n }\n\n /**\n * Creates a ReadableStream wrapper that tracks download progress.\n * Uses the callback aggregation system to emit progress to all registered listeners.\n * Honours `signal` between reads and releases the underlying reader on\n * any exit path (done, error, abort).\n * @param response - The fetch Response object with ReadableStream body\n * @param url - URL being downloaded (for progress event data and callback lookup)\n * @param signal - Optional AbortSignal observed at chunk boundaries\n * @returns Object containing the progress-tracking stream and total size\n */\n private createProgressTrackingStream(\n context: BaseContext,\n response: Response,\n url: string,\n signal?: AbortSignal,\n ): { stream: ReadableStream<Uint8Array>; total: number | null } {\n // Extract Content-Length from response headers\n const contentLengthHeader = response.headers.get(\"content-length\");\n const total = contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : null;\n\n let loaded = 0;\n\n if (!response.body) {\n // Fallback for responses without body - shouldn't happen for audio files\n throw new Error(\"Response body is null\");\n }\n\n const reader = response.body.getReader();\n const pendingCallbacks = this.pendingCallbacks.get(context);\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n while (true) {\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) {\n // Emit final progress event at 100%\n AudioCache.callAllCallbacks(pendingCallbacks, url, \"onLoadingProgress\", {\n url,\n loaded,\n total,\n progress: total ? 1 : -1, // 100% if total known, -1 if unknown\n timestamp: Date.now(),\n } satisfies LoadingProgressEvent);\n controller.close();\n return;\n }\n\n if (value) {\n loaded += value.byteLength;\n\n // Emit progress event\n const progress = total ? loaded / total : -1;\n AudioCache.callAllCallbacks(pendingCallbacks, url, \"onLoadingProgress\", {\n url,\n loaded,\n total,\n progress,\n timestamp: Date.now(),\n } satisfies LoadingProgressEvent);\n\n controller.enqueue(value);\n }\n }\n } catch (error) {\n controller.error(error);\n } finally {\n reader.releaseLock();\n }\n },\n\n cancel(reason) {\n // Clean up reader when stream is cancelled\n return reader.cancel(reason);\n },\n });\n\n return { stream, total };\n }\n\n /**\n * Collects all chunks from a ReadableStream into a single ArrayBuffer\n * @param stream - The ReadableStream to collect from\n * @returns Promise that resolves to the complete ArrayBuffer\n */\n private static async collectStreamToArrayBuffer(\n stream: ReadableStream<Uint8Array>,\n knownLength?: number,\n ): Promise<ArrayBuffer> {\n const reader = stream.getReader();\n\n try {\n if (knownLength !== undefined && knownLength > 0) {\n // Pre-allocation path: we know the exact size\n const result = new ArrayBuffer(knownLength);\n const uint8View = new Uint8Array(result);\n let offset = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n if (value) {\n uint8View.set(value, offset);\n offset += value.byteLength;\n }\n }\n\n return result;\n } else {\n // Exponential growth path: unknown size\n let buffer = new Uint8Array(8192); // Start with 8KB\n let totalLength = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n if (value) {\n // Grow buffer if needed\n if (totalLength + value.byteLength > buffer.length) {\n const newSize = Math.max(buffer.length * 2, totalLength + value.byteLength);\n const newBuffer = new Uint8Array(newSize);\n newBuffer.set(buffer.subarray(0, totalLength));\n buffer = newBuffer;\n }\n\n buffer.set(value, totalLength);\n totalLength += value.byteLength;\n }\n }\n\n // Return exact-sized ArrayBuffer\n return buffer.slice(0, totalLength).buffer;\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Fetch + decode an audio URL WITHOUT touching the persistent Cache API.\n * Used when the Cache API global (`caches`) is unavailable (e.g. Node).\n *\n * Mirrors the network branch of {@link fetchAndCacheBuffer}/{@link getAudioBuffer}\n * but never reads from or writes to `caches`. Still:\n * - uses the in-memory decoded-buffer LRU (write happens in {@link getAudioBuffer}),\n * - emits progress callbacks via {@link createProgressTrackingStream},\n * - is deduped through {@link getOrCreatePendingRequest} by the caller.\n */\n private async fetchAndDecodeWithoutCache(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n ): Promise<ArrayBuffer> {\n const fetchResponse = await fetch(url, { signal });\n\n if (fetchResponse.status !== 200 && !fetchResponse.ok) {\n throw new Error(`Failed to fetch resource: ${fetchResponse.status} ${fetchResponse.statusText}`);\n }\n\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n // Use progress tracking for the response if body exists\n if (fetchResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, fetchResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n }\n // Fallback for mock responses without body (testing scenario)\n return await fetchResponse.arrayBuffer();\n }\n\n /**\n * Run the shared network-result pipeline: fetch bytes, decode them, emit\n * loading callbacks, and retain the decoded buffer in the in-memory LRU.\n * The caller supplies the fetch strategy so persistent-cache and degraded\n * runtimes share this plumbing without changing their network behavior.\n */\n private async fetchDecodeAndCache(\n context: BaseContext,\n url: string,\n decodedBuffers: ByteBoundedLRUCache<string, AudioBuffer>,\n fetchBuffer: () => Promise<ArrayBuffer>,\n callbacks?: Pick<CacheCallbacks, \"onLoadingComplete\" | \"onLoadingError\" | \"onCacheError\">,\n reportCacheError = false,\n ): Promise<AudioBuffer> {\n try {\n const arrayBuffer = await fetchBuffer();\n let audioBuffer: AudioBuffer;\n try {\n audioBuffer = await AudioCache.decodeAudioData(context, arrayBuffer);\n } catch (error) {\n callbacks?.onLoadingError?.({\n url,\n error: toError(error),\n errorType: \"decode\",\n timestamp: Date.now(),\n });\n throw error;\n }\n\n callbacks?.onLoadingComplete?.({\n url,\n duration: audioBuffer.duration,\n size: arrayBuffer.byteLength,\n timestamp: Date.now(),\n });\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n } catch (error) {\n callbacks?.onLoadingError?.({\n url,\n error: toError(error),\n errorType: getNetworkErrorType(error),\n timestamp: Date.now(),\n });\n if (reportCacheError) {\n callbacks?.onCacheError?.({\n url,\n error: toError(error),\n operation: \"get\",\n timestamp: Date.now(),\n });\n }\n throw error;\n }\n }\n\n private static async decodeAudioData(context: BaseContext, arrayBuffer: ArrayBuffer): Promise<AudioBuffer> {\n try {\n return await context.decodeAudioData(arrayBuffer);\n } catch (error) {\n console.error(\"Failed to decode audio data:\", error);\n throw error;\n }\n }\n\n private static async getMetadataFromCache(url: string, cache: Cache): Promise<CacheMetadata | null> {\n try {\n const metaResponse = await cache.match(metaKey(url));\n if (metaResponse?.ok) {\n return await metaResponse.json();\n }\n return null;\n } catch (error) {\n console.error(\"Failed to get metadata from cache:\", error);\n return null;\n }\n }\n\n /**\n * Get an AudioBuffer for the specified URL, using intelligent caching strategies.\n *\n * Caching Flow:\n * 1. Check memory cache (LRU) for decoded AudioBuffer\n * 2. Check persistent cache for raw ArrayBuffer and metadata\n * 3. Make conditional HTTP request if validation tokens available\n * 4. Decode audio data and cache at all levels\n *\n * The cache prioritizes HTTP conditional requests (ETag/Last-Modified) over TTL\n * to ensure content freshness while maintaining performance through 304 responses.\n *\n * @param context - AudioContext for decoding audio data\n * @param url - URL of the audio resource to fetch\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to decoded AudioBuffer\n * @throws Error if audio cannot be fetched or decoded\n */\n public async getAudioBuffer(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n callbacks?: CacheCallbacks,\n ): Promise<AudioBuffer> {\n let decodedBuffers = this.decodedBuffers.get(context);\n if (decodedBuffers === undefined) {\n decodedBuffers = new ByteBoundedLRUCache<string, AudioBuffer>(\n DEFAULT_DECODED_BUFFER_CACHE_BYTES,\n estimateAudioBufferBytes,\n );\n this.decodedBuffers.set(context, decodedBuffers);\n }\n\n // Call loading start callback\n if (callbacks?.onLoadingStart) {\n callbacks.onLoadingStart({\n url,\n timestamp: Date.now(),\n });\n }\n\n // Check if the decoded buffer is already available in memory cache.\n // Single get + narrow avoids the has/get/`!` race.\n const memoryHit = decodedBuffers.get(url);\n if (memoryHit !== undefined) {\n if (callbacks?.onCacheHit) {\n callbacks.onCacheHit({\n url,\n cacheType: \"memory\",\n timestamp: Date.now(),\n });\n }\n return memoryHit;\n }\n\n // handle data: urls\n if (url.startsWith(\"data:\")) {\n const parsed = parseDataUrl(url);\n if (parsed === null) {\n const error = new Error(`Malformed data: URL: ${url.slice(0, 32)}…`);\n if (callbacks?.onLoadingError) {\n callbacks.onLoadingError({\n url,\n error,\n errorType: \"decode\",\n timestamp: Date.now(),\n });\n }\n throw error;\n }\n const audioBuffer = await AudioCache.decodeAudioData(context, parsed.bytes.buffer as ArrayBuffer);\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n }\n\n // When the browser Cache API is unavailable (e.g. Node), transparently\n // degrade to a fetch-only path: no persistent cache read/write, no error\n // log — still using the in-memory LRU, pending-request dedup, and progress\n // callbacks. The available-cache path below is byte-for-byte unchanged.\n if (!AudioCache.isCacheApiAvailable()) {\n return this.getOrCreatePendingRequest(\n context,\n url,\n async (requestSignal) => {\n // No persistent cache, so this is always a miss.\n if (callbacks?.onCacheMiss) {\n callbacks.onCacheMiss({\n url,\n reason: \"not-found\",\n timestamp: Date.now(),\n });\n }\n\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () => this.fetchAndDecodeWithoutCache(context, url, requestSignal),\n callbacks,\n );\n },\n signal,\n { onLoadingProgress: callbacks?.onLoadingProgress },\n );\n }\n\n const cache = await AudioCache.openCache();\n\n const metadata = await AudioCache.getMetadataFromCache(url, cache);\n\n // Determine if we should make a network request\n // This logic implements HTTP caching best practices:\n // 1. Check Cache-Control freshness first (RFC-compliant behavior)\n // 2. If stale, use validation headers for conditional requests\n // 3. Fall back to TTL when no validation tokens exist\n const shouldFetch = (() => {\n if (!metadata) {\n return true; // Must fetch if nothing is cached\n }\n\n // Check for directives that require revalidation\n if (requiresRevalidation(metadata.cacheControl)) {\n return true; // Must revalidate due to no-cache, no-store, or must-revalidate\n }\n\n // Check Cache-Control freshness\n const maxAge = parseMaxAge(metadata.cacheControl);\n if (maxAge !== null) {\n const age = (Date.now() - metadata.timestamp) / 1000;\n if (maxAge > 0 && age < maxAge) {\n return false; // Fresh content, serve from cache\n }\n // If max-age=0 or content is stale, proceed to validation\n }\n\n // Content is stale (or max-age=0), check if we can revalidate\n if (metadata.etag || metadata.lastModified) {\n return true; // Stale but can be validated with conditional request\n }\n\n // No validation headers available, fall back to TTL\n return Date.now() - metadata.timestamp > AudioCache.cacheExpirationTime;\n })();\n\n return this.getOrCreatePendingRequest(\n context,\n url,\n async (requestSignal) => {\n if (shouldFetch) {\n // Cache miss - need to fetch from network\n if (callbacks?.onCacheMiss) {\n callbacks.onCacheMiss({\n url,\n reason: metadata ? \"expired\" : \"not-found\",\n timestamp: Date.now(),\n });\n }\n\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () =>\n this.fetchAndCacheBuffer(\n context,\n url,\n cache,\n { etag: metadata?.etag, lastModified: metadata?.lastModified },\n requestSignal,\n { onCacheHit: callbacks?.onCacheHit },\n ),\n callbacks,\n true,\n );\n } else {\n // Content should be fresh in cache\n const cachedBuffer = await AudioCache.getBufferFromCache(url, cache);\n if (cachedBuffer) {\n // Cache hit from browser cache\n if (callbacks?.onCacheHit) {\n callbacks.onCacheHit({\n url,\n cacheType: \"browser\",\n timestamp: Date.now(),\n });\n }\n\n const audioBuffer = await AudioCache.decodeAudioData(context, cachedBuffer);\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n } else {\n // Cache inconsistency - metadata exists but body is missing\n if (callbacks?.onCacheError) {\n callbacks.onCacheError({\n url,\n error: new Error(\"Cache inconsistency: metadata exists but body is missing\"),\n operation: \"get\",\n timestamp: Date.now(),\n });\n }\n\n // Fallback to network if body missing but metadata is fresh\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () =>\n this.fetchAndCacheBuffer(\n context,\n url,\n cache,\n { etag: metadata?.etag, lastModified: metadata?.lastModified },\n requestSignal,\n { onCacheHit: callbacks?.onCacheHit },\n ),\n callbacks,\n );\n }\n }\n },\n signal,\n { onLoadingProgress: callbacks?.onLoadingProgress },\n );\n }\n\n public clearMemoryCache(): void {\n this.decodedBuffers = new WeakMap();\n this.pendingRequests = new WeakMap();\n this.pendingCallbacks = new WeakMap();\n }\n}\n","export default \"data:audio/wav;base64,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\"","import type { BaseContext } from \"./context\";\n\n/**\n * Autoplay unlock — Howler-parity automatic AudioContext resume on first\n * user gesture.\n *\n * On mobile browsers (especially iOS Safari) an `AudioContext` constructed\n * before any user interaction is created in `suspended` state. It will not\n * produce sound until BOTH:\n *\n * 1. `context.resume()` is called, AND\n * 2. an `AudioBufferSourceNode` is created and started inside the user\n * gesture's call stack (the iOS \"primer\").\n *\n * Many callers do (1) but not (2), and hear silence with no obvious cause.\n * This module watches the context for suspension and arms `touchend` / `click`\n * / `keydown` listeners on `document.body` (or `document` as fallback). A\n * gesture resumes the context, plays a 1-sample silent primer inside the\n * gesture call stack, removes all three listeners, and calls the `onUnlock`\n * callback. If the context suspends again, the listeners are re-armed.\n *\n * Design adapted (not copied) from Howler.js `_unlockAudio` — MIT licensed.\n */\n\nconst UNLOCK_EVENT_TYPES: ReadonlyArray<\"touchend\" | \"click\" | \"keydown\"> = [\"touchend\", \"click\", \"keydown\"];\nconst GESTURE_LISTENER_OPTIONS = { capture: true, passive: true } as const;\n\n/**\n * A `Document`-like type with the methods we need. Avoids a hard dependency\n * on DOM lib types so the package keeps building when `lib: [\"dom\"]` is off.\n */\ninterface DocumentLike {\n body?: EventTargetLike | null;\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\ninterface EventTargetLike {\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\n/**\n * Options for `installAutoplayUnlock`.\n */\nexport interface AutoplayUnlockOptions {\n /**\n * The audio context that should be resumed and primed.\n */\n context: BaseContext;\n\n /**\n * Called after the context has been resumed and the primer buffer has been\n * started. Used by `Cacophony` to emit the public `unlock` event.\n * Errors thrown by the callback are caught and logged so a faulty listener\n * cannot break the unlock flow.\n */\n onUnlock: () => void;\n}\n\n/**\n * Watch for context suspension and install unlock listeners while needed.\n * Returns a cleanup function that removes both the context watcher and any\n * armed gesture listeners (e.g. when a `Cacophony` instance is torn down).\n *\n * If the environment is non-browser (`document` is undefined), this is a\n * no-op and returns a no-op cleanup.\n *\n * @internal\n */\nexport function installAutoplayUnlock(opts: AutoplayUnlockOptions): () => void {\n const { context, onUnlock } = opts;\n\n // Server-side / non-browser: nothing to do.\n if (typeof document === \"undefined\") {\n return () => {};\n }\n\n // `BaseContext.resume` is optional. If this context has no resume() we\n // cannot actually unlock it — installing gesture listeners would be noise,\n // and firing the `unlock` event without a fulfilled resume() would violate\n // the contract (caller would see `unlock` while `Cacophony.locked` stays\n // true, since `locked` is derived from `context.state === \"suspended\"`).\n // Treat the absence as \"this context isn't ours to unlock\" and bail.\n const resume = (context as unknown as { resume?: () => Promise<void> }).resume;\n if (typeof resume !== \"function\") {\n return () => {};\n }\n\n const doc = document as unknown as DocumentLike;\n const target: EventTargetLike = (doc.body as EventTargetLike | null | undefined) ?? doc;\n const observableContext = context as unknown as {\n state?: string;\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n };\n\n let armed = false;\n let resumeInFlight = false;\n let disposed = false;\n\n const removeAll = () => {\n if (!armed) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.removeEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = false;\n };\n\n const handler = () => {\n if (disposed || resumeInFlight) return;\n resumeInFlight = true;\n\n // Remove listeners FIRST so a re-entrant gesture cannot re-trigger us\n // while resume() is in flight.\n removeAll();\n\n // Play the iOS primer INSIDE the gesture call stack. iOS requires that\n // an AudioBufferSourceNode be created and started synchronously inside\n // the gesture handler for the context to truly unlock — calling\n // resume() alone is not sufficient. A 1-sample silent buffer is the\n // minimum that satisfies the requirement and is inaudible.\n try {\n // BaseContext doesn't declare createBuffer because Cacophony's normal\n // code path always feeds an AudioBuffer constructed externally; the\n // primer needs to construct a 1-sample buffer in-place, so we widen\n // the type locally here. Both AudioContext and OfflineAudioContext\n // expose createBuffer.\n const ctxWithCreateBuffer = context as unknown as {\n createBuffer(numberOfChannels: number, length: number, sampleRate: number): unknown;\n };\n const buffer = ctxWithCreateBuffer.createBuffer(1, 1, context.sampleRate);\n const source = context.createBufferSource();\n source.buffer = buffer as any;\n source.connect(context.destination);\n // The load-bearing iOS pattern is start(0); stop(0) on a silent buffer\n // INSIDE the gesture call stack. A 1-sample non-looping buffer would\n // end on its own, but the pair is what the platform contract names.\n source.start(0);\n source.stop(0);\n } catch (err) {\n // Primer failure should not block the rest of the unlock; the\n // context.resume() call below is still useful on platforms that do\n // not require the primer.\n console.warn(\"[cacophony/autoplayUnlock] primer failed:\", err);\n }\n\n // Resume the context, then emit unlock. The `unlock` event signals that\n // audio is actually playable, so we must wait for resume() to fulfill\n // before emitting. On rejection we surface the error and do NOT emit\n // unlock — a failed resume is not an unlock. `resume` is guaranteed to\n // be a function here (guarded at install time above).\n resume.call(context).then(\n () => {\n resumeInFlight = false;\n if (disposed) return;\n try {\n onUnlock();\n } catch (err) {\n console.error(\"[cacophony/autoplayUnlock] onUnlock callback threw:\", err);\n }\n },\n (err: unknown) => {\n resumeInFlight = false;\n if (disposed) return;\n console.warn(\"[cacophony/autoplayUnlock] resume failed:\", err);\n syncGestureListeners();\n },\n );\n };\n\n const armAll = () => {\n if (disposed || armed || resumeInFlight) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.addEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = true;\n };\n\n function syncGestureListeners(): void {\n if (observableContext.state === \"suspended\") {\n armAll();\n } else {\n removeAll();\n }\n }\n\n const handleStateChange = () => {\n if (!disposed) syncGestureListeners();\n };\n\n observableContext.addEventListener?.(\"statechange\", handleStateChange);\n syncGestureListeners();\n\n return () => {\n if (disposed) return;\n disposed = true;\n removeAll();\n observableContext.removeEventListener?.(\"statechange\", handleStateChange);\n };\n}\n","type EventMap = Record<string, unknown>;\ntype EventKey<T extends EventMap> = string & keyof T;\ntype EventListener<T> = (params: T) => void | Promise<void>;\n\ntype ListenerEntry<T extends EventMap, K extends keyof T> = {\n fn: EventListener<T[K]>;\n once: boolean;\n};\n\ntype ListenerStore<T extends EventMap> = Partial<{\n [K in keyof T]: Array<ListenerEntry<T, K>>;\n}>;\n\n/**\n * Type-safe event emitter.\n */\nexport class TypedEventEmitter<T extends EventMap> {\n private listeners: ListenerStore<T> = {};\n\n /**\n * Register event listener.\n * @returns Cleanup function\n * @example\n * const cleanup = emitter.on('play', (playback) => console.log(playback));\n * cleanup(); // Remove listener\n */\n on<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): () => void {\n const list = (this.listeners[eventName] ??= []);\n list.push({ fn, once: false });\n return () => this.off(eventName, fn);\n }\n\n /**\n * Register one-time event listener.\n * The returned cleanup is idempotent: calling it after the listener has\n * auto-removed (because the event fired) is a safe no-op.\n * @returns Cleanup function\n */\n once<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): () => void {\n const list = (this.listeners[eventName] ??= []);\n list.push({ fn, once: true });\n return () => this.off(eventName, fn);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): void {\n const listeners = this.listeners[eventName];\n if (listeners) {\n this.listeners[eventName] = listeners.filter((listener) => listener.fn !== fn);\n }\n }\n\n /**\n * Emit event synchronously.\n *\n * Listeners fire in registration order against a snapshot taken at the\n * start of the dispatch. Concurrent mutations to `this.listeners[eventName]`\n * during dispatch — including `off()` calls from inside a listener and a\n * re-entrant `emit()` — do not affect which listeners fire for THIS emit.\n *\n * `once` listeners are removed from the stored array BEFORE iteration, so\n * a re-entrant `emit()` does not see them and they cannot fire twice. An\n * in-listener `off()` of a sibling is preserved because removal operates\n * on the current stored array, not on the pre-emit snapshot.\n */\n emit<K extends EventKey<T>>(eventName: K, params: T[K]): void {\n const stored = this.listeners[eventName];\n if (!stored || stored.length === 0) return;\n // Pre-split: keep stays in storage, fire is the dispatch snapshot.\n // Setting storage to `keep` BEFORE dispatch makes once-removal atomic with\n // dispatch (re-entrant emit cannot see a not-yet-fired once listener) and\n // leaves a stable array for in-listener off()/on() to mutate.\n const fire = stored.slice();\n const keep = stored.filter((listener) => !listener.once);\n this.listeners[eventName] = keep;\n for (const listener of fire) {\n listener.fn(params);\n }\n }\n\n /**\n * Emit event asynchronously with error isolation.\n * Listener errors are logged but don't break other listeners.\n *\n * Same snapshot semantics as `emit`: the dispatched listener set is\n * frozen at call time, and `once` listeners are removed from storage\n * before dispatch so a re-entrant emit cannot re-fire them.\n */\n emitAsync<K extends EventKey<T>>(eventName: K, params: T[K]): Promise<void> {\n const stored = this.listeners[eventName];\n if (!stored || stored.length === 0) return Promise.resolve();\n const fire = stored.slice();\n const keep = stored.filter((listener) => !listener.once);\n this.listeners[eventName] = keep;\n const promises = fire.map((listener) =>\n Promise.resolve()\n .then(() => listener.fn(params))\n .catch((error) => console.error(`Error in listener for event ${eventName}:`, error)),\n );\n return Promise.all(promises).then(() => {});\n }\n\n /**\n * Remove all event listeners.\n */\n removeAllListeners(): void {\n this.listeners = {};\n }\n}\n","import type { Bus } from \"./bus\";\nimport type { BaseSound, FadeType, LoopCount, PlayOptions, Position } from \"./cacophony\";\nimport type { BiquadFilterNode } from \"./context\";\nimport type { Playback } from \"./playback\";\nimport type { Sound } from \"./sound\";\n\nexport class Group implements BaseSound {\n private _position: Position = [0, 0, 0];\n loopCount: LoopCount = 0;\n private playIndex: number = 0;\n\n constructor(public sounds: Sound[] = []) {}\n\n /**\n * Prepares a random sound from the group for playback.\n * @returns The playback object representing the prepared sound, or undefined if the group is empty.\n */\n preplayRandom(): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n const randomSound = this.randomSound();\n const playbacks = randomSound.preplay();\n return playbacks.length > 0 ? playbacks[0] : undefined;\n }\n\n /**\n * Plays a random sound from the group.\n * @param options - Options for configuring fade behavior.\n * @returns The playback object representing the played sound, or undefined if the group is empty.\n */\n playRandom(options?: PlayOptions): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n return this.randomSound().play(options)[0];\n }\n\n /**\n * Prepares the sounds in the group for playback in a specific order.\n *\n * @param shouldLoop - Indicates whether the sounds should be prepared for looping.\n * @returns The playback object representing the first sound being prepared, or undefined if the group is empty.\n */\n preplayOrdered(shouldLoop: boolean = true): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n if (this.playIndex >= this.sounds.length) {\n return undefined;\n }\n const sound = this.sounds[this.playIndex];\n const playbacks = sound.preplay();\n if (playbacks.length === 0) {\n return undefined;\n }\n this.playIndex = (this.playIndex + 1) % this.sounds.length;\n if (!shouldLoop && this.playIndex === 0) {\n this.playIndex = this.sounds.length;\n }\n return playbacks[0];\n }\n\n /**\n * Plays the sounds in the group in a specific order.\n *\n * @param shouldLoop - Indicates whether the sounds should be played in a loop.\n * @param options - Options for configuring fade behavior.\n * @returns The playback object representing the first sound being played, or undefined if the group is empty.\n */\n playOrdered(shouldLoop: boolean = true, options?: PlayOptions): Playback | undefined {\n if (this.sounds.length === 0 || this.playIndex >= this.sounds.length) {\n return undefined;\n }\n const playbacks = this.sounds[this.playIndex].play(options);\n if (playbacks.length === 0) {\n return undefined;\n }\n this.playIndex = (this.playIndex + 1) % this.sounds.length;\n if (!shouldLoop && this.playIndex === 0) {\n this.playIndex = this.sounds.length;\n }\n return playbacks[0];\n }\n\n /**\n * Restarts ordered playback from the first sound in the group.\n */\n resetOrder(): void {\n this.playIndex = 0;\n }\n\n get duration() {\n return this.sounds.map((sound) => sound.duration).reduce((a, b) => Math.max(a, b), 0);\n }\n\n seek(time: number): void {\n this.sounds.forEach((sound) => sound.seek?.(time));\n }\n\n addSound(sound: Sound): void {\n this.sounds.push(sound);\n }\n\n /**\n * Returns a random sound from the group.\n * @throws Error if the group is empty.\n */\n randomSound(): Sound {\n if (this.sounds.length === 0) {\n throw new Error(\"Cannot get a random sound from an empty group\");\n }\n const randomIndex = Math.floor(Math.random() * this.sounds.length);\n return this.sounds[randomIndex];\n }\n\n preplay(): Playback[] {\n const playbacks = this.sounds.map((sound) => sound.preplay());\n return playbacks.flat();\n }\n\n /***\n * Plays all sounds in the group.\n * @returns {Playback[]} An array of Playback objects, one for each sound in the group.\n */\n\n play(options?: PlayOptions): Playback[] {\n return this.sounds.flatMap((sound) => sound.play(options));\n }\n\n /**\n * A boolean indicating whether any of the sounds in the group are currently playing.\n * @returns {boolean} True if any sound is playing, false otherwise.\n */\n\n get isPlaying(): boolean {\n return this.sounds.some((sound) => sound.isPlaying);\n }\n\n /**\n * Stops all the sounds in the group.\n */\n\n stop(): void {\n this.sounds.forEach((sound) => sound.stop());\n }\n\n pause(): void {\n this.sounds.forEach((sound) => sound.pause());\n }\n\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeTo(value, duration, type))).then(() => {});\n }\n\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeIn(duration, type))).then(() => {});\n }\n\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeOut(duration, type))).then(() => {});\n }\n\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.stopWithFade(duration, type))).then(() => {});\n }\n\n loop(loopCount?: LoopCount): LoopCount {\n if (loopCount === undefined) {\n return this.loopCount;\n }\n this.loopCount = loopCount;\n this.sounds.forEach((sound) => sound.loop(loopCount));\n return this.loopCount;\n }\n\n /**\n * Adds a filter to all sounds in the group.\n * Filters are cloned to playbacks, not shared.\n */\n addFilter(filter: BiquadFilterNode): void {\n this.sounds.forEach((sound) => sound.addFilter(filter));\n }\n\n /**\n * Removes a filter from all sounds in the group.\n */\n removeFilter(filter: BiquadFilterNode): void {\n this.sounds.forEach((sound) => sound.removeFilter(filter));\n }\n\n /**\n * Routes every sound in this group to the specified Bus (or back to\n * master). Fans the call out to every member; see {@link Sound.routeTo}\n * for full semantics. With a `sendGain`, adds a per-sound send instead\n * of redirecting primary routing.\n */\n routeTo(target: Bus | string, sendGain?: number): void {\n if (sendGain !== undefined) {\n this.sounds.forEach((sound) => sound.routeTo(target, sendGain));\n } else {\n this.sounds.forEach((sound) => sound.routeTo(target));\n }\n }\n\n set position(position: [number, number, number]) {\n this._position = position;\n this.sounds.forEach((sound) => (sound.position = this._position));\n }\n\n get position(): [number, number, number] {\n return this._position;\n }\n\n get volume(): number {\n if (this.sounds.length === 0) {\n return 1;\n }\n return this.sounds.map((sound) => sound.volume).reduce((a, b) => a + b, 0) / this.sounds.length;\n }\n\n set volume(volume: number) {\n this.sounds.forEach((sound) => (sound.volume = volume));\n }\n\n get playbackRate(): number {\n if (this.sounds.length === 0) {\n return 1;\n }\n return this.sounds[0].playbackRate;\n }\n\n set playbackRate(rate: number) {\n this.sounds.forEach((sound) => (sound.playbackRate = rate));\n }\n}\n","import type Hls from \"hls.js\";\nimport type { ErrorData, Events } from \"hls.js\";\n\nconst HLS_NOT_INSTALLED_MESSAGE = \"hls.js is not installed; install hls.js or use a direct stream URL.\";\nconst HLS_UNSUPPORTED_MESSAGE =\n \"HLS playback is unavailable because Media Source Extensions are not supported in this browser; use a direct stream URL.\";\n\ntype HlsErrorListener = (error: Error, recoverable: boolean) => void;\n\nfunction isMissingHlsModule(cause: unknown): boolean {\n let current = cause;\n const visited = new Set<Error>();\n while (current instanceof Error && !visited.has(current)) {\n visited.add(current);\n const code = (current as Error & { code?: unknown }).code;\n if (current.message.includes(\"hls.js\") && (code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\")) {\n return true;\n }\n current = (current as Error & { cause?: unknown }).cause;\n }\n return false;\n}\n\n/**\n * Owns one optional hls.js instance and its media-element attachment.\n *\n * The module is loaded only for `.m3u8` streams without native HLS support, so\n * consumers that do not install the optional peer never load it.\n */\nexport class HlsAdapter {\n private destroyed = false;\n private errorListenerRegistered = false;\n\n private readonly handleError = (_event: string, data: ErrorData): void => {\n const message = `hls.js ${data.type}: ${data.details}`;\n const error = new Error(message, data.error ? { cause: data.error } : undefined);\n this.onError(error, false);\n };\n\n private constructor(\n private readonly hls: Hls,\n private readonly errorEvent: Events.ERROR,\n private readonly onError: HlsErrorListener,\n ) {}\n\n static async create(onError: HlsErrorListener): Promise<HlsAdapter> {\n let HlsConstructor: typeof import(\"hls.js\").default;\n try {\n ({ default: HlsConstructor } = await import(\"hls.js\"));\n } catch (cause) {\n if (isMissingHlsModule(cause)) {\n throw new Error(HLS_NOT_INSTALLED_MESSAGE, { cause });\n }\n throw cause;\n }\n\n if (!HlsConstructor.isSupported()) {\n throw new Error(HLS_UNSUPPORTED_MESSAGE);\n }\n\n return new HlsAdapter(new HlsConstructor(), HlsConstructor.Events.ERROR, onError);\n }\n\n attach(audio: HTMLAudioElement, url: string): void {\n if (this.destroyed) {\n throw new Error(\"HlsAdapter has been destroyed\");\n }\n if (!this.errorListenerRegistered) {\n this.hls.on(this.errorEvent, this.handleError);\n this.errorListenerRegistered = true;\n }\n this.hls.loadSource(url);\n this.hls.attachMedia(audio);\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n if (this.errorListenerRegistered) {\n this.hls.off(this.errorEvent, this.handleError);\n this.errorListenerRegistered = false;\n }\n this.hls.detachMedia();\n this.hls.destroy();\n }\n}\n","/**\n * Minimal logging seam used by Cacophony for host-side (main-thread) diagnostic\n * output. Injecting a logger via {@link RuntimeOptions.logger} — or silencing\n * all output via {@link RuntimeOptions.quiet} — lets embedders (notably Node /\n * headless / CLI hosts) capture or suppress the `[cacophony/worklet]` chatter\n * without monkey-patching `console`.\n */\nexport interface CacophonyLogger {\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\n/**\n * Default logger: forwards each level to the corresponding `console` method.\n *\n * The methods dispatch through `console.*` dynamically (rather than binding the\n * reference once) so that test spies installed on `console.info` / `console.warn`\n * / `console.error` after this object is created are still observed.\n */\nexport const consoleLogger: CacophonyLogger = {\n info: (...args) => console.info(...args),\n warn: (...args) => console.warn(...args),\n error: (...args) => console.error(...args),\n};\n\n/** No-op logger: discards all output. Used when `quiet` is set. */\nexport const noopLogger: CacophonyLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n","import type { BiquadFilterNode } from \"./context\";\n\nexport type FilterCloneOverrides = {\n filters?: BiquadFilterNode[];\n};\n\nexport abstract class FilterManager {\n _filters: BiquadFilterNode[] = [];\n\n /**\n * Adds a BiquadFilterNode to the filter chain.\n * @throws {Error} If the same filter instance is added twice.\n */\n addFilter(filter: BiquadFilterNode) {\n // Prevent adding the same filter instance twice (would break Web Audio graph topology)\n if (this._filters.includes(filter)) {\n throw new Error(\"Cannot add the same filter instance twice\");\n }\n this._filters.push(filter);\n }\n\n /**\n * Removes a BiquadFilterNode from the filter chain by object identity.\n * @throws {Error} If the filter was never added to this container.\n */\n removeFilter(filter: BiquadFilterNode) {\n // Remove by object identity, not by comparing property values\n // This prevents bugs where filters with identical properties can't be removed\n const initialLength = this._filters.length;\n this._filters = this._filters.filter((f) => f !== filter);\n\n // Error if filter was not in the array (nothing was removed)\n if (this._filters.length === initialLength) {\n throw new Error(\"Cannot remove filter that was never added to this container\");\n }\n }\n\n get filters() {\n return this._filters;\n }\n\n addFilters(filters: BiquadFilterNode[]) {\n filters.forEach((filter) => this.addFilter(filter));\n }\n\n cleanup(): void {\n // Disconnect all filters before removing them\n this._filters.forEach((filter) => filter.disconnect());\n this._filters = [];\n }\n}\n","import type { PanType, Position } from \"./cacophony\";\nimport type { BaseContext, PannerNode, StereoPannerNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\n\n/**\n * The HRTF-relevant subset of {@link PannerOptions}. Derived via `Pick` so the\n * set of fields tracks the lib.dom definition rather than being re-listed.\n */\nexport type HrtfPannerOptions = Pick<\n PannerOptions,\n | \"coneInnerAngle\"\n | \"coneOuterAngle\"\n | \"coneOuterGain\"\n | \"distanceModel\"\n | \"maxDistance\"\n | \"channelCount\"\n | \"channelCountMode\"\n | \"channelInterpretation\"\n | \"panningModel\"\n | \"refDistance\"\n | \"rolloffFactor\"\n | \"positionX\"\n | \"positionY\"\n | \"positionZ\"\n | \"orientationX\"\n | \"orientationY\"\n | \"orientationZ\"\n>;\n\n/**\n * Canonical type describing the 3D-audio configuration for a sound, discriminated\n * by {@link PanType}.\n *\n * - For `panType: \"stereo\"` the only relevant field is `stereoPan` (-1..1). HRTF\n * position/orientation fields are intentionally not present on this variant\n * so that callers cannot accidentally set them on a stereo source.\n * - For `panType: \"HRTF\"` the variant carries the {@link HrtfPannerOptions}\n * subset plus an optional `[x,y,z]` position tuple.\n *\n * Containers / mixins exposing this type guarantee the HRTF variant is fully\n * populated (`Required`) so callers can read fields without further narrowing.\n * Inputs that only carry a partial override (e.g. clone calls) use the looser\n * {@link PanCloneOverrides} shape.\n */\nexport type ThreeDOptions =\n | { panType: \"stereo\"; stereoPan: number }\n | ({ panType: \"HRTF\"; position?: Position } & Required<HrtfPannerOptions>);\n\nexport type PanCloneOverrides = {\n panType?: PanType;\n stereoPan?: number; // -1 (left) to 1 (right)\n threeDOptions?: Partial<HrtfPannerOptions>; // HRTF panning only\n position?: Position; // HRTF panning only, [x, y, z]\n};\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\nexport function PannerMixin<TBase extends Constructor>(Base: TBase) {\n abstract class PannerMixin extends Base {\n panner?: PannerNode | StereoPannerNode;\n _panType: PanType = \"stereo\";\n\n get panType(): PanType {\n return this._panType;\n }\n\n setPanType(panType: PanType, audioContext: BaseContext) {\n if (this._panType === panType && this.panner) {\n // If the pan type is already set and a panner exists, do nothing\n return;\n }\n\n // Clean up existing panner if it exists\n if (this.panner) {\n this.panner.disconnect();\n }\n\n this._panType = panType;\n if (panType === \"stereo\") {\n this.panner = audioContext.createStereoPanner();\n } else {\n this.panner = audioContext.createPanner();\n }\n }\n\n setPannerNode(pannerNode: PannerNode) {\n this.panner = pannerNode;\n }\n\n /**\n * Gets the stereo panning value.\n * @returns {number | null} The current stereo pan value, or null if stereo panning is not applicable.\n */\n\n get stereoPan(): number | null {\n if (this.panType === \"stereo\") {\n return (this.panner as StereoPannerNode).pan.value;\n }\n return null;\n }\n\n /**\n * Sets the stereo panning value.\n * @param {number} value - The stereo pan value to set, between -1 (left) and 1 (right).\n * @throws {Error} Throws an error if stereo panning is not available, if the sound has been cleaned up, or if the value is out of bounds.\n */\n\n set stereoPan(value: number) {\n if (this.panType !== \"stereo\") {\n throw new Error(\"Stereo panning is not available when using HRTF.\");\n }\n if (!this.panner) {\n throw new Error(\"Cannot set stereo pan of a sound that has been cleaned up\");\n }\n if (value < -1 || value > 1) {\n throw new RangeError(\"Stereo pan must be between -1 and 1.\");\n }\n (this.panner as StereoPannerNode).pan.setValueAtTime(value, this.panner.context.currentTime);\n }\n\n /**\n * Gets the 3D audio options if HRTF panning is used.\n * @returns {ThreeDOptions} The current 3D audio options (HRTF variant).\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n get threeDOptions(): ThreeDOptions {\n if (!this.panner) {\n throw new Error(\"Cannot get 3D options of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot get 3D options of a sound that is not using HRTF\");\n }\n const panner = this.panner as PannerNode;\n return {\n panType: \"HRTF\",\n coneInnerAngle: panner.coneInnerAngle,\n coneOuterAngle: panner.coneOuterAngle,\n coneOuterGain: panner.coneOuterGain,\n distanceModel: panner.distanceModel,\n maxDistance: panner.maxDistance,\n channelCount: this.panner.channelCount,\n channelCountMode: panner.channelCountMode,\n channelInterpretation: panner.channelInterpretation,\n panningModel: panner.panningModel,\n refDistance: panner.refDistance,\n rolloffFactor: panner.rolloffFactor,\n positionX: panner.positionX.value,\n positionY: panner.positionY.value,\n positionZ: panner.positionZ.value,\n orientationX: panner.orientationX.value,\n orientationY: panner.orientationY.value,\n orientationZ: panner.orientationZ.value,\n };\n }\n\n /**\n * Sets the 3D audio options for HRTF panning.\n * Accepts either a full {@link ThreeDOptions} value or a partial HRTF override.\n * Any field omitted from the input is left at its current value on the underlying PannerNode.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n set threeDOptions(options: ThreeDOptions | Partial<HrtfPannerOptions>) {\n if (!this.panner) {\n throw new Error(\"Cannot set 3D options of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot set 3D options of a sound that is not using HRTF\");\n }\n // If caller passed a stereo-variant ThreeDOptions to an HRTF panner, reject.\n if (\"panType\" in options && options.panType === \"stereo\") {\n throw new Error(\"Cannot apply stereo ThreeDOptions to an HRTF panner\");\n }\n const panner = this.panner as PannerNode;\n panner.coneInnerAngle = options.coneInnerAngle !== undefined ? options.coneInnerAngle : panner.coneInnerAngle;\n panner.coneOuterAngle = options.coneOuterAngle !== undefined ? options.coneOuterAngle : panner.coneOuterAngle;\n panner.coneOuterGain = options.coneOuterGain !== undefined ? options.coneOuterGain : panner.coneOuterGain;\n panner.distanceModel = options.distanceModel ?? panner.distanceModel;\n panner.maxDistance = options.maxDistance !== undefined ? options.maxDistance : panner.maxDistance;\n panner.channelCount = options.channelCount !== undefined ? options.channelCount : panner.channelCount;\n panner.channelCountMode = options.channelCountMode ?? panner.channelCountMode;\n panner.channelInterpretation = options.channelInterpretation ?? panner.channelInterpretation;\n panner.panningModel = options.panningModel ?? panner.panningModel;\n panner.refDistance = options.refDistance !== undefined ? options.refDistance : panner.refDistance;\n panner.rolloffFactor = options.rolloffFactor !== undefined ? options.rolloffFactor : panner.rolloffFactor;\n panner.positionX.value = options.positionX !== undefined ? options.positionX : panner.positionX.value;\n panner.positionY.value = options.positionY !== undefined ? options.positionY : panner.positionY.value;\n panner.positionZ.value = options.positionZ !== undefined ? options.positionZ : panner.positionZ.value;\n panner.orientationX.value = options.orientationX !== undefined ? options.orientationX : panner.orientationX.value;\n panner.orientationY.value = options.orientationY !== undefined ? options.orientationY : panner.orientationY.value;\n panner.orientationZ.value = options.orientationZ !== undefined ? options.orientationZ : panner.orientationZ.value;\n }\n\n /**\n * Sets the position of the audio source in 3D space (HRTF panning only).\n * @param {Position} position - The [x, y, z] coordinates of the audio source.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n set position(position: Position) {\n if (!this.panner) {\n throw new Error(\"Cannot move a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot move a sound that is not using HRTF\");\n }\n const [x, y, z] = position;\n const panner = this.panner as PannerNode;\n panner.positionX.value = x;\n panner.positionY.value = y;\n panner.positionZ.value = z;\n }\n\n /**\n * Gets the position of the audio source in 3D space (HRTF panning only).\n * @returns {Position} The [x, y, z] coordinates of the audio source.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n get position(): Position {\n if (!this.panner) {\n throw new Error(\"Cannot get position of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot get position of a sound that is not using HRTF\");\n }\n const panner = this.panner as PannerNode;\n return [panner.positionX.value, panner.positionY.value, panner.positionZ.value];\n }\n\n cleanup(): void {\n if (this.panner) {\n this.panner.disconnect();\n this.panner = undefined;\n }\n super.cleanup();\n }\n }\n\n return PannerMixin;\n}\n","import type { FadeType } from \"./cacophony\";\nimport type { GainNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\n\nexport type VolumeCloneOverrides = {\n volume?: number;\n};\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\ntype ActiveFade = {\n timeout: ReturnType<typeof setTimeout>;\n resolve: () => void;\n node: GainNode;\n target: number;\n type: FadeType;\n};\n\nexport function VolumeMixin<TBase extends Constructor>(Base: TBase) {\n abstract class VolumeMixin extends Base {\n gainNode?: GainNode;\n _fadeTimeout?: ReturnType<typeof setTimeout>;\n _isFading: boolean = false;\n private _activeFade?: ActiveFade;\n\n setGainNode(gainNode: GainNode) {\n this.gainNode = gainNode;\n }\n\n cleanup(): void {\n // cancelFade resolves any pending fade promise synchronously, so\n // awaiters of an in-flight fade unblock before the gainNode is torn down.\n this.cancelFade();\n if (this.gainNode) {\n this.gainNode.disconnect();\n this.gainNode = undefined;\n }\n super.cleanup();\n }\n\n /**\n * Gets the current volume of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n get volume(): number {\n if (!this.gainNode) {\n throw new Error(\"Cannot get volume of a sound that has been cleaned up\");\n }\n return this.gainNode.gain.value;\n }\n\n /**\n * Sets the volume of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n set volume(v: number) {\n if (!this.gainNode) {\n throw new Error(\"Cannot set volume of a sound that has been cleaned up\");\n }\n this.cancelFade();\n this.gainNode.gain.value = v;\n }\n\n /**\n * Whether a fade is currently in progress.\n */\n get isFading(): boolean {\n return this._isFading;\n }\n\n /**\n * Fades the volume to a target value over a duration.\n * @param {number} value - The target volume (0 to 1).\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type, \"linear\" or \"exponential\". Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes on a live context, or when its automation has been\n * scheduled on an offline context.\n */\n fadeTo(value: number, duration: number, type: FadeType = \"linear\"): Promise<void> {\n if (!this.gainNode) {\n throw new Error(\"Cannot fade a sound that has been cleaned up\");\n }\n this.cancelFade();\n\n // Capture the gainNode at fade start so the timeout closes over the node\n // present when fadeTo was called -- not whichever node (or undefined)\n // this.gainNode happens to point at when the timer fires.\n const node = this.gainNode;\n const now = node.context.currentTime;\n const endTime = now + duration / 1000;\n\n node.gain.setValueAtTime(node.gain.value, now);\n\n if (type === \"exponential\") {\n node.gain.exponentialRampToValueAtTime(value === 0 ? 0.0001 : value, endTime);\n } else {\n node.gain.linearRampToValueAtTime(value, endTime);\n }\n\n if (\"startRendering\" in node.context) {\n if (value === 0 && type === \"exponential\") {\n node.gain.setValueAtTime(0, endTime);\n }\n this._isFading = false;\n return Promise.resolve();\n }\n\n this._isFading = true;\n\n return new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n // If this fade was superseded (cancelled / re-fired) the active-fade\n // slot has already been cleared by cancelFade. Bail without resolving\n // a second time.\n if (this._activeFade?.timeout !== timeout) {\n return;\n }\n this._activeFade = undefined;\n this._fadeTimeout = undefined;\n this._isFading = false;\n // Web Audio's exponentialRampToValueAtTime requires a strict-positive\n // target, so the ramp went to 0.0001. Snap to 0 at completion.\n if (value === 0 && type === \"exponential\") {\n node.gain.value = 0;\n }\n resolve();\n }, duration);\n this._activeFade = { timeout, resolve, node, target: value, type };\n this._fadeTimeout = timeout;\n });\n }\n\n /**\n * Cancels any in-progress fade. The fade's pending promise is resolved\n * (Option A: cancellation is not an error; awaiters learn the outcome by\n * reading the resulting volume). Safe to call repeatedly.\n */\n cancelFade(): void {\n const active = this._activeFade;\n this._activeFade = undefined;\n if (this._fadeTimeout !== undefined) {\n clearTimeout(this._fadeTimeout);\n this._fadeTimeout = undefined;\n }\n if (active && this.gainNode) {\n const node = this.gainNode;\n const now = node.context.currentTime;\n node.gain.cancelScheduledValues(now);\n // A cancelled fade-to-zero exponential would otherwise leave the gain\n // latched at the 0.0001 ramp target. Snap to 0 so the audible state\n // matches the caller's intent at the moment they cancelled.\n if (active.target === 0 && active.type === \"exponential\") {\n node.gain.value = 0;\n } else {\n // For all other cancellations, leave the gain wherever the ramp had\n // reached -- pin it there so no further scheduled values apply.\n node.gain.setValueAtTime(node.gain.value, now);\n }\n }\n this._isFading = false;\n // Resolve LAST: any handler attached to the fade promise will observe\n // the post-cancel volume and isFading === false.\n if (active) {\n active.resolve();\n }\n }\n\n /**\n * Fades in from silence to the current volume.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n if (!this.gainNode) {\n throw new Error(\"Cannot fade a sound that has been cleaned up\");\n }\n const target = this.gainNode.gain.value;\n this.gainNode.gain.setValueAtTime(0.0001, this.gainNode.context.currentTime);\n return this.fadeTo(target, duration, type);\n }\n\n /**\n * Fades out from the current volume to silence.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return this.fadeTo(0, duration, type);\n }\n }\n\n return VolumeMixin;\n}\n","import type { Bus } from \"./bus\";\nimport type { FadeType } from \"./cacophony\";\nimport type { PlaybackContainer } from \"./container\";\nimport type { AudioNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { EffectChain } from \"./effectChain\";\nimport type { CacophonyEffect } from \"./effects\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { PlaybackEvents } from \"./events\";\nimport { FilterManager } from \"./filters\";\nimport { PannerMixin } from \"./pannerMixin\";\nimport { VolumeMixin } from \"./volumeMixin\";\n\nexport type PlaybackState = \"unplayed\" | \"playing\" | \"paused\" | \"stopped\";\n\nexport abstract class BasePlayback extends PannerMixin(VolumeMixin(FilterManager)) {\n public source?: AudioNode;\n protected _effectChain?: EffectChain;\n private _effectContext?: BaseContext;\n /**\n * Per-playback send-gain allocations owned by the shared routing state\n * machine. Cleanup disconnects every allocation deterministically.\n */\n _sendGains: Map<Bus, GainNode> = new Map();\n protected _state: PlaybackState = \"unplayed\";\n public origin: PlaybackContainer;\n public eventEmitter: TypedEventEmitter<PlaybackEvents> = new TypedEventEmitter<PlaybackEvents>();\n\n constructor(origin: PlaybackContainer) {\n super();\n this.origin = origin;\n }\n\n protected setEffectChainEndpoints(input: AudioNode, output: AudioNode): void {\n this._effectContext =\n (input.context as BaseContext | undefined) ??\n (this.origin as PlaybackContainer & { context?: BaseContext }).context;\n if (this._effectChain) {\n this._effectChain.setEndpoints(input, output);\n return;\n }\n this._effectChain = new EffectChain(input, output, this.constructor.name);\n }\n\n /** Build and splice an effect into this live playback's pre-panner chain. */\n addEffect(effect: CacophonyEffect): Promise<AudioNode> {\n try {\n return this.materializeEffect(effect);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /** @internal Source preplay path that preserves synchronous native-build failures. */\n _addSourceEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect);\n }\n\n /** @internal Materialize a source-level biquad recipe and retain filter API compatibility. */\n _addFilterEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect, (handle) => {\n this._filters.push(handle as BiquadFilterNode);\n });\n }\n\n private materializeEffect(effect: CacophonyEffect, onBuilt?: (handle: AudioNode) => void): Promise<AudioNode> {\n if (!this._effectChain || !this._effectContext) {\n throw new Error(\"Cannot add an effect before the playback effect chain is initialized\");\n }\n const chain = this._effectChain;\n const reservation = chain.reserve();\n let built;\n try {\n built = effect.build(this._effectContext);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n if (built instanceof Promise || (typeof built === \"object\" && built !== null && \"then\" in built)) {\n return Promise.resolve(built).then(\n (resolved) => {\n const handle = chain.resolve(reservation, resolved);\n onBuilt?.(handle);\n return handle;\n },\n (error) => {\n chain.cancel(reservation);\n throw error;\n },\n );\n }\n try {\n const handle = chain.resolve(reservation, built);\n onBuilt?.(handle);\n return Promise.resolve(handle);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n }\n\n rampEffectParam(\n handle: AudioNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate an effect before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(handle, paramName, value, options);\n }\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot add a filter before the playback effect chain is initialized\");\n }\n this._effectChain.add(filter, this._filters.length);\n this._filters.push(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot remove a filter before the playback effect chain is initialized\");\n }\n this._effectChain.remove(filter);\n this._filters = this._filters.filter((candidate) => candidate !== filter);\n }\n\n setFilterOrder(filters: readonly BiquadFilterNode[]): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot reorder filters before the playback effect chain is initialized\");\n }\n const nonFilters = this._effectChain.nodes.filter((node) => !this._filters.includes(node as BiquadFilterNode));\n this._effectChain.setOrder([...filters, ...nonFilters]);\n this._filters = [...filters];\n }\n\n setFilterBypassed(filter: BiquadFilterNode, bypassed: boolean): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot bypass a filter before the playback effect chain is initialized\");\n }\n this._effectChain.setBypassed(filter, bypassed);\n }\n\n isFilterBypassed(filter: BiquadFilterNode): boolean {\n return this._effectChain?.isBypassed(filter) ?? false;\n }\n\n rampFilterParam(\n filter: BiquadFilterNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate a filter before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(filter, paramName, value, options);\n }\n\n abstract play(): [this];\n abstract pause(): void;\n abstract stop(): void;\n\n /**\n * Checks if the audio is currently playing.\n */\n\n get isPlaying(): boolean {\n return this._state === \"playing\";\n }\n\n get isPaused(): boolean {\n return this._state === \"paused\";\n }\n\n protected markPlaying(): void {\n this._state = \"playing\";\n }\n\n protected markPaused(): void {\n this._state = \"paused\";\n }\n\n protected markStopped(): void {\n this._state = \"stopped\";\n }\n\n protected emitPlayStarted(isResume: boolean): void {\n this.markPlaying();\n this.emit(\"play\", this);\n if (isResume) {\n this.emit(\"resume\", undefined);\n }\n this.origin.cacophony?.emit(\"globalPlay\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitPaused(): void {\n this.markPaused();\n this.emit(\"pause\", undefined);\n this.origin.cacophony?.emit(\"globalPause\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitStopped(): void {\n this.markStopped();\n this.emit(\"stop\", undefined);\n this.origin.cacophony?.emit(\"globalStop\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n public emit<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n public async emitAsync<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Fades the volume to a target value, emitting fadeStart and fadeEnd events.\n */\n fadeTo(value: number, duration: number, type: FadeType = \"linear\"): Promise<void> {\n this.emit(\"fadeStart\", { target: value, duration, type });\n return super.fadeTo(value, duration, type).then(() => {\n this.emit(\"fadeEnd\", undefined);\n });\n }\n\n /**\n * Cancels any in-progress fade, emitting fadeCancel if a fade was active.\n */\n cancelFade(): void {\n const wasFading = this._isFading;\n super.cancelFade();\n if (wasFading) {\n this.emit(\"fadeCancel\", undefined);\n }\n }\n\n cleanup(): void {\n for (const sendGain of this._sendGains.values()) {\n try {\n sendGain.disconnect();\n } catch {\n // The node may already have been disconnected externally.\n }\n }\n this._sendGains.clear();\n this._effectChain?.destroy();\n this._effectChain = undefined;\n this._effectContext = undefined;\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","import type { BasePlayback } from \"./basePlayback\";\nimport type { Cacophony, FadeType, Position } from \"./cacophony\";\nimport type { BiquadFilterNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\nimport type { HrtfPannerOptions, ThreeDOptions } from \"./pannerMixin\";\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\n/**\n * Fully-populated HRTF defaults used to initialize the container's canonical\n * `_threeDOptions` and to recover a coherent HRTF state when a partial HRTF\n * override is applied to a stereo-variant container.\n */\nconst defaultHrtfThreeDOptions: ThreeDOptions = {\n panType: \"HRTF\",\n coneInnerAngle: 360,\n coneOuterAngle: 360,\n coneOuterGain: 0,\n distanceModel: \"inverse\",\n maxDistance: 10000,\n channelCount: 2,\n channelCountMode: \"clamped-max\",\n channelInterpretation: \"speakers\",\n panningModel: \"HRTF\",\n refDistance: 1,\n rolloffFactor: 1,\n positionX: 0,\n positionY: 0,\n positionZ: 0,\n orientationX: 0,\n orientationY: 0,\n orientationZ: 0,\n};\n\n/**\n * Public structural contract of the class returned by the {@link PlaybackContainer}\n * mixin factory. Describes the surface that consumers can rely on when they hold\n * a reference to a container (for example, `BasePlayback.origin`).\n *\n * `stereoPan` is `number` (centered = `0`); the storage and setter only ever\n * carry a number, so the public read shape matches.\n */\nexport interface PlaybackContainer {\n cacophony?: Cacophony;\n playbacks: BasePlayback[];\n preplay(): BasePlayback[];\n play(): BasePlayback[];\n stop(): void;\n pause(): void;\n addFilter(filter: BiquadFilterNode): void;\n removeFilter(filter: BiquadFilterNode): void;\n isPlaying: boolean;\n position: Position;\n threeDOptions: ThreeDOptions;\n stereoPan: number;\n volume: number;\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;\n fadeIn(duration: number, type?: FadeType): Promise<void>;\n fadeOut(duration: number, type?: FadeType): Promise<void>;\n stopWithFade(duration: number, type?: FadeType): Promise<void>;\n}\n\nexport function PlaybackContainer<TBase extends Constructor>(Base: TBase) {\n abstract class PlaybackContainer extends Base {\n playbacks: BasePlayback[] = [];\n _position: Position = [0, 0, 0];\n _stereoPan: number = 0;\n /**\n * Canonical 3D-audio configuration storage. Always holds a fully-populated\n * HRTF variant ({@link ThreeDOptions} with `panType: \"HRTF\"`) so callers\n * reading via the getter never see undefined fields. Setting the stereo\n * variant replaces this with the stereo shape (no stale HRTF fields).\n */\n _threeDOptions: ThreeDOptions = { ...defaultHrtfThreeDOptions };\n _volume: number = 1;\n\n abstract preplay(): BasePlayback[];\n\n /**\n * Starts playback of the sound and returns a Playback instance representing this particular playback.\n * Multiple Playback instances can be created by calling this method multiple times,\n * allowing for the same sound to be played concurrently with different settings.\n */\n\n play(): BasePlayback[] {\n const playback = this.preplay();\n playback.forEach((p) => p.play());\n return playback;\n }\n\n /**\n * Stops all current playbacks of the sound immediately. This will halt the sound regardless of how many times it has been played.\n */\n\n stop() {\n this.playbacks.forEach((p) => {\n p.stop();\n p.cleanup();\n });\n this.playbacks = [];\n }\n\n /**\n * Pauses all current playbacks of the sound.\n */\n\n pause(): void {\n this.playbacks.forEach((playback) => playback.pause());\n }\n\n /**\n * Adds a BiquadFilterNode to the container's filter chain.\n * Filters are cloned when creating new playbacks - existing playbacks are not affected.\n * Each playback gets independent filter instances for isolated processing.\n */\n\n addFilter(filter: BiquadFilterNode): void {\n super.addFilter(filter);\n // Note: Filters are cloned when creating new playbacks.\n // Existing playbacks remain unchanged to maintain independent filter chains.\n }\n\n /**\n * Removes a BiquadFilterNode from the container's filter chain.\n * Only affects future playbacks - existing playbacks retain their cloned filters.\n * @throws {Error} If the filter was never added to this container.\n */\n\n removeFilter(filter: BiquadFilterNode): void {\n super.removeFilter(filter);\n // Note: Existing playbacks have cloned filter instances and are not affected.\n }\n\n /**\n * Returns a boolean indicating whether the object is currently playing.\n * an object is playing if any of its playbacks are currently playing.\n */\n\n get isPlaying(): boolean {\n return this.playbacks.some((p) => p.isPlaying);\n }\n\n /**\n * Retrieves the current 3D spatial position of the sound in the audio context.\n * The position is returned as an array of three values[x, y, z].\n */\n\n get position(): Position {\n if (this._threeDOptions.panType === \"stereo\") {\n return [0, 0, 0];\n }\n return [this._threeDOptions.positionX, this._threeDOptions.positionY, this._threeDOptions.positionZ];\n }\n\n /**\n * Sets the 3D spatial position of the sound in the audio context.\n * The position is an array of three values[x, y, z].\n * This method updates the position of all active playbacks of the sound.\n */\n\n set position(position: Position) {\n if (this._threeDOptions.panType === \"HRTF\") {\n this._threeDOptions.positionX = position[0];\n this._threeDOptions.positionY = position[1];\n this._threeDOptions.positionZ = position[2];\n }\n this.playbacks.forEach((p) => (p.position = position));\n }\n\n /**\n * Returns a fresh copy of the canonical 3D-audio configuration. The clone\n * prevents callers from mutating the container's internal state via the\n * returned reference (previously the raw `_threeDOptions` object leaked).\n */\n get threeDOptions(): ThreeDOptions {\n return { ...this._threeDOptions };\n }\n\n /**\n * Accepts either a full canonical {@link ThreeDOptions} value, or a partial\n * HRTF-options override (legacy ergonomics). When a partial is provided the\n * existing variant is merged in place — and if the existing variant is\n * stereo, the partial is interpreted as a switch back to HRTF defaults plus\n * the override (no stale stereo fields). When a full variant is provided\n * with a different `panType`, the storage is replaced wholesale so stale\n * fields from the previous variant cannot be observed via the getter.\n */\n set threeDOptions(options: ThreeDOptions | Partial<HrtfPannerOptions>) {\n if (\"panType\" in options) {\n // Full canonical variant — replace storage so the discriminant is honored.\n this._threeDOptions = { ...options };\n } else if (this._threeDOptions.panType === \"HRTF\") {\n this._threeDOptions = { ...this._threeDOptions, ...options };\n } else {\n // Was stereo, now receiving HRTF partial — fall back to HRTF defaults + override.\n this._threeDOptions = { ...defaultHrtfThreeDOptions, ...options };\n }\n this.playbacks.forEach((p) => (p.threeDOptions = this._threeDOptions));\n }\n\n get stereoPan(): number {\n return this._stereoPan;\n }\n\n set stereoPan(value: number) {\n this._stereoPan = value;\n this.playbacks.forEach((p) => (p.stereoPan = value));\n }\n\n /***\n * Gets the volume of the sound. This volume level affects all current and future playbacks of this sound instance.\n * The volume is specified as a linear value between 0 (silent) and 1 (full volume).\n */\n\n get volume(): number {\n return this._volume;\n }\n\n /***\n * Sets the volume of the sound. This volume level affects all current and future playbacks of this sound instance.\n * The volume is specified as a linear value between 0 (silent) and 1 (full volume).\n */\n\n set volume(volume: number) {\n this._volume = volume;\n this.playbacks.forEach((p) => (p.volume = volume));\n }\n\n /**\n * Fades the volume of all playbacks to a target value over a duration.\n * @param {number} value - The target volume (0 to 1).\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void> {\n this._volume = value;\n return Promise.all(this.playbacks.map((p) => p.fadeTo(value, duration, type))).then(() => {});\n }\n\n /**\n * Fades in all playbacks from silence to their current volume.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.playbacks.map((p) => p.fadeIn(duration, type))).then(() => {});\n }\n\n /**\n * Fades out all playbacks from their current volume to silence.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.playbacks.map((p) => p.fadeOut(duration, type))).then(() => {});\n }\n\n /**\n * Fades out all playbacks then stops them.\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes and all playbacks are stopped.\n */\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n return this.fadeOut(duration, type).then(() => this.stop());\n }\n }\n return PlaybackContainer;\n}\n","import type { BasePlayback } from \"./basePlayback\";\nimport type { Bus, BusRoutedSource } from \"./bus\";\nimport type { Cacophony } from \"./cacophony\";\nimport { PlaybackContainer } from \"./container\";\nimport type { BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport type { CacophonyEffect } from \"./effects\";\nimport { BiquadRecipeEffect } from \"./effects\";\nimport { FilterManager } from \"./filters\";\n\n/**\n * Shared routing state machine for playback-producing sources.\n *\n * The concrete source supplies its audio context, master output, owning\n * Cacophony instance, and playback list. This mixin owns primary-route state,\n * additive sends, bus registration, live rewiring, drain handling, and route\n * cleanup for every source type.\n */\nexport abstract class RoutableSource extends PlaybackContainer(FilterManager) implements BusRoutedSource {\n protected abstract context: BaseContext;\n protected abstract globalGainNode: GainNode;\n abstract get cacophony(): Cacophony | undefined;\n\n /** Primary route target. `null` is the canonical master route. */\n protected _routeTarget: Bus | null = null;\n /** Additional send target to gain-value mappings. */\n protected _sends: Map<Bus, number> = new Map();\n /** Effect recipes materialized independently for each future playback. */\n private readonly _effects: CacophonyEffect[] = [];\n private readonly _filterEffects = new Map<BiquadFilterNode, CacophonyEffect>();\n private readonly _filterEffectRecipes = new Set<CacophonyEffect>();\n\n addEffect(effect: CacophonyEffect): void {\n if (this._effects.includes(effect)) {\n throw new Error(\"Cannot add the same effect recipe twice\");\n }\n this._effects.push(effect);\n }\n\n removeEffect(effect: CacophonyEffect): void {\n const index = this._effects.indexOf(effect);\n if (index === -1) {\n throw new Error(\"Cannot remove an effect recipe that was never added\");\n }\n this._effects.splice(index, 1);\n }\n\n override addFilter(filter: BiquadFilterNode): void {\n super.addFilter(filter);\n const effect = new BiquadRecipeEffect(filter);\n this._filterEffects.set(filter, effect);\n this._filterEffectRecipes.add(effect);\n this._effects.push(effect);\n }\n\n override removeFilter(filter: BiquadFilterNode): void {\n super.removeFilter(filter);\n const effect = this._filterEffects.get(filter);\n if (effect) {\n this._effects.splice(this._effects.indexOf(effect), 1);\n this._filterEffects.delete(filter);\n this._filterEffectRecipes.delete(effect);\n }\n }\n\n /**\n * Route this source to a bus, or add an independent send when `sendGain`\n * is provided.\n */\n routeTo(target: Bus | string, sendGain?: number): void {\n const bus = this._resolveBusArg(target);\n if (sendGain !== undefined) {\n this._addSend(bus, sendGain);\n return;\n }\n this._setPrimary(bus);\n }\n\n private _resolveBusArg(target: Bus | string): Bus {\n if (typeof target !== \"string\") {\n return target;\n }\n const bus = this.cacophony?.getBus(target);\n if (!bus) {\n throw new Error(`No bus registered with name '${target}'`);\n }\n return bus;\n }\n\n /**\n * Resolve the node used by future playbacks for their primary output.\n * Sources left on a subsequently destroyed bus fall back to master.\n */\n _resolveRouteTargetNode(): GainNode {\n if (!this._routeTarget) {\n return this.globalGainNode;\n }\n if (this._routeTarget.destroyed) {\n console.warn(\n `${this.constructor.name} routed to destroyed bus '${this._routeTarget.name ?? \"<anonymous>\"}'; falling back to master`,\n );\n return this.globalGainNode;\n }\n return this._routeTarget.input;\n }\n\n /**\n * Establish every configured send on a newly created playback.\n */\n protected _wireRouteSends(playback: BasePlayback): void {\n const outputNode = (playback as BasePlayback & { readonly outputNode: GainNode }).outputNode;\n for (const [bus, gainValue] of this._sends) {\n if (bus.destroyed) {\n console.warn(`${this.constructor.name} has a send to destroyed bus '${bus.name ?? \"<anonymous>\"}'; skipping`);\n continue;\n }\n const sendGain = this.context.createGain();\n sendGain.gain.value = gainValue;\n outputNode.connect(sendGain);\n sendGain.connect(bus.input);\n playback._sendGains.set(bus, sendGain);\n }\n }\n\n /** Materialize all declared recipes, then establish this playback's sends. */\n protected _preparePlayback(playback: BasePlayback): void {\n for (const effect of this._effects) {\n const build = this._filterEffectRecipes.has(effect)\n ? playback._addFilterEffect(effect)\n : playback._addSourceEffect(effect);\n void build.catch((error) => {\n console.warn(`${this.constructor.name} could not build a per-playback effect; continuing without it.`, error);\n });\n }\n this._wireRouteSends(playback);\n }\n\n private _setPrimary(bus: Bus): void {\n if (bus.destroyed) {\n throw new Error(`Cannot route to destroyed bus '${bus.name ?? \"<anonymous>\"}'`);\n }\n const collapseToMaster = bus.input === this.globalGainNode;\n const oldTarget = this._routeTarget;\n const oldTargetNode = this._resolveRouteTargetNode();\n this._routeTarget = collapseToMaster ? null : bus;\n const newTarget = this._routeTarget;\n const newTargetNode = this._resolveRouteTargetNode();\n\n // Bus registration follows bus identity, not resolved node identity. A\n // destroyed bus and master can resolve to the same node while still\n // requiring the old bus registration to be removed.\n if (oldTarget !== newTarget) {\n if (oldTarget && !this._sends.has(oldTarget)) {\n oldTarget._unregisterRoutedSource(this);\n }\n if (newTarget) {\n newTarget._registerRoutedSource(this);\n }\n }\n if (oldTargetNode === newTargetNode) {\n return;\n }\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n try {\n playback.outputNode.disconnect(oldTargetNode);\n } catch {\n // A playback may already have been disconnected during cleanup.\n }\n try {\n playback.outputNode.connect(newTargetNode);\n } catch {\n // Live rewiring is best-effort for externally disconnected nodes.\n }\n }\n }\n\n private _addSend(bus: Bus, gainValue: number): void {\n if (bus.destroyed) {\n throw new Error(`Cannot add a send to destroyed bus '${bus.name ?? \"<anonymous>\"}'`);\n }\n this._sends.set(bus, gainValue);\n bus._registerRoutedSource(this);\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n const existing = playback._sendGains.get(bus);\n if (existing) {\n existing.gain.value = gainValue;\n continue;\n }\n const sendGain = this.context.createGain();\n sendGain.gain.value = gainValue;\n try {\n playback.outputNode.connect(sendGain);\n } catch {\n try {\n sendGain.disconnect();\n } catch {\n // The failed connection may leave the fresh node already detached.\n }\n continue;\n }\n sendGain.connect(bus.input);\n playback._sendGains.set(bus, sendGain);\n }\n }\n\n _onBusDrained(bus: Bus, target: Bus): void {\n if (this._routeTarget === bus) {\n this._setPrimary(target);\n }\n if (!this._sends.has(bus)) {\n return;\n }\n const gainValue = this._sends.get(bus) as number;\n this._sends.delete(bus);\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n const sendGain = playback._sendGains.get(bus);\n if (!sendGain) {\n continue;\n }\n try {\n sendGain.disconnect();\n } catch {\n // The node may already have been disconnected externally.\n }\n playback._sendGains.delete(bus);\n }\n this._addSend(target, gainValue);\n }\n\n cleanup(): void {\n if (this._routeTarget) {\n this._routeTarget._unregisterRoutedSource(this);\n }\n for (const bus of this._sends.keys()) {\n bus._unregisterRoutedSource(this);\n }\n this._effects.length = 0;\n this._filterEffects.clear();\n this._filterEffectRecipes.clear();\n super.cleanup();\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, Cacophony, LoopCount, PanType } from \"./cacophony\";\nimport type {\n AudioNode,\n AudioParam,\n BaseContext,\n BiquadFilterNode,\n GainNode,\n MediaStreamAudioSourceNode,\n} from \"./context\";\nimport { RoutableSource } from \"./routableSource\";\n\nexport interface MediaStreamSoundOptions {\n panType?: PanType;\n stopTracksOnStop?: boolean;\n /**\n * Attach the stream to a muted `HTMLAudioElement` while it plays.\n *\n * Chromium will not render a remote WebRTC `MediaStreamTrack` that is only\n * connected to the Web Audio graph: packets arrive but the decoder produces\n * zero samples, so a `MediaStreamAudioSourceNode` taps pure silence. Pulling\n * the same stream through a media element \"primes\" the decode pipeline; the\n * Web Audio path then receives real audio. The element is muted so it does\n * not double-play over the (possibly spatialised) Web Audio output. Firefox\n * does not need this, but the priming element is harmless there.\n *\n * Defaults to `true`. Set to `false` for streams that are never routed to a\n * Web Audio graph, or in environments without `HTMLAudioElement`.\n */\n primeWithMediaElement?: boolean;\n}\n\nexport class MediaStreamPlayback extends BasePlayback {\n public declare origin: MediaStreamSound;\n public declare source?: MediaStreamAudioSourceNode;\n private stopTracksOnStop: boolean;\n private tracksStopped = false;\n private pausedTrackStates?: Map<MediaStreamTrack, boolean>;\n /**\n * Muted media element that keeps Chromium's decode pipeline alive for the\n * stream so the Web Audio tap receives real audio. See\n * {@link MediaStreamSoundOptions.primeWithMediaElement}.\n */\n private primeElement?: HTMLAudioElement;\n\n constructor(\n origin: MediaStreamSound,\n source: MediaStreamAudioSourceNode,\n gainNode: GainNode,\n context: BaseContext,\n outputNode: AudioNode,\n panType: PanType,\n stopTracksOnStop: boolean,\n primeWithMediaElement: boolean = true,\n ) {\n super(origin);\n this.stopTracksOnStop = stopTracksOnStop;\n this.source = source;\n this.setPanType(panType, context);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n this.gainNode!.connect(outputNode);\n if (primeWithMediaElement) {\n this.createPrimeElement();\n }\n }\n\n private createPrimeElement(): void {\n if (typeof Audio === \"undefined\" || !this.source) {\n return;\n }\n try {\n const element = new Audio();\n element.muted = true;\n element.srcObject = this.source.mediaStream;\n this.primeElement = element;\n } catch {\n // Environment without media-element support: priming is best-effort.\n this.primeElement = undefined;\n }\n }\n\n private teardownPrimeElement(): void {\n const element = this.primeElement;\n if (!element) {\n return;\n }\n this.primeElement = undefined;\n try {\n element.pause();\n element.srcObject = null;\n } catch {\n // Element may already be torn down by the browser.\n }\n }\n\n get duration(): number {\n return 0;\n }\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a media stream that has been cleaned up\");\n }\n if (this.isPlaying) {\n return [this];\n }\n const tracks = this.source.mediaStream.getTracks();\n if (tracks.length > 0 && tracks.every((track) => track.readyState === \"ended\")) {\n throw new Error(\"Cannot play a media stream whose tracks have ended\");\n }\n const isResume = this.isPaused;\n if (isResume && this.pausedTrackStates) {\n tracks.forEach((track) => {\n const enabled = this.pausedTrackStates?.get(track);\n if (enabled !== undefined) {\n track.enabled = enabled;\n }\n });\n }\n this.pausedTrackStates = undefined;\n // Muted autoplay is always permitted, so this needs no user gesture; the\n // returned promise is ignored because failure only loses Chromium priming.\n this.primeElement?.play().catch(() => {});\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || !this.isPlaying) {\n return;\n }\n const tracks = this.source.mediaStream.getTracks();\n this.pausedTrackStates = new Map(tracks.map((track) => [track, track.enabled]));\n tracks.forEach((track) => (track.enabled = false));\n this.primeElement?.pause();\n this.emitPaused();\n }\n\n resume(): void {\n this.play();\n }\n\n stop(): void {\n if (!this.source) {\n throw new Error(\"Cannot stop a media stream that has been cleaned up\");\n }\n const shouldEmitStop = this._state === \"playing\" || this._state === \"paused\";\n this.stopOwnedTracks();\n this.pausedTrackStates = undefined;\n this.teardownPrimeElement();\n if (shouldEmitStop) {\n this.emitStopped();\n } else {\n this.markStopped();\n }\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot add a filter to a media stream that has been cleaned up\");\n }\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot remove a filter from a media stream that has been cleaned up\");\n }\n super.removeFilter(filter);\n }\n\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a media stream that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n private stopOwnedTracks(): void {\n if (!this.stopTracksOnStop || this.tracksStopped || !this.source) {\n return;\n }\n this.source.mediaStream.getTracks().forEach((track) => track.stop());\n this.tracksStopped = true;\n }\n\n cleanup(): void {\n if (!this.source) {\n return;\n }\n this.stopOwnedTracks();\n this.pausedTrackStates = undefined;\n this.markStopped();\n this.teardownPrimeElement();\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n}\n\nexport class MediaStreamSound extends RoutableSource implements BaseSound {\n public declare playbacks: MediaStreamPlayback[];\n protected context: BaseContext;\n protected globalGainNode: GainNode;\n private stream: MediaStream;\n private stopTracksOnStop: boolean;\n private panType: PanType;\n private primeWithMediaElement: boolean;\n\n constructor(\n stream: MediaStream,\n context: BaseContext,\n globalGainNode: GainNode,\n options: MediaStreamSoundOptions = {},\n private _cacophony?: Cacophony,\n ) {\n super();\n this.stream = stream;\n this.context = context;\n this.globalGainNode = globalGainNode;\n this.panType = options.panType ?? \"HRTF\";\n this.stopTracksOnStop = options.stopTracksOnStop ?? true;\n this.primeWithMediaElement = options.primeWithMediaElement ?? true;\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n private createStreamSource(stream: MediaStream): MediaStreamAudioSourceNode {\n if (!this.context.createMediaStreamSource) {\n throw new Error(\"Media stream sources are not supported on this audio context (e.g. OfflineAudioContext).\");\n }\n return this.context.createMediaStreamSource(stream);\n }\n\n preplay(): MediaStreamPlayback[] {\n if (this.playbacks[0]) {\n return [this.playbacks[0]];\n }\n\n const source = this.createStreamSource(this.stream);\n const gainNode = this.context.createGain();\n const playback = new MediaStreamPlayback(\n this,\n source,\n gainNode,\n this.context,\n this._resolveRouteTargetNode(),\n this.panType,\n this.stopTracksOnStop,\n this.primeWithMediaElement,\n );\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else {\n playback.stereoPan = this.stereoPan ?? 0;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n seek(_time: number): void {}\n\n resume(): void {\n this.playbacks.forEach((playback) => playback.resume());\n }\n\n loop(_loopCount?: LoopCount): LoopCount {\n return 0;\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n cleanup(): void {\n this.stop();\n super.cleanup();\n }\n}\n","/**\n * {@link LoudnessMeter} — a main-thread handle over the `loudness-meter`\n * AudioWorkletProcessor (ITU-R BS.1770-5 momentary / short-term / integrated\n * loudness + true-peak).\n *\n * The meter taps a target node's OUTPUT as a BRANCH: it connects the target to\n * the metering worklet's input without disturbing the target's existing forward\n * edge, so inserting a meter never alters the audible path. The worklet itself\n * is pass-through; its output is routed through an owned zero-gain sink so the\n * branch stays silent while remaining on a live render graph.\n *\n * Readings arrive ~10×/s over the MessagePort and are cached on this handle\n * ({@link momentary} / {@link shortTerm} / {@link integrated} / {@link truePeak});\n * an optional {@link onUpdate} callback fires on each report.\n *\n * @see https://www.itu.int/rec/R-REC-BS.1770-5-202311-I/en\n */\nimport type { AudioNode, AudioWorkletNode, GainNode } from \"../context\";\n\n/** A single set of loudness readings posted by the worklet. */\nexport interface LoudnessReading {\n /** Momentary loudness over the last 400 ms, in LKFS (LUFS). */\n momentary: number;\n /** Short-term loudness over the last 3 s, in LKFS (LUFS). */\n shortTerm: number;\n /** Gated integrated loudness since the last reset, in LKFS (LUFS). */\n integrated: number;\n /** Running true-peak level since the last reset, in dBTP. */\n truePeak: number;\n}\n\ninterface LoudnessMessage extends LoudnessReading {\n type: \"loudness\";\n}\n\nexport class LoudnessMeter {\n private readonly node: AudioWorkletNode;\n private readonly source: AudioNode;\n private readonly silentSink?: GainNode;\n private readonly sinkDestination?: AudioNode;\n private latest: LoudnessReading = {\n momentary: -Infinity,\n shortTerm: -Infinity,\n integrated: -Infinity,\n truePeak: -Infinity,\n };\n\n /**\n * Invoked on every report from the worklet (~10×/s) with the latest readings.\n * Assign a function to observe live loudness; leave undefined to poll the\n * cached getters instead.\n */\n onUpdate?: (reading: LoudnessReading) => void;\n\n /**\n * @param node The constructed `loudness-meter` AudioWorkletNode.\n * @param source The node whose output is being metered. Its output is\n * branched into `node` here; the source's existing connections are\n * untouched (the audible path is preserved).\n * @param silentSink Optional zero-gain node owned by the meter. When supplied,\n * the worklet output is connected through it to `sinkDestination`, keeping\n * the metering worklet on a pulled graph without adding audible output.\n * @param sinkDestination Destination for the owned zero-gain sink.\n */\n constructor(node: AudioWorkletNode, source: AudioNode, silentSink?: GainNode, sinkDestination?: AudioNode) {\n this.node = node;\n this.source = source;\n this.silentSink = silentSink;\n this.sinkDestination = sinkDestination;\n this.node.port.addEventListener(\"message\", this.handleMessage);\n // Some MessagePort implementations require an explicit start when using\n // addEventListener rather than onmessage.\n this.node.port.start?.();\n // Branch tap: source.output → meter (in ADDITION to source's existing edges).\n this.source.connect(this.node);\n if (this.silentSink && this.sinkDestination) {\n this.silentSink.gain.value = 0;\n this.node.connect(this.silentSink);\n this.silentSink.connect(this.sinkDestination);\n }\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n const data = event.data as LoudnessMessage | undefined;\n if (!data || data.type !== \"loudness\") {\n return;\n }\n this.latest = {\n momentary: data.momentary,\n shortTerm: data.shortTerm,\n integrated: data.integrated,\n truePeak: data.truePeak,\n };\n this.onUpdate?.(this.latest);\n };\n\n /** Latest momentary loudness (400 ms) in LKFS. */\n get momentary(): number {\n return this.latest.momentary;\n }\n\n /** Latest short-term loudness (3 s) in LKFS. */\n get shortTerm(): number {\n return this.latest.shortTerm;\n }\n\n /** Latest gated integrated loudness (since last reset) in LKFS. */\n get integrated(): number {\n return this.latest.integrated;\n }\n\n /** Latest true-peak level (since last reset) in dBTP. */\n get truePeak(): number {\n return this.latest.truePeak;\n }\n\n /** A snapshot of all current readings. */\n get reading(): LoudnessReading {\n return { ...this.latest };\n }\n\n /** The underlying metering worklet node (for advanced wiring). */\n get workletNode(): AudioWorkletNode {\n return this.node;\n }\n\n /**\n * Resets the integrated-loudness gate accumulator and the true-peak hold on\n * the worklet (momentary/short-term keep sliding). Use to start a fresh\n * integrated measurement of a new programme.\n */\n reset(): void {\n this.node.port.postMessage({ command: \"reset\" });\n }\n\n /**\n * Stops metering: removes the branch tap from the source and detaches the\n * message listener. The audible path (the source's other edges) is untouched.\n */\n disconnect(): void {\n this.node.port.removeEventListener?.(\"message\", this.handleMessage);\n try {\n this.source.disconnect(this.node);\n } catch {\n // Already disconnected — ignore.\n }\n if (this.silentSink) {\n try {\n this.node.disconnect(this.silentSink);\n } catch {\n // Already disconnected — ignore.\n }\n }\n if (this.silentSink && this.sinkDestination) {\n try {\n this.silentSink.disconnect(this.sinkDestination);\n } catch {\n // Already disconnected — ignore.\n }\n }\n }\n}\n","import type { Cacophony, PanType } from \"./cacophony\";\nimport type { AudioNode, BaseContext, GainNode } from \"./context\";\nimport { MediaStreamPlayback, MediaStreamSound, type MediaStreamSoundOptions } from \"./mediaStream\";\nimport type { HrtfPannerOptions } from \"./pannerMixin\";\n\n/** Options for acquiring and monitoring a microphone stream. */\nexport interface MicrophoneStreamOptions extends MediaStreamSoundOptions {\n /** Constraints forwarded to `navigator.mediaDevices.getUserMedia`. */\n constraints?: MediaStreamConstraints;\n /**\n * Stop the microphone tracks when playback stops or is cleaned up.\n * Defaults to `true`. Set to `false` to keep the stream reusable.\n */\n stopTracksOnStop?: boolean;\n /** Initial left/right pan when `panType` is `\"stereo\"`. */\n stereoPan?: number;\n /** Initial spatial options when `panType` is `\"HRTF\"` (the default). */\n threeDOptions?: Partial<HrtfPannerOptions>;\n}\n\n/**\n * A microphone playback uses the same BasePlayback lifecycle, event, panning,\n * effect, routing, and cleanup implementation as every other MediaStream.\n */\nexport { MediaStreamPlayback as MicrophonePlayback };\n\n/**\n * A live microphone source backed by the current MediaStreamSound architecture.\n * Use {@link MicrophoneStream.request} (or `Cacophony.getMicrophoneStream`) when\n * the stream still needs to be acquired so permission failures remain observable.\n */\nexport class MicrophoneStream extends MediaStreamSound {\n static async request(\n context: BaseContext,\n outputNode?: AudioNode,\n options: MicrophoneStreamOptions = {},\n cacophony?: Cacophony,\n ): Promise<MicrophoneStream> {\n const stream = await navigator.mediaDevices.getUserMedia(options.constraints ?? { audio: true });\n return new MicrophoneStream(context, stream, outputNode, options, cacophony);\n }\n\n constructor(\n context: BaseContext,\n stream: MediaStream,\n outputNode?: AudioNode,\n options: MicrophoneStreamOptions = {},\n cacophony?: Cacophony,\n ) {\n const { constraints: _constraints, stereoPan, threeDOptions, ...mediaStreamOptions } = options;\n const panType: PanType = mediaStreamOptions.panType ?? \"HRTF\";\n super(\n stream,\n context,\n (outputNode ?? context.destination) as GainNode,\n {\n ...mediaStreamOptions,\n panType,\n primeWithMediaElement: mediaStreamOptions.primeWithMediaElement ?? false,\n stopTracksOnStop: mediaStreamOptions.stopTracksOnStop ?? true,\n },\n cacophony,\n );\n\n if (panType === \"stereo\") {\n this.stereoPan = stereoPan ?? 0;\n } else if (threeDOptions) {\n this.threeDOptions = threeDOptions;\n }\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, Cacophony, LoopCount, PanType, SoundType, StreamCapabilities } from \"./cacophony\";\nimport type { AudioNode, AudioParam, AudioWorkletNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport { RoutableSource } from \"./routableSource\";\n\nexport type PcmStreamState = \"idle\" | \"playing\" | \"paused\" | \"stopped\" | \"ended\";\n\nexport interface PcmStreamBufferEvent {\n bufferedDuration: number;\n}\n\nexport type PcmStreamEvents = {\n stateChange: PcmStreamState;\n underrun: PcmStreamBufferEvent;\n drain: PcmStreamBufferEvent;\n ended: undefined;\n error: unknown;\n};\n\nexport interface PcmStreamPullSource {\n seek(time: number): void;\n cleanup(): void;\n}\n\nexport interface PcmStreamSoundOptions {\n /** Number of interleaved channels in every {@link PcmStreamSound.write} call. @default 1 */\n channelCount?: number;\n /** Fixed ring-buffer capacity in seconds. @default 1 */\n bufferDuration?: number;\n /** PCM that must be buffered before initial consumption starts, in seconds. @default 0.05 */\n latency?: number;\n /** Spatial panner implementation used by the playback. @default \"HRTF\" */\n panType?: PanType;\n /** Aborting tears down the worklet and routing graph. */\n signal?: AbortSignal;\n}\n\ntype PcmWorkletMessage =\n | { type: \"consumed\"; frames: number }\n | { type: \"underrun\" }\n | { type: \"ended\" }\n | { type: \"overflow\" };\n\nexport class PcmStreamPlayback extends BasePlayback {\n public declare origin: PcmStreamSound;\n public declare source?: AudioWorkletNode;\n\n constructor(\n origin: PcmStreamSound,\n source: AudioWorkletNode,\n gainNode: GainNode,\n context: BaseContext,\n outputNode: AudioNode,\n panType: PanType,\n ) {\n super(origin);\n this.source = source;\n this.setPanType(panType, context);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n this.gainNode!.connect(outputNode);\n }\n\n get duration(): number {\n return Number.POSITIVE_INFINITY;\n }\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a PCM stream that has been cleaned up\");\n }\n if (this.isPlaying) {\n return [this];\n }\n const isResume = this.isPaused;\n this.source.port.postMessage({ type: \"play\" });\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || !this.isPlaying) {\n return;\n }\n this.source.port.postMessage({ type: \"pause\" });\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source) {\n return;\n }\n const shouldEmitStop = this.isPlaying || this.isPaused;\n this.source.port.postMessage({ type: \"stop\" });\n if (shouldEmitStop) {\n this.emitStopped();\n } else {\n this.markStopped();\n }\n }\n\n _handleEnded(): void {\n if (!this.source) {\n return;\n }\n this.markStopped();\n this.emit(\"ended\", undefined);\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot add a filter to a PCM stream that has been cleaned up\");\n }\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot remove a filter from a PCM stream that has been cleaned up\");\n }\n super.removeFilter(filter);\n }\n\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a PCM stream that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as AudioNode);\n }\n\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as AudioNode);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n cleanup(): void {\n if (!this.source) {\n return;\n }\n this.markStopped();\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n}\n\nexport class PcmStreamSound extends RoutableSource implements BaseSound {\n public declare playbacks: PcmStreamPlayback[];\n public soundType?: SoundType;\n public streamCapabilities?: StreamCapabilities;\n protected context: BaseContext;\n protected globalGainNode: GainNode;\n private readonly workletNode: AudioWorkletNode;\n private readonly channelCount: number;\n private readonly capacityFrames: number;\n private readonly panType: PanType;\n private readonly eventEmitter = new TypedEventEmitter<PcmStreamEvents>();\n private readonly signal?: AbortSignal;\n private bufferedFrames = 0;\n private backpressured = false;\n private inputEnded = false;\n private disposed = false;\n private state: PcmStreamState = \"idle\";\n private pullSource?: PcmStreamPullSource;\n\n private readonly handleWorkletMessage = (event: MessageEvent<PcmWorkletMessage>): void => {\n switch (event.data.type) {\n case \"consumed\":\n this.bufferedFrames = Math.max(0, this.bufferedFrames - event.data.frames);\n if (this.backpressured && this.bufferedFrames < this.capacityFrames) {\n this.backpressured = false;\n this.emit(\"drain\", { bufferedDuration: this.bufferedDuration });\n }\n break;\n case \"underrun\":\n this.emit(\"underrun\", { bufferedDuration: this.bufferedDuration });\n break;\n case \"ended\":\n this.bufferedFrames = 0;\n this.playbacks.forEach((playback) => playback._handleEnded());\n this.setState(\"ended\");\n this.emit(\"ended\", undefined);\n break;\n case \"overflow\":\n this.backpressured = true;\n break;\n }\n };\n\n private readonly handleAbort = (): void => {\n this.cleanup();\n };\n\n constructor(\n workletNode: AudioWorkletNode,\n context: BaseContext,\n globalGainNode: GainNode,\n options: PcmStreamSoundOptions & {\n channelCount: number;\n bufferDuration: number;\n latency: number;\n },\n private readonly _cacophony?: Cacophony,\n ) {\n super();\n this.workletNode = workletNode;\n this.context = context;\n this.globalGainNode = globalGainNode;\n this.channelCount = options.channelCount;\n this.capacityFrames = Math.ceil(context.sampleRate * options.bufferDuration);\n this.panType = options.panType ?? \"HRTF\";\n this.signal = options.signal;\n this.workletNode.port.addEventListener(\"message\", this.handleWorkletMessage as EventListener);\n this.workletNode.port.start?.();\n this.signal?.addEventListener(\"abort\", this.handleAbort, { once: true });\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n get bufferedDuration(): number {\n return this.bufferedFrames / this.context.sampleRate;\n }\n\n get inputChannelCount(): number {\n return this.channelCount;\n }\n\n on<K extends keyof PcmStreamEvents>(event: K, listener: (data: PcmStreamEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n off<K extends keyof PcmStreamEvents>(event: K, listener: (data: PcmStreamEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n private emit<K extends keyof PcmStreamEvents>(event: K, data: PcmStreamEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n private setState(state: PcmStreamState): void {\n if (this.state === state) {\n return;\n }\n this.state = state;\n this.emit(\"stateChange\", state);\n }\n\n attachPullSource(source: PcmStreamPullSource, capabilities: StreamCapabilities): void {\n if (this.disposed || this.pullSource) {\n throw new Error(\"Cannot attach a pull source to this PCM stream\");\n }\n this.pullSource = source;\n this.soundType = \"streaming\";\n this.streamCapabilities = capabilities;\n }\n\n handlePullError(error: unknown): void {\n if (!this.disposed) {\n this.emit(\"error\", error);\n }\n }\n\n /**\n * Enqueue PCM at the context's sample rate.\n *\n * Samples are interleaved according to `channelCount`. `false` means the\n * fixed ring buffer is full (or this accepted write filled it); wait for\n * `drain` before retrying a rejected chunk.\n */\n write(samples: Float32Array): boolean {\n if (this.disposed) {\n throw new Error(\"Cannot write to a PCM stream that has been cleaned up\");\n }\n if (this.inputEnded) {\n throw new Error(\"Cannot write PCM after end()\");\n }\n if (!(samples instanceof Float32Array)) {\n throw new TypeError(\"PCM writes require an interleaved Float32Array\");\n }\n if (samples.length % this.channelCount !== 0) {\n throw new RangeError(`Interleaved PCM length must be divisible by channelCount (${this.channelCount})`);\n }\n const frameCount = samples.length / this.channelCount;\n if (frameCount > this.capacityFrames) {\n throw new RangeError(\"PCM chunk is larger than the configured ring buffer\");\n }\n if (this.bufferedFrames + frameCount > this.capacityFrames) {\n this.backpressured = true;\n return false;\n }\n\n this.workletNode.port.postMessage({\n type: \"write\",\n samples: samples.slice(),\n });\n this.bufferedFrames += frameCount;\n if (this.bufferedFrames === this.capacityFrames) {\n this.backpressured = true;\n return false;\n }\n return true;\n }\n\n end(): void {\n if (this.disposed || this.inputEnded) {\n return;\n }\n this.inputEnded = true;\n this.workletNode.port.postMessage({ type: \"end\" });\n }\n\n preplay(): PcmStreamPlayback[] {\n if (this.disposed) {\n throw new Error(\"Cannot play a PCM stream that has been cleaned up\");\n }\n if (this.playbacks[0]) {\n return [this.playbacks[0]];\n }\n const playback = new PcmStreamPlayback(\n this,\n this.workletNode,\n this.context.createGain(),\n this.context,\n this._resolveRouteTargetNode(),\n this.panType,\n );\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else {\n playback.stereoPan = this.stereoPan;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n play(): PcmStreamPlayback[] {\n if (this.state === \"ended\") {\n throw new Error(\"Cannot play a PCM stream after it has ended\");\n }\n const playbacks = this.preplay();\n playbacks.forEach((playback) => playback.play());\n this.setState(\"playing\");\n return playbacks;\n }\n\n pause(): void {\n if (this.state !== \"playing\") {\n return;\n }\n this.playbacks.forEach((playback) => playback.pause());\n this.setState(\"paused\");\n }\n\n resume(): void {\n if (this.state !== \"paused\") {\n return;\n }\n this.playbacks.forEach((playback) => playback.play());\n this.setState(\"playing\");\n }\n\n stop(): void {\n if (this.disposed) {\n return;\n }\n const hadPlayback = this.playbacks.length > 0;\n super.stop();\n if (!hadPlayback) {\n this.workletNode.port.postMessage({ type: \"stop\" });\n }\n this.bufferedFrames = 0;\n this.backpressured = false;\n this.inputEnded = false;\n this.setState(\"stopped\");\n }\n\n seek(time: number): void {\n if (!this.pullSource) {\n throw new Error(\"PCM streams do not support seeking\");\n }\n this.pullSource.seek(time);\n }\n\n loop(_loopCount?: LoopCount): LoopCount {\n throw new Error(\"PCM streams do not support looping\");\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n cleanup(): void {\n if (this.disposed) {\n return;\n }\n this.stop();\n this.disposed = true;\n this.pullSource?.cleanup();\n this.pullSource = undefined;\n this.signal?.removeEventListener(\"abort\", this.handleAbort);\n this.workletNode.port.removeEventListener(\"message\", this.handleWorkletMessage as EventListener);\n this.workletNode.disconnect();\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","/*\n * Dynamics processor core — context-free DSP math for a feed-forward dynamic\n * range compressor / limiter / expander / gate.\n *\n * Implements the canonical design from:\n * D. Giannoulis, M. Massberg, J. D. Reiss,\n * \"Digital Dynamic Range Compressor Design — A Tutorial and Analysis\",\n * J. Audio Eng. Soc., Vol. 60, No. 6, pp. 399-408 (2012).\n *\n * Topology (the paper's recommended best practice, p.399 abstract / pp.404-405):\n * - feed-forward (side-chain driven from the INPUT),\n * - gain computer = static dB->dB curve (eqs 2-4),\n * - level detector placed in the LOG domain AFTER the gain computer\n * (eq 23): smooth the gain-reduction control signal, not the raw level,\n * - smooth branching peak detector for the ballistics (eq 16).\n *\n * A SINGLE gain computer, parameterized by threshold T, ratio R and knee W,\n * serves all four behaviours (Giannoulis 2012, p.403; see notes.md\n * \"Expander / Gate from the SAME machinery\"):\n * - R > 1 -> compressor (slope 1/R < 1 above T),\n * - R -> infinity -> limiter (slope -> 0 above T, eqs 18-19),\n * - R < 1 -> downward expander (slope > 1 BELOW T),\n * - extreme R < 1 -> gate (very steep downward expansion below T).\n *\n * This file holds ONLY pure numeric math (operates on plain numbers and\n * Float32Array). It has no AudioWorklet / global dependencies so it can be\n * unit-tested directly (the worklet shell in dynamics.ts delegates to it).\n */\n\n/** Parameters consumed per processing block. All dB values are dBFS. */\nexport interface DynamicsParams {\n /** Threshold T (dB) — level above which compression starts. */\n threshold: number;\n /** Ratio R — reciprocal of slope above T. >1 compress, ->inf limit, <1 expand. */\n ratio: number;\n /** Knee width W (dB) — soft-knee transition centered on T; 0 = hard knee. */\n knee: number;\n /** Attack time tau_A (s) — gain-decrease time constant. */\n attack: number;\n /** Release time tau_R (s) — gain-recovery time constant. */\n release: number;\n /** Make-up gain M (dB) — constant output boost. */\n makeup: number;\n}\n\n/**\n * Default AudioParam values for the dynamics worklet, in the worklet's own\n * units. SINGLE SOURCE OF TRUTH — the worklet's `parameterDescriptors`\n * (dynamics.ts) builds its descriptors from this table, and tests that need the\n * shipped defaults import it here rather than re-typing literals. Living in the\n * worklet-global-free core keeps it importable from plain Node test runners.\n *\n * Defaults: a gentle, audible-but-safe compressor. Ratios cover compressor\n * (ratio >= 1), limiter (ratio -> large) and downward expander/gate (ratio < 1)\n * from one parameter set.\n */\nexport const DYNAMICS_DEFAULTS: Readonly<DynamicsParams> = {\n threshold: -24,\n ratio: 4,\n knee: 6,\n attack: 0.003,\n release: 0.25,\n makeup: 0,\n};\n\n/**\n * Ratio override the `createGate` factory (cacophony.ts) applies to turn the\n * shared dynamics worklet into a gate-like downward expander. SINGLE SOURCE OF\n * TRUTH for that override — `createGate` and the gate regression tests both\n * import it so a drift in the gate's ratio breaks the test.\n */\nexport const GATE_DEFAULT_RATIO = 0.1;\n\n/**\n * Smallest linear level we are willing to take a logarithm of. Below this the\n * input is treated as silence (gain-reduction control = 0), avoiding -Infinity\n * dB from log10(0).\n */\nconst LEVEL_FLOOR = 1e-12;\n\n/**\n * Static gain computer — maps an input level x_G (dB) to an output level\n * y_G (dB) given threshold T, ratio R and knee width W.\n *\n * Hard knee (W = 0), Giannoulis 2012 eq.3:\n * y_G = x_G for x_G <= T\n * y_G = T + (x_G - T) / R for x_G > T\n *\n * Soft knee (W > 0), eq.4 — quadratic interpolation across +/- W/2 around T:\n * y_G = x_G 2(x_G-T) < -W\n * y_G = x_G + (1/R - 1)(x_G - T + W/2)^2 / (2W) |2(x_G-T)| <= W\n * y_G = T + (x_G - T) / R 2(x_G-T) > W\n *\n * A limiter is R -> infinity: 1/R -> 0, so above T the curve flattens to y_G = T\n * (eqs 18-19). We handle the infinite/zero-slope case explicitly to avoid\n * division by infinity.\n *\n * Downward EXPANSION (R < 1, Giannoulis 2012 p.403 \"for ratios < 1 ... the\n * variable simply becomes smaller than -1\"): the slope-altered region is BELOW\n * the threshold rather than above it. With slope 1/R > 1 applied for x_G < T,\n * y_G = T + (x_G - T)/R is more negative than x_G, so quiet signals are pushed\n * further down (increasing dynamic range; an extreme R<1 ratio is a gate).\n * See notes.md \"Expander / Gate from the SAME machinery\". The compressor branch\n * (R >= 1) and the expander branch (R < 1) are mirror images about T, so one\n * function parameterized by T/R/W serves compressor, limiter, expander and gate.\n */\nexport function computeStaticGain(xG: number, threshold: number, ratio: number, knee: number): number {\n const slope = ratio === Number.POSITIVE_INFINITY ? 0 : 1 / ratio; // 1/R; limiter -> 0\n const delta = xG - threshold;\n const isExpander = ratio < 1; // R < 1 => downward expansion below T\n\n if (isExpander) {\n // Downward expander: slope-altered region is BELOW threshold (eqs 2-4\n // mirrored). Soft knee centered on T (eq.4 form, mirrored to the lower\n // side): blend the unity slope (above) into slope 1/R (below).\n if (knee > 0 && Math.abs(2 * delta) <= knee) {\n // eq.4 quadratic interpolation, mirrored about T for the expander.\n // The expander's slope-altered region is BELOW T, so the knee must join\n // the slope line at the LOWER edge (delta = -W/2) and unity at the UPPER\n // edge (delta = +W/2). Anchor the parabola to the upper edge (t = 0 there)\n // and subtract the curvature so y_G meets the slope line below: this\n // yields y_G <= x_G everywhere (never boosts) and C0/C1 continuity at both\n // edges (slope 1/R below, unity above). The MINUS sign is the mirror of\n // the compressor branch's PLUS — same eq.4 form, opposite altered side.\n const t = delta - knee / 2; // 0 at the upper edge (delta = +W/2)\n return xG - ((slope - 1) * (t * t)) / (2 * knee);\n }\n if (delta >= 0) {\n // x_G >= T (above threshold) — unity for a downward expander.\n return xG;\n }\n // x_G < T (below threshold) — slope 1/R > 1 pushes the level down.\n return threshold + delta * slope;\n }\n\n // Compressor / limiter (R >= 1): slope-altered region is ABOVE threshold.\n // Soft knee (eq.4 middle branch): 2|x_G - T| <= W.\n if (knee > 0 && Math.abs(2 * delta) <= knee) {\n // eq.4: y_G = x_G + (1/R - 1)(x_G - T + W/2)^2 / (2W)\n const t = delta + knee / 2;\n return xG + ((slope - 1) * (t * t)) / (2 * knee);\n }\n\n // Hard knee / outside knee region (eq.3 / eq.4 outer branches).\n if (delta <= 0) {\n // x_G <= T (below threshold) — unity. (eq.3 first case.)\n return xG;\n }\n // x_G > T (above threshold) — slope 1/R. (eq.3 second case; eq.4 upper branch.)\n return threshold + delta * slope;\n}\n\n/**\n * Time-constant -> one-pole coefficient mapping, Giannoulis 2012 eq.7:\n * alpha = exp(-1 / (tau * f_s))\n * tau is the time to reach 1 - 1/e of the final value. For tau > 0, f_s > 0\n * the result lies in (0, 1); larger tau -> alpha closer to 1 (slower).\n */\nexport function timeConstantToCoefficient(tau: number, sampleRate: number): number {\n if (tau <= 0) return 0; // degenerate: instantaneous response\n return Math.exp(-1 / (tau * sampleRate));\n}\n\n/**\n * Stateful feed-forward dynamics processor. One instance owns the ballistics\n * state (the smoothed gain-reduction envelope y_L of the eq.16 detector) and is\n * reused block-to-block so the IIR memory is preserved across process() calls —\n * constructing a fresh instance per block would reset the envelope and produce\n * audible transients (same hazard documented for the b-format upmixer).\n */\nexport class DynamicsProcessor {\n /**\n * Smoothed gain-reduction control envelope y_L[n-1] (the state of the eq.16\n * smooth branching peak detector). Operates on the gain-reduction signal\n * x_L = x_G - y_G (eq.23), which is 0 (no reduction) when not compressing, so\n * the envelope rests at 0.\n */\n private envelopeDb = 0;\n\n constructor(private readonly sampleRate: number) {}\n\n /** Reset the ballistics state (e.g. on a discontinuity). */\n reset(): void {\n this.envelopeDb = 0;\n }\n\n /**\n * Process one sample, returning the gain-applied output sample.\n *\n * Signal flow (Giannoulis 2012 recommended feed-forward, log-domain detector\n * after the gain computer):\n * 1. x_G = 20 log10 |x| (eq.23 line 1)\n * 2. y_G = gain computer(x_G; T, R, W) (eqs 3-4)\n * 3. x_L = x_G - y_G (eq.23 line 2)\n * — the instantaneous gain-reduction (>= 0 for compression/limiting,\n * also >= 0 for downward expansion).\n * 4. smooth branching peak detector on x_L (eq.16)\n * 5. c_dB = -y_L + M (eq.23 line 3, eq.1)\n * 6. y[n] = 10^(c_dB / 20) * x[n] (eq.1 linear form)\n *\n * @param alphaAttack precomputed attack coefficient alpha_A (eq.7)\n * @param alphaRelease precomputed release coefficient alpha_R (eq.7)\n */\n private processSample(\n x: number,\n threshold: number,\n ratio: number,\n knee: number,\n alphaAttack: number,\n alphaRelease: number,\n makeup: number,\n ): number {\n const level = Math.abs(x);\n\n // eq.23 line 1: x_G = 20 log10 |x|. Floor silence to avoid -Inf dB.\n const xG = level < LEVEL_FLOOR ? 20 * Math.log10(LEVEL_FLOOR) : 20 * Math.log10(level);\n\n // eqs 3-4: static curve.\n const yG = computeStaticGain(xG, threshold, ratio, knee);\n\n // eq.23 line 2: gain-reduction control signal (log domain, after the gain\n // computer). x_L >= 0 means \"this much reduction is wanted right now\".\n const xL = xG - yG;\n\n // eq.16 smooth branching peak detector — smooth the gain-reduction signal.\n // Attack branch when more reduction is wanted than currently applied;\n // release branch otherwise. The smooth (vs plain) branching form low-passes\n // BOTH branches so there is no discontinuity at the attack<->release switch.\n if (xL > this.envelopeDb) {\n // attack: x_L[n] > y_L[n-1]\n this.envelopeDb = alphaAttack * this.envelopeDb + (1 - alphaAttack) * xL;\n } else {\n // release: x_L[n] <= y_L[n-1]\n this.envelopeDb = alphaRelease * this.envelopeDb + (1 - alphaRelease) * xL;\n }\n\n // eq.23 line 3 + eq.1 make-up: control voltage c_dB = -y_L + M.\n const controlDb = -this.envelopeDb + makeup;\n\n // eq.1 linear form: y[n] = c[n] * x[n], with c[n] = 10^(c_dB/20).\n const controlLinear = 10 ** (controlDb / 20);\n return controlLinear * x;\n }\n\n /**\n * Process a block. `input` and `output` may alias (in-place is fine). The\n * attack/release coefficients are computed once per block (k-rate params);\n * the per-sample side-chain runs the eq.16 detector and eq.1 multiply.\n */\n process(input: Float32Array, output: Float32Array, params: DynamicsParams): void {\n // eq.7: tau -> alpha, once per block (k-rate).\n const alphaAttack = timeConstantToCoefficient(params.attack, this.sampleRate);\n const alphaRelease = timeConstantToCoefficient(params.release, this.sampleRate);\n\n const n = Math.min(input.length, output.length);\n for (let i = 0; i < n; i++) {\n output[i] = this.processSample(\n input[i],\n params.threshold,\n params.ratio,\n params.knee,\n alphaAttack,\n alphaRelease,\n params.makeup,\n );\n }\n }\n}\n","/*\n * Modulated-delay processor core — context-free DSP math for the unified\n * delay-line modulation effect behind delay/echo, chorus, flanger, vibrato and\n * doubling.\n *\n * Implements the canonical unified circuit from:\n * J. Dattorro, \"Effect Design, Part 2: Delay-Line Modulation and Chorus\",\n * J. Audio Eng. Soc., Vol. 45, No. 10, pp. 764-788 (1997).\n *\n * Topology (Dattorro 1997, Fig. 36, p.775) — ONE modulated delay line wrapped\n * in dry/wet/feedback paths:\n *\n * H(z) = (blend + feedforward * z^-i.frac) / (1 + feedback * z^-center)\n *\n * - blend — dry path gain on x[n].\n * - feedforward — gain on the MODULATED wet tap z^-i.frac, where the read\n * pointer i.frac = NOMINAL_DELAY + CHORUS_WIDTH * y[n] sweeps with the LFO.\n * - feedback — gain on a FIXED center tap z^-center. Dattorro p.775: the\n * feedback is taken from a SEPARATE fixed tap at the nominal center, NEVER\n * the modulated tap — feeding back a pitch-modulated signal is objectionable.\n *\n * Knob presets (Table 6, p.775) yield each effect from this one circuit:\n * vibrato blend=0, ff=1, fb=0 (100% wet)\n * flanger blend=0.7071, ff=0.7071, fb=-0.7071 (blend=ff for deepest trough)\n * chorus blend=0.7071, ff=1, fb=0.7071 (white-chorus knobs: blend=fb, ff=1)\n * doubling blend=0.7071, ff=0.7071, fb=0\n * echo blend=1, ff<=1, fb<1 (modulation off)\n * Stability requires |feedback| <= 0.9999999 (Table 6, p.776).\n *\n * LFO: sinusoidal, bipolar [-1, 1] (Dattorro p.767 — a triangular LFO gives an\n * unnatural piecewise-constant instantaneous pitch). Modulated delay in samples\n * is delayTime + depth*sin(phase); phase advances at `rate` Hz. Stereo uses a\n * quadrature LFO (90 deg per channel, p.776), realized by seeding each channel's\n * core with lfoPhase0 = ch * pi/2 at construction (see modulated-delay.ts).\n *\n * Interpolation: the read pointer lands between samples, so the fractional delay\n * is interpolated with a Lagrange FIR filter (Laakso, Valimaki, Karjalainen &\n * Laine, \"Splitting the Unit Delay\", IEEE SP Mag 1996, Eq. 42 / N=1,3 table).\n * Lagrange is FIR (stateless coefficients): updating the coefficients every\n * sample under modulation does NOT excite the recursive transient that plagues\n * an allpass fractional-delay filter (Laakso p.52), and its magnitude stays <= 1\n * for feedback-loop safety (Laakso p.42). Two orders are exposed:\n * - \"linear\" (N=1, Laakso Eq.43): h0 = 1-d, h1 = d.\n * - \"cubic\" (N=3, Laakso table): the 4-tap maximally-flat interpolator, with\n * D = 1 + d so the fraction sits between taps 1 and 2 (M_opt centering).\n * Allpass interpolation is deliberately deferred: under modulation it produces\n * transients and is clean only over ~+/-1 semitone (Dattorro p.770-774).\n *\n * NOTE on \"white chorus\": Dattorro (p.776) defines white chorus as negative\n * feedback AND allpass fractional-delay interpolation. This core uses the\n * white-chorus KNOB settings (the negative-feedback path) but substitutes\n * cubic-Lagrange interpolation (Laakso 1996) for Dattorro's allpass — it is\n * transient-safe under modulation (Laakso p.52) at the cost of some\n * high-frequency trough depth versus a true allpass-interpolated white chorus.\n *\n * This file holds ONLY pure numeric math (plain numbers + Float32Array). It has\n * no AudioWorklet / global dependencies, so it is unit-testable directly; the\n * worklet shell in modulated-delay.ts delegates to it.\n */\n\n/** Fractional-delay interpolation order. Both are Lagrange FIR (Laakso 1996). */\nexport type InterpolationMode = \"cubic\" | \"linear\";\n\n/** Parameters consumed per processing block. */\nexport interface ModulatedDelayParams {\n /** Nominal (center) delay in milliseconds — the fixed feedback tap center. */\n delayTime: number;\n /** Peak LFO delay excursion in milliseconds (CHORUS_WIDTH). 0 = pure delay. */\n depth: number;\n /** LFO frequency f_e in Hz. */\n rate: number;\n /** Feedback gain on the FIXED center tap. |feedback| <= 0.9999999 for stability. */\n feedback: number;\n /** Dry path gain (blend). */\n blend: number;\n /** Wet (modulated tap) gain (feedforward). */\n feedforward: number;\n /** Fractional-delay interpolation order (Lagrange FIR). */\n interpolation: InterpolationMode;\n}\n\n/**\n * The Dattorro Table 6 knob constant 1/sqrt(2), printed VERBATIM as 0.7071 in\n * the paper (p.775 — Dattorro quantized it to 4 decimals for his q23 fixed-point\n * hardware). The factory presets (cacophony.ts) and the white-chorus condition\n * blend = feedback use this exact printed value, NOT the full-precision\n * Math.SQRT1_2, so the shipped knobs match the paper's table digit-for-digit.\n */\n// biome-ignore lint/suspicious/noApproximativeNumericConstant: Dattorro Table 6 prints 0.7071 verbatim; matching the paper digit-for-digit is intentional, not an inexact Math.SQRT1_2.\nexport const DATTORRO_INV_SQRT2 = 0.7071;\n\n/**\n * Default AudioParam values for the modulated-delay worklet, in the worklet's\n * own units. SINGLE SOURCE OF TRUTH — the worklet's `parameterDescriptors`\n * (modulated-delay.ts) builds its descriptors from this table, and tests that\n * need the shipped defaults import it here rather than re-typing literals.\n *\n * Defaults: a short, dry, slow setting (a transparent near-bypass) — the\n * createDelay/createChorus/createFlanger/createVibrato/createDoubling factories\n * (cacophony.ts) spread Dattorro Table 6 knob presets over these.\n */\nexport const MODULATED_DELAY_DEFAULTS: Readonly<ModulatedDelayParams> = {\n delayTime: 5,\n depth: 0,\n rate: 0.5,\n feedback: 0,\n blend: 1,\n feedforward: DATTORRO_INV_SQRT2,\n interpolation: \"cubic\",\n};\n\n/** Maximum nominal delay (ms) the buffer is sized for (delayTime AudioParam max). */\nexport const MAX_DELAY_MS = 1000;\n/** Maximum LFO excursion (ms) the buffer is sized for (depth AudioParam max). */\nexport const MAX_DEPTH_MS = 50;\n/** Stability bound on |feedback| (Dattorro Table 6, q23 max, p.776). */\nexport const MAX_FEEDBACK = 0.9999999;\n\n/**\n * Lagrange linear (N=1) fractional-delay coefficients (Laakso 1996 Eq.43):\n * h0 = 1 - d, h1 = d for fractional delay d in [0, 1).\n * Two-tap FIR; h0 weights the integer tap, h1 the next sample. h0 + h1 = 1\n * (partition of unity, DC gain 1). Exposed pure for isolated testing.\n */\nexport function lagrangeLinear(d: number): [number, number] {\n return [1 - d, d];\n}\n\n/**\n * Lagrange cubic (N=3) fractional-delay coefficients (Laakso 1996, N=3 table,\n * p.41). The 4-tap maximally-flat interpolator evaluated at total delay\n * D = 1 + d, so the desired fraction d in [0, 1) sits between taps 1 and 2\n * (Laakso M_opt centering, Eq.21) — the regime where |H| <= 1:\n *\n * h0 = -(D-1)(D-2)(D-3)/6\n * h1 = D(D-2)(D-3)/2\n * h2 = -D(D-1)(D-3)/2\n * h3 = D(D-1)(D-2)/6\n *\n * The returned taps weight delay-line samples at integer offsets {i-1, i, i+1,\n * i+2} relative to the integer part of the read pointer. Sum = 1 (DC gain 1).\n */\nexport function lagrangeCubic(d: number): [number, number, number, number] {\n const D = 1 + d;\n return [\n (-(D - 1) * (D - 2) * (D - 3)) / 6,\n (D * (D - 2) * (D - 3)) / 2,\n (-D * (D - 1) * (D - 3)) / 2,\n (D * (D - 1) * (D - 2)) / 6,\n ];\n}\n\n/** Convert milliseconds to a (fractional) sample count at the given rate. */\nfunction msToSamples(ms: number, sampleRate: number): number {\n return (ms / 1000) * sampleRate;\n}\n\n/**\n * Stateful modulated-delay processor for ONE channel. Owns the circular delay\n * buffer, the write pointer and the LFO phase, and is reused block-to-block so\n * the delay-line contents, feedback recirculation and LFO continuity all survive\n * across process() calls — constructing a fresh instance per block would clear\n * the buffer and reset the LFO, producing audible clicks (same hazard documented\n * for the dynamics ballistics and ADAA history cores).\n *\n * The buffer is sized ONCE in the constructor for MAX_DELAY_MS + MAX_DEPTH_MS\n * plus a margin for the cubic interpolator's 4-tap window, so the read pointer\n * can never run past the allocation regardless of the (clamped) params.\n */\nexport class ModulatedDelayProcessor {\n /** Circular delay line. */\n private readonly buffer: Float32Array;\n /** Length of the circular buffer (samples). */\n private readonly size: number;\n /** Write head index into `buffer`. */\n private writeIndex = 0;\n /** Current LFO phase (radians). */\n private lfoPhase: number;\n\n constructor(\n private readonly sampleRate: number,\n lfoPhase0 = 0,\n ) {\n // Size for the worst case: max nominal delay + max excursion + cubic margin\n // (the cubic interpolator reads one sample before and two after the integer\n // tap). +4 samples of slack keeps every read index inside the allocation.\n const maxDelaySamples = msToSamples(MAX_DELAY_MS + MAX_DEPTH_MS, sampleRate);\n this.size = Math.ceil(maxDelaySamples) + 4;\n this.buffer = new Float32Array(this.size);\n this.lfoPhase = lfoPhase0;\n }\n\n /** Clear the delay line, write head and LFO phase. */\n reset(): void {\n this.buffer.fill(0);\n this.writeIndex = 0;\n this.lfoPhase = 0;\n }\n\n /** Read the delay line at integer offset `delaySamples` behind the write head. */\n private readInteger(delaySamples: number): number {\n let idx = this.writeIndex - delaySamples;\n idx %= this.size;\n if (idx < 0) idx += this.size;\n return this.buffer[idx];\n }\n\n /**\n * Read the delay line at a fractional delay `delaySamples` behind the write\n * head, interpolating with the selected Lagrange FIR (Laakso 1996).\n */\n private readFractional(delaySamples: number, mode: InterpolationMode): number {\n const intDelay = Math.floor(delaySamples);\n const frac = delaySamples - intDelay;\n\n // Linear (N=1), or cubic degraded to linear near the head: when intDelay < 1\n // the cubic's newest tap (intDelay-1) would be a FUTURE sample, so fall back\n // to the 2-tap linear interpolator. Linear reaches delay 0 exactly (offset 0\n // = the just-written current sample) for true through-zero flange/vibrato.\n if (mode === \"linear\" || intDelay < 1) {\n const [h0, h1] = lagrangeLinear(frac);\n // The desired delay intDelay+frac sits between offset intDelay (newer) and\n // offset intDelay+1 (older). h0 weights the integer tap, h1 the next-OLDER\n // sample, so a larger frac pulls toward the larger (older) delay.\n return h0 * this.readInteger(intDelay) + h1 * this.readInteger(intDelay + 1);\n }\n\n // Cubic: D = 1 + frac centers the fraction between taps 1 and 2, so the four\n // taps run newest->oldest at offsets {intDelay-1, intDelay, intDelay+1,\n // intDelay+2}, weighted by h0..h3 from the Laakso N=3 table in that order.\n const [h0, h1, h2, h3] = lagrangeCubic(frac);\n return (\n h0 * this.readInteger(intDelay - 1) +\n h1 * this.readInteger(intDelay) +\n h2 * this.readInteger(intDelay + 1) +\n h3 * this.readInteger(intDelay + 2)\n );\n }\n\n /**\n * Process a block. `input` and `output` may alias (in-place is fine).\n *\n * Per sample (Dattorro 1997 Fig. 36):\n * 1. center = fixed nominal delay tap (samples) (feedback tap)\n * 2. fb = feedback * delayLine[center] (fixed tap)\n * 3. w[n] = x[n] - feedback*delayLine[center] -> written (recirculation)\n * The summer SUBTRACTS the feedback (Fig. 36's negative-feedback summer,\n * p.775), realizing the paper's denominator 1 + feedback*z^-center (p.776).\n * 4. i.frac = center + depth * sin(lfoPhase) (modulated tap)\n * 5. wet = interpolate delayLine at i.frac (Lagrange FIR)\n * 6. y[n] = blend * w[n] + feedforward * wet (dry + wet mix)\n * The dry (blend) tap reads the recirculation node w[n], NOT the raw input\n * x[n], so both numerator taps share the feedback denominator and the\n * transfer function is exactly H(z) = (blend + feedforward*z^-i)/(1 +\n * feedback*z^-center) (p.776). This is the canonical allpass-comb: with\n * blend = feedback and feedforward = 1 it collapses to a unity-magnitude\n * allpass (the white-chorus condition, p.776). When feedback = 0, w[n] =\n * x[n], so the dry path is the plain input as expected for the no-feedback\n * presets (vibrato/industry chorus/doubling/echo).\n * The LFO phase advances by 2*pi*rate/sampleRate each sample.\n */\n process(input: Float32Array, output: Float32Array, params: ModulatedDelayParams): void {\n // k-rate-style coefficients computed once per block.\n const centerSamples = msToSamples(params.delayTime, this.sampleRate);\n const depthSamples = msToSamples(params.depth, this.sampleRate);\n const feedback = Math.max(-MAX_FEEDBACK, Math.min(MAX_FEEDBACK, params.feedback));\n const blend = params.blend;\n const feedforward = params.feedforward;\n const mode = params.interpolation;\n const phaseInc = (2 * Math.PI * params.rate) / this.sampleRate;\n\n // The fixed feedback tap reads the integer center; clamp to a valid offset.\n const centerTap = Math.max(0, Math.min(this.size - 1, Math.round(centerSamples)));\n\n const n = Math.min(input.length, output.length);\n for (let i = 0; i < n; i++) {\n const x = input[i];\n\n // Feedback from the FIXED center tap (Dattorro p.775: never the modulated\n // tap). Read BEFORE writing this sample so the loop delay is `center`.\n const fb = feedback * this.readInteger(centerTap);\n\n // Write the recirculated sample into the delay line at the write head.\n // The feedback is SUBTRACTED (Dattorro Fig. 36's negative-feedback summer,\n // p.775), so the transfer function denominator is 1 + feedback*z^-center.\n const w = x - fb;\n this.buffer[this.writeIndex] = w;\n\n // Modulated read pointer i.frac = center + depth * LFO (Dattorro eq, p.765).\n let readDelay = centerSamples + depthSamples * Math.sin(this.lfoPhase);\n // Allow the delay down to 0 so flangers/vibrato can sweep through absolute\n // zero (Dattorro p.775; Table 7 vibrato/flange onset 0 ms). Offset 0 is the\n // legitimate just-written current sample (we write before reading the wet\n // tap). readFractional degrades cubic -> linear near the head so no future\n // sample is ever read. The upper clamp keeps the cubic's oldest tap\n // (intDelay+2) inside the allocation.\n if (readDelay < 0) readDelay = 0;\n if (readDelay > this.size - 3) readDelay = this.size - 3;\n const wet = this.readFractional(readDelay, mode);\n\n // Fig. 36 output: dry (blend, off the recirculation node w[n]) + modulated\n // wet (feedforward). Both taps share the feedback denominator, giving\n // H(z) = (blend + feedforward*z^-i)/(1 + feedback*z^-center) (p.776).\n output[i] = blend * w + feedforward * wet;\n\n // Advance write head and LFO phase.\n this.writeIndex = (this.writeIndex + 1) % this.size;\n this.lfoPhase += phaseInc;\n if (this.lfoPhase >= 2 * Math.PI) this.lfoPhase -= 2 * Math.PI;\n }\n }\n}\n","/*\n * Time-stretch core — context-free, unit-testable OFFLINE independent\n * time-scaling (change tempo WITHOUT changing pitch).\n *\n * Algorithm: Zdeněk Průša & Nicki Holighaus, \"Phase Vocoder Done Right\",\n * Proc. EUSIPCO 2017 / arXiv:2202.07382 (2022). The synthesis phase is\n * reconstructed from the analysis-phase gradients via Phase Gradient Heap\n * Integration (PGHI / RTPGHI), originally Průša, Balazs & Søndergaard 2017\n * (\"A noniterative method for reconstruction of phase from STFT magnitude\")\n * and Průša & Søndergaard 2016 (RTPGHI, DAFx-16). No peak picking, no partial\n * tracking, no transient detection — phase coherence (both horizontal/time and\n * vertical/frequency) is enforced automatically by integrating along the\n * spectrogram's magnitude ridges (Průša 2022, p.2-3, Fig. 2).\n *\n * WHY THIS IS OFFLINE, NOT A REAL-TIME WORKLET\n * --------------------------------------------\n * The project's OLA worklet base (`src/processors/ola.ts`) hardwires the hop to\n * the 128-sample Web Audio render quantum and runs analysis hop == synthesis\n * hop, i.e. it is a UNITY-RATE analysis/synthesis loop and cannot change the\n * time base in real time (see reports/scout-resurrection.md §B3). Genuine\n * independent time-stretch requires analysis hop ≠ synthesis hop\n * (a_s = α·a_a). So this is implemented as a whole-buffer offline transform:\n * STFT the entire signal at analysis hop a_a → reconstruct synthesis phase with\n * PGHI heap integration → ISTFT/overlap-add at synthesis hop a_s = round(α·a_a)\n * → return a new buffer of length ≈ input·α at the SAME pitch. (Průša 2022,\n * Eq.7: α = a_s/a_a; output length = αL.)\n *\n * The spectrum/phase manipulation that carries the testable invariants lives\n * here, context-free, mirroring the project's core/shell split\n * (phase-vocoder-core.ts, waveshaper-core.ts, dynamics-core.ts).\n */\n\nimport FFT from \"fft.js\";\n\n/** Options for {@link timeStretch}. */\nexport interface TimeStretchOptions {\n /**\n * FFT length / window length `M` (samples), power of two (fft.js radix-4\n * constraint). Průša 2022 evaluation used M=8192; for general short cacophony\n * buffers a smaller window keeps frequency-resolution-vs-time-resolution\n * balanced. Default 2048 (matches the project's phase-vocoder block size).\n */\n fftSize?: number;\n /**\n * Analysis hop `a_a` (samples). Synthesis hop is `a_s = round(a_a·factor)`\n * (Průša 2022, Eq.7). Default = fftSize/4 (75% overlap, COLA-exact for Hann).\n */\n analysisHop?: number;\n /**\n * Relative tolerance `tol` for the significant-bin set; `abstol = tol·max(s)`\n * (Průša 2022, p.3, Algorithm 1 line 1). Default 1e-6 (paper value).\n */\n tol?: number;\n /**\n * Deterministic RNG seed for the random phase assigned to insignificant bins\n * (Algorithm 1 line 3). Default 1 — deterministic so tests are reproducible.\n */\n randomSeed?: number;\n}\n\nconst TWO_PI = 2 * Math.PI;\n\n/** Hann window of `length` (periodic / DFT-even form), matching phase-vocoder.ts. */\nfunction hannWindow(length: number): Float32Array {\n const win = new Float32Array(length);\n for (let i = 0; i < length; i++) {\n win[i] = 0.5 * (1 - Math.cos((TWO_PI * i) / length));\n }\n return win;\n}\n\n/**\n * Principal-argument operator `[x]_2π = x − 2π·round(x/2π)` — wraps to the\n * nearest interval of length 2π (Průša 2022, p.2, the `[·]_2π` operator with\n * round-to-closest-integer `⌈·⌋`). Used by every finite-difference scheme.\n */\nfunction princarg(x: number): number {\n return x - TWO_PI * Math.round(x / TWO_PI);\n}\n\n/** Mulberry32 — tiny deterministic PRNG so insignificant-bin phase is reproducible. */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Flat typed-array binary MAX-heap keyed by a Float64 magnitude, carrying an\n * Int32 payload (here: an encoded (bin, frameSelector) tuple). No per-pop\n * allocation — the backing arrays are sized once to the worst case and reused\n * by `clear()`. This is the \"self-sorting max-heap\" of Průša 2022 Algorithm 1\n * (always pops the globally-highest-magnitude known coefficient).\n */\nclass MaxHeap {\n private keys: Float64Array;\n private vals: Int32Array;\n private size = 0;\n\n constructor(capacity: number) {\n this.keys = new Float64Array(capacity);\n this.vals = new Int32Array(capacity);\n }\n\n clear(): void {\n this.size = 0;\n }\n\n get length(): number {\n return this.size;\n }\n\n push(key: number, val: number): void {\n let i = this.size++;\n this.keys[i] = key;\n this.vals[i] = val;\n // sift up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (this.keys[parent] >= this.keys[i]) break;\n this.swap(i, parent);\n i = parent;\n }\n }\n\n /** Pop the payload of the max-key element. Caller must check `length > 0`. */\n pop(): number {\n const topVal = this.vals[0];\n const last = --this.size;\n this.keys[0] = this.keys[last];\n this.vals[0] = this.vals[last];\n // sift down\n let i = 0;\n for (;;) {\n const l = 2 * i + 1;\n const r = l + 1;\n let largest = i;\n if (l < this.size && this.keys[l] > this.keys[largest]) largest = l;\n if (r < this.size && this.keys[r] > this.keys[largest]) largest = r;\n if (largest === i) break;\n this.swap(i, largest);\n i = largest;\n }\n return topVal;\n }\n\n private swap(a: number, b: number): void {\n const k = this.keys[a];\n this.keys[a] = this.keys[b];\n this.keys[b] = k;\n const v = this.vals[a];\n this.vals[a] = this.vals[b];\n this.vals[b] = v;\n }\n}\n\n/**\n * OFFLINE independent time-stretch of a single channel.\n *\n * @param input Mono signal.\n * @param factor Stretch factor `α = a_s/a_a` (Průša 2022, Eq.7). `>1` lengthens\n * (slower tempo), `<1` shortens (faster tempo); pitch unchanged.\n * @returns A new `Float32Array` of length ≈ round(input.length·factor) at the\n * SAME pitch as the input.\n *\n * Pipeline (Průša 2022, the STFT analysis-modify-synthesis loop, p.2-3):\n * 1. Whole-signal STFT (Hann window, analysis hop a_a) → magnitude s + phase φ_a.\n * 2. Phase gradients: CENTERED time derivative Δt φ_a (Eqs 13-15, heterodyned)\n * and the directional forward/backward frequency difference Δf φ_a (Eqs\n * 16-17). See the gradient block below for why the directional (rather than\n * the centered+trapezoidal) frequency integration is used.\n * 3. Per frame: PGHI heap integration (Algorithm 1) → synthesis phase φ_s,\n * using synthesis hop a_s for the trapezoidal TIME update (Eq.8) and\n * synthesis freq step b_s = α·b_a for the directional FREQUENCY update.\n * 4. ISTFT each synthesis frame (s·e^{iφ_s}) + Hann window, overlap-add at\n * synthesis hop a_s, normalized by the COLA window-energy envelope.\n */\nexport function timeStretch(input: Float32Array, factor: number, opts: TimeStretchOptions = {}): Float32Array {\n if (!(factor > 0)) {\n throw new Error(`timeStretch: factor must be > 0, got ${factor}`);\n }\n\n // FFT length M must be a power of two (fft.js radix constraint) and large\n // enough that the default hop M/4 is a positive integer with real overlap.\n // M < 4 would make the default hop fractional (M=2 → 0.5); require M ≥ 16 so\n // a Hann window has a meaningful main lobe and the default 75%-overlap hop is\n // a sensible integer.\n const M = opts.fftSize ?? 2048;\n if (!Number.isInteger(M) || M < 16 || (M & (M - 1)) !== 0) {\n throw new Error(`timeStretch: fftSize must be a power of two ≥ 16, got ${M}`);\n }\n const aA = opts.analysisHop ?? M / 4; // analysis hop a_a (M/4 is integer since M is a power of two ≥ 16)\n // The analysis hop must be a positive INTEGER: the frame loop reads\n // padded[n*aA + i], and a fractional hop reads off-grid (undefined → NaN).\n // It must also not exceed M (hops larger than the window leave gaps with no\n // COLA coverage).\n if (!Number.isInteger(aA) || aA < 1 || aA > M) {\n throw new Error(`timeStretch: analysisHop must be a positive integer in [1, fftSize=${M}], got ${aA}`);\n }\n const aS = Math.max(1, Math.round(aA * factor)); // synthesis hop a_s = round(α·a_a), Eq.7\n const tol = opts.tol ?? 1e-6;\n const rand = mulberry32(opts.randomSeed ?? 1);\n\n const nyq = M / 2; // index of the Nyquist bin\n const nBins = nyq + 1; // non-redundant bins 0..M/2\n\n // Analysis (b_a) and synthesis (b_s = α·b_a) frequency steps. The per-bin\n // angular spacing on the unwrapped axis is 2π·bin/M; the frequency update\n // accumulates the directional Δf gradient scaled by the synthesis step.\n // We carry the ratio (b_s/b_a) = a_s/a_a directly via the gradient scaling\n // below (see freq-update comment), so b_a/b_s never need explicit values.\n\n const win = hannWindow(M);\n\n // ---- Framing geometry -------------------------------------------------\n // Pad the front and back by one window so the first and last samples get\n // full window coverage (standard STFT zero-padding). Number of analysis\n // frames covers the whole padded signal at hop a_a.\n const pad = M;\n const paddedLen = input.length + 2 * pad;\n const padded = new Float32Array(paddedLen);\n padded.set(input, pad);\n\n const nFrames = Math.max(1, Math.floor((paddedLen - M) / aA) + 1);\n\n // ---- FFT scratch ------------------------------------------------------\n const fft = new FFT(M);\n // fft.js returns its complex arrays typed as any[]; the runtime is a flat\n // interleaved Float32Array [re,im,re,im,...] of length 2*M.\n const frameTime = new Float32Array(M);\n const freq = fft.createComplexArray() as unknown as Float32Array;\n\n // Per-frame magnitude `s` and analysis phase `φ_a`, bins 0..nyq.\n const mag: Float32Array[] = new Array(nFrames);\n const phase: Float32Array[] = new Array(nFrames);\n\n for (let n = 0; n < nFrames; n++) {\n const start = n * aA;\n for (let i = 0; i < M; i++) {\n frameTime[i] = padded[start + i] * win[i];\n }\n fft.realTransform(freq, frameTime);\n const s = new Float32Array(nBins);\n const ph = new Float32Array(nBins);\n for (let m = 0; m < nBins; m++) {\n const re = freq[2 * m];\n const im = freq[2 * m + 1];\n s[m] = Math.hypot(re, im);\n ph[m] = Math.atan2(im, re);\n }\n mag[n] = s;\n phase[n] = ph;\n }\n\n // ---- Phase gradients (Průša 2022, centered finite differences) --------\n // Time-direction derivative Δt φ_a (Eqs 13-15), HETERODYNED: the linear\n // phase advance 2π·m·a_a/M of a stationary sinusoid is removed before the\n // principal-argument wrap, then added back. We store Δt already MULTIPLIED\n // BY a_a (i.e. the per-analysis-hop phase increment Δt·a_a), because the\n // synthesis time update multiplies by a_s and the only thing that matters is\n // the ratio a_s/a_a — storing the per-hop increment lets the trapezoidal\n // update (Eq.8) just scale by (a_s/a_a)/... See the heap loop below.\n //\n // dtInc[n][m] = a_a · (Δt,cent φ_a)(m,n) (radians advanced per analysis hop)\n // dfFwd[n][m] = [φ_a(m+1,n) − φ_a(m,n)]_2π — the FORWARD wrapped frequency-\n // direction difference (Eq.17 numerator, b_a units). Used\n // DIRECTIONALLY: forward (Δf,fwd) for up-propagation, backward\n // (Δf,back(m) = Δf,fwd(m-1)) for down-propagation.\n //\n // FREQUENCY-INTEGRATION SCHEME — why forward/backward (rectangle) rather than\n // the centered+trapezoidal form of Algorithm 1 lines 17/22. The paper presents\n // backward (Eq.16), forward (Eq.17) AND centered (Eq.18) finite-difference\n // schemes for the frequency-direction gradient (it makes the explicit \"any of\n // the schemes can be used\" remark for the time-direction derivative and applies\n // the same backward/forward/centered choice to frequency). Algorithm 1 as written (lines 17/22)\n // itself uses trapezoidal frequency integration; we DELIBERATELY do not, for\n // the measured reason documented below. Directional forward/backward\n // integration telescopes EXACTLY to the analysis phase along a ridge:\n // up: φ_s(m+1) = φ_s(m) + Δf,fwd(m) ⇒ φ_a(m+1) − φ_a(m)\n // down: φ_s(m-1) = φ_s(m) − Δf,fwd(m-1) ⇒ φ_a(m-1) − φ_a(m)\n // so it preserves both spectral identity at α=1 and the off-bin tone's peak\n // location. The centered+trapezoidal variant was implemented faithfully and\n // MEASURED to be strictly worse here: it lowers tonal spectral purity in every\n // tested case (e.g. 440 Hz factor=1: 0.94 → 0.56) and SHIFTS the dominant bin\n // of an off-bin tone at factor≠1 (440 Hz, bin 41 → 45 at factor=2), breaking\n // pitch preservation, while improving chirp spectral spread only marginally\n // (≈8% at factor 2, ≈1% at factor 3) and single-impulse temporal spread ≈11%.\n // See reports/fix-B3-timestretch.md for the full measurements. This is a\n // DELIBERATE, MEASURED deviation from Algorithm 1's trapezoidal frequency\n // propagation (NOT the paper's baseline), chosen because trapezoidal broke\n // pitch preservation in our tests; the forward/backward directional schemes\n // are themselves paper-sanctioned gradient estimates (Eq.16/17).\n const dtInc: Float32Array[] = new Array(nFrames);\n const dfFwd: Float32Array[] = new Array(nFrames);\n\n for (let n = 0; n < nFrames; n++) {\n dtInc[n] = new Float32Array(nBins);\n dfFwd[n] = new Float32Array(nBins);\n }\n\n for (let n = 0; n < nFrames; n++) {\n const ph = phase[n];\n const phPrev = n > 0 ? phase[n - 1] : null;\n const phNext = n < nFrames - 1 ? phase[n + 1] : null;\n\n for (let m = 0; m < nBins; m++) {\n // expected linear advance of bin m over one analysis hop\n const expected = (TWO_PI * m * aA) / M;\n\n // backward & forward heterodyned time differences (Eqs 13-14):\n // Δt,back·a_a = [φ(n) − φ(n−1) − expected]_2π + expected\n // Δt,fwd·a_a = [φ(n+1) − φ(n) − expected]_2π + expected\n let dt: number;\n if (phPrev && phNext) {\n const back = princarg(ph[m] - phPrev[m] - expected) + expected;\n const fwd = princarg(phNext[m] - ph[m] - expected) + expected;\n dt = 0.5 * (back + fwd); // centered (Eq.15)\n } else if (phPrev) {\n dt = princarg(ph[m] - phPrev[m] - expected) + expected; // backward only at last frame\n } else if (phNext) {\n dt = princarg(phNext[m] - ph[m] - expected) + expected; // forward only at first frame\n } else {\n dt = expected; // single frame: assume stationary\n }\n dtInc[n][m] = dt;\n\n // frequency-direction FORWARD wrapped difference (Eq.17): the directed\n // increment from bin m to bin m+1. No heterodyning (a pure sinusoid has\n // Δf φ ≈ 0, the paper's testable property, p.2). The last bin has no m+1.\n dfFwd[n][m] = m < nBins - 1 ? princarg(ph[m + 1] - ph[m]) : 0;\n }\n }\n\n // ---- PGHI heap integration (Průša 2022, Algorithm 1) ------------------\n // Produce the synthesis phase φ_s frame by frame. The frequency update uses\n // synthesis freq step b_s = α·b_a; since dfFwd holds the per-bin directional\n // (forward/backward, Eq.16/17) difference (already in \"per b_a\" units of\n // radians/binstep), scaling it by the factor α = a_s/a_a converts the b_a\n // step to the b_s step (b_s = α·b_a).\n const ratio = aS / aA; // = α, the time-scaling factor (Eq.7)\n const synthPhase: Float32Array[] = new Array(nFrames);\n for (let n = 0; n < nFrames; n++) synthPhase[n] = new Float32Array(nBins);\n\n // Heap payload encodes (bin << 1 | sel): sel=0 -> previous frame (n-1),\n // sel=1 -> current frame (n). Capacity 2·nBins (prev seeds + current bins).\n const heap = new MaxHeap(2 * nBins);\n const assigned = new Uint8Array(nBins); // whether φ_s(m,n) computed this frame\n const inSet = new Uint8Array(nBins); // significant set I membership this frame\n\n for (let n = 0; n < nFrames; n++) {\n const s = mag[n];\n const sPrev = n > 0 ? mag[n - 1] : null;\n const phiS = synthPhase[n];\n const phiSPrev = n > 0 ? synthPhase[n - 1] : null;\n\n // Algorithm 1 line 1: abstol = tol · max(s(·,n) ∪ s(·,n-1)).\n let maxS = 0;\n for (let m = 0; m < nBins; m++) {\n if (s[m] > maxS) maxS = s[m];\n if (sPrev && sPrev[m] > maxS) maxS = sPrev[m];\n }\n const abstol = tol * maxS;\n\n // Lines 2-3: significant set I = { m : s(m,n) > abstol }; insignificant\n // bins get random phase and do not propagate.\n heap.clear();\n let remaining = 0;\n for (let m = 0; m < nBins; m++) {\n assigned[m] = 0;\n if (s[m] > abstol) {\n inSet[m] = 1;\n remaining++;\n } else {\n inSet[m] = 0;\n phiS[m] = TWO_PI * rand(); // random phase, Algorithm 1 line 3\n assigned[m] = 1; // resolved (will not be propagated to)\n }\n }\n\n if (n === 0 || !phiSPrev || !sPrev) {\n // First frame (or no previous synthesis phase): there is no prior frame to\n // time-propagate from, so the column is integrated purely in frequency.\n // PGHI (Průša 2022, Algorithm 1) requires EVERY significant bin be assigned\n // through the integration. The significant set may be several DISCONNECTED\n // ridges (islands separated by insignificant bins): seeding only the single\n // global-max bin and frequency-spreading reaches just ITS island, leaving\n // every other island's bins at the Float32Array default (phase 0) — a\n // violation of the PGHI guarantee. So seed each connected component in\n // turn: anchor the highest-magnitude UNASSIGNED significant bin to its\n // analysis phase, frequency-spread the heap through its component, then\n // re-seed the next component until no significant bin is unassigned.\n while (remaining > 0) {\n let seed = -1;\n let seedMag = -1;\n for (let m = 0; m < nBins; m++) {\n if (inSet[m] && !assigned[m] && s[m] > seedMag) {\n seedMag = s[m];\n seed = m;\n }\n }\n if (seed < 0) break; // safety: no significant bin left\n phiS[seed] = phase[n][seed];\n assigned[seed] = 1;\n remaining--;\n heap.push(s[seed], (seed << 1) | 1);\n\n // Frequency-only spreading within this component (b_s = α·b_a folded into\n // `ratio`). Drains the heap to the component boundary; the outer loop then\n // re-seeds the next disconnected component.\n // up: φ_s(m+1)=φ_s(m)+b_s·Δf,fwd(m)\n // down: φ_s(m-1)=φ_s(m)−b_s·Δf,fwd(m-1) [= Δf,back(m)]\n while (heap.length > 0) {\n const top = heap.pop();\n const mh = top >> 1;\n if (mh + 1 < nBins && inSet[mh + 1] && !assigned[mh + 1]) {\n phiS[mh + 1] = phiS[mh] + ratio * dfFwd[n][mh];\n assigned[mh + 1] = 1;\n remaining--;\n heap.push(s[mh + 1], ((mh + 1) << 1) | 1);\n }\n if (mh - 1 >= 0 && inSet[mh - 1] && !assigned[mh - 1]) {\n phiS[mh - 1] = phiS[mh] - ratio * dfFwd[n][mh - 1];\n assigned[mh - 1] = 1;\n remaining--;\n heap.push(s[mh - 1], ((mh - 1) << 1) | 1);\n }\n }\n }\n continue;\n }\n\n // Lines 4-5: seed the heap with all significant PREVIOUS-frame coefficients\n // (their synthesis phase is already known from frame n-1).\n for (let m = 0; m < nBins; m++) {\n if (inSet[m]) {\n // key on the previous frame's magnitude (the known coefficient's mag)\n heap.push(sPrev[m], (m << 1) | 0); // sel=0 -> previous frame\n }\n }\n\n // Lines 6-27: integrate until all significant current-frame bins assigned.\n while (heap.length > 0 && remaining > 0) {\n const top = heap.pop();\n const mh = top >> 1;\n const sel = top & 1;\n\n if (sel === 0) {\n // Top is a previous-frame coefficient (n-1): propagate in TIME into the\n // current frame (Algorithm 1 lines 8-13). Trapezoidal time update\n // (Průša 2022, Eq.8):\n // φ_s(m,n) = φ_s(m,n-1) + (a_s/2)·[Δt(m,n-1) + Δt(m,n)]\n // dtInc holds the per-analysis-hop increment (Δt·a_a); the synthesis\n // step a_s = α·a_a, so a_s·Δt = α·(Δt·a_a) = ratio·dtInc.\n if (inSet[mh] && !assigned[mh]) {\n phiS[mh] = phiSPrev[mh] + 0.5 * ratio * (dtInc[n - 1][mh] + dtInc[n][mh]);\n assigned[mh] = 1;\n remaining--;\n heap.push(s[mh], (mh << 1) | 1); // now a current-frame known coeff\n }\n } else {\n // Top is a current-frame coefficient (n) with known synthesis phase:\n // propagate in FREQUENCY to m±1 (Algorithm 1 lines 15-26) using the\n // directed forward/backward gradient Δf,fwd with synthesis freq step\n // b_s = α·b_a (folded into `ratio`):\n // φ_s(m+1,n) = φ_s(m,n) + b_s·Δf,fwd(m,n)\n // φ_s(m-1,n) = φ_s(m,n) − b_s·Δf,fwd(m-1,n) [= Δf,back(m,n)]\n if (mh + 1 < nBins && inSet[mh + 1] && !assigned[mh + 1]) {\n phiS[mh + 1] = phiS[mh] + ratio * dfFwd[n][mh];\n assigned[mh + 1] = 1;\n remaining--;\n heap.push(s[mh + 1], ((mh + 1) << 1) | 1);\n }\n if (mh - 1 >= 0 && inSet[mh - 1] && !assigned[mh - 1]) {\n phiS[mh - 1] = phiS[mh] - ratio * dfFwd[n][mh - 1];\n assigned[mh - 1] = 1;\n remaining--;\n heap.push(s[mh - 1], ((mh - 1) << 1) | 1);\n }\n }\n }\n\n // Any significant bins the heap could not reach (disconnected ridge): fall\n // back to a pure time update so they still advance coherently rather than\n // being left at zero. This never double-assigns (guarded by `assigned`).\n if (remaining > 0) {\n for (let m = 0; m < nBins; m++) {\n if (inSet[m] && !assigned[m]) {\n phiS[m] = phiSPrev[m] + 0.5 * ratio * (dtInc[n - 1][m] + dtInc[n][m]);\n assigned[m] = 1;\n remaining--;\n }\n }\n }\n }\n\n // ---- ISTFT + overlap-add resynthesis at synthesis hop a_s -------------\n // Per-frame inverse STFT of s·e^{iφ_s} (Průša 2022, the per-frame inverse,\n // p.2), Hann-windowed again, overlap-added at synthesis hop a_s, then divided\n // by the accumulated squared-window envelope (canonical COLA normalization /\n // dual synthesis window, p.2). Output length = round(input.length·factor).\n const outLen = Math.max(1, Math.round(input.length * factor));\n const synthPad = M;\n const synthBufLen = (nFrames - 1) * aS + M + synthPad;\n const acc = new Float32Array(synthBufLen);\n const winEnergy = new Float32Array(synthBufLen);\n\n const specOut = fft.createComplexArray() as unknown as Float32Array;\n const timeOut = fft.createComplexArray() as unknown as Float32Array;\n\n for (let n = 0; n < nFrames; n++) {\n const s = mag[n];\n const phiS = synthPhase[n];\n // Build the half-spectrum (bins 0..nyq) as re/im, then mirror.\n for (let m = 0; m < nBins; m++) {\n const re = s[m] * Math.cos(phiS[m]);\n const im = s[m] * Math.sin(phiS[m]);\n specOut[2 * m] = re;\n specOut[2 * m + 1] = im;\n }\n // Nyquist and DC must be real for a real signal; imag already ~0 since we\n // mirror with conjugate symmetry next.\n fft.completeSpectrum(specOut);\n fft.inverseTransform(timeOut, specOut);\n\n const start = n * aS;\n for (let i = 0; i < M; i++) {\n const w = win[i];\n // timeOut is interleaved; real part at even indices.\n acc[start + i] += timeOut[2 * i] * w;\n winEnergy[start + i] += w * w;\n }\n }\n\n // Normalize by the COLA window-energy envelope (canonical dual window). The\n // analysis front-pad was `pad`; the synthesis frames begin at index 0 here,\n // and the analysis pad maps to a synthesis-side offset of round(pad·factor).\n for (let i = 0; i < synthBufLen; i++) {\n if (winEnergy[i] > 1e-8) acc[i] /= winEnergy[i];\n }\n\n const offset = Math.round(pad * factor);\n const out = new Float32Array(outLen);\n for (let i = 0; i < outLen; i++) {\n const src = offset + i;\n out[i] = src < synthBufLen ? acc[src] : 0;\n }\n return out;\n}\n\n/**\n * Multichannel convenience: time-stretch each channel independently with the\n * same factor/options. Returns one new `Float32Array` per channel, all the same\n * length (≈ round(channelLength·factor)).\n */\nexport function timeStretchChannels(\n channels: Float32Array[],\n factor: number,\n opts: TimeStretchOptions = {},\n): Float32Array[] {\n return channels.map((ch) => timeStretch(ch, factor, opts));\n}\n","/**\n * The Playback class encapsulates the functionality for playing audio in a web application.\n * It integrates with the repo's audio-context abstractions so playback works across native and mocked contexts.\n * This class allows for the manipulation of audio playback through various features such as:\n * - Playing and stopping audio\n * - Looping audio a specific number of times or infinitely\n * - Adjusting volume and playback rate\n * - Applying stereo or 3D (HRTF) panning\n * - Adding and removing filters to modify the audio output\n * - Handling audio looping with custom logic\n * - Fading audio in and out linearly or exponentially\n * - Seeking to specific points in the audio\n * - Checking if the audio is currently playing\n * - Cleaning up resources when the audio is no longer needed\n *\n * The class is designed to be flexible and can be used with different types of audio sources,\n * including buffer sources and media elements. It also provides detailed control over the audio's\n * spatial characteristics when using 3D audio.\n */\n\nimport { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, FadeType, LoopCount, PanType } from \"./cacophony\";\nimport type {\n AudioBuffer,\n AudioNode,\n AudioParam,\n AudioWorkletNode,\n BaseContext,\n BiquadFilterNode,\n GainNode,\n SourceNode,\n} from \"./context\";\nimport type { Sound } from \"./sound\";\nimport { WORKLETS } from \"./worklets\";\n\ntype PlaybackCloneOverrides = {\n loopCount: LoopCount;\n panType: PanType;\n};\n\nexport class Playback extends BasePlayback implements BaseSound {\n private context: BaseContext;\n public declare source?: SourceNode;\n loopCount: LoopCount = 0;\n currentLoop: number = 0;\n private buffer?: AudioBuffer;\n private _offset: number = 0;\n private _startTime: number = 0;\n private _playbackRate: number = 1;\n /**\n * Promise that tracks an in-flight media-element `.play()` settlement.\n * Set when the media element's `play()` returns a pending promise; cleared\n * once that promise settles. Used by `loopEnded` to avoid racing source-state\n * mutations (`seek`/`play`) against the deferred state transition driven by\n * this promise.\n */\n private _mediaPlayPromise?: Promise<void>;\n _fadeInConfig?: { duration: number; type: FadeType; perLoop: boolean; targetVolume: number };\n _fadeOutConfig?: { duration: number; type: FadeType };\n _loopEndCallback?: () => void;\n /**\n * Desired pitch-shift factor (1 = no shift, 2 = +1 octave, 0.5 = -1 octave).\n * Stored even before the worklet node exists so a value set on a cleaned-up /\n * not-yet-built playback is honoured once {@link setPitchShift} builds the node.\n */\n private _pitchFactor: number = 1;\n /**\n * Creates an instance of the Playback class.\n * @throws {Error} Throws an error if an invalid pan type is provided.\n */\n\n constructor(\n public origin: Sound,\n source: SourceNode,\n gainNode: GainNode,\n ) {\n super(origin);\n this.context = origin.context;\n this.loopCount = origin.loopCount;\n this.setPanType(origin.panType, origin.context);\n this.source = source;\n if (\"buffer\" in source && source.buffer) {\n this.buffer = source.buffer;\n }\n this.setupSourceNode(source);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n }\n\n override setPanType(panType: PanType, audioContext: BaseContext): void {\n const previousPanner = this.panner;\n super.setPanType(panType, audioContext);\n\n if (this.panner === previousPanner || !this.panner || !this.source || !this.gainNode) {\n return;\n }\n\n this.setEffectChainEndpoints(this.source, this.panner);\n this.panner.connect(this.gainNode);\n }\n\n private setupSourceNode(source: SourceNode) {\n if (\"mediaElement\" in source && source.mediaElement) {\n source.mediaElement.onended = this.loopEnded;\n } else if (\"onended\" in source) {\n source.onended = this.loopEnded;\n } else {\n throw new Error(\"Unsupported source type\");\n }\n }\n\n /**\n * Gets the duration of the audio in seconds.\n * @returns {number} The duration of the audio or NaN if the duration is unknown.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n get duration() {\n if (!this.source) {\n throw new Error(\"Cannot get duration of a sound that has been cleaned up\");\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n return this.source.mediaElement.duration;\n }\n if (!this.buffer) {\n return NaN;\n }\n return this.buffer.duration || NaN;\n }\n\n /**\n * Gets the current playback rate of the audio.\n */\n\n get playbackRate() {\n return this._playbackRate;\n }\n\n get volume(): number {\n return super.volume;\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n /**\n * Sets the playback rate of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up or if the source type is unsupported.\n */\n\n set playbackRate(rate: number) {\n if (rate <= 0) {\n throw new Error(\"Playback rate must be greater than 0\");\n }\n if (this._state === \"playing\") {\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n this._offset += elapsed;\n this._startTime = this.context.currentTime;\n }\n this._playbackRate = rate;\n if (!this.source) {\n return;\n }\n if (\"playbackRate\" in this.source) {\n this.source.playbackRate.value = rate;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.playbackRate = rate;\n }\n }\n\n /**\n * Handles the loop event when the audio ends.\n * This method is bound to the 'onended' event of the audio source.\n * It manages looping logic and restarts playback if necessary.\n */\n loopEnded = (): void => {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n // For media-element sources, a previous play() may still have a pending\n // settlement promise -- the deferred state transition races with the\n // seek+play we are about to perform. Defer the loop work until the\n // pending play resolves so source mutations don't interleave.\n if (\"mediaElement\" in this.source && this._mediaPlayPromise) {\n void this._mediaPlayPromise.then(() => this._runLoopEnded());\n return;\n }\n\n this._runLoopEnded();\n };\n\n private _runLoopEnded(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n this.currentLoop++;\n\n if (this.loopCount !== \"infinite\" && this.currentLoop > this.loopCount) {\n // Final iteration -- emit ended, then either fade out or stop immediately\n this.emit(\"ended\", undefined);\n if (this._fadeOutConfig) {\n // Guard the chained stop: an external stop()/cleanup() between the\n // fade resolving and this .then would otherwise run stop()/source\n // access against torn-down state. cancelFade() in stop()/cleanup()\n // resolves the fade promise synchronously, which is what makes this\n // .then fire at all after a teardown.\n void this.fadeOut(this._fadeOutConfig.duration, this._fadeOutConfig.type).then(() => {\n if (this._state !== \"stopped\" && this.source) {\n this.stop();\n this.removeFromOrigin();\n }\n });\n } else {\n this.stop();\n this.removeFromOrigin();\n }\n } else {\n this._loopEndCallback?.();\n this.seek(0); // Resets offset and handles play/pause state internally.\n // seek() calls pause() then play() when wasPlaying is true,\n // so play() handles both media-element and buffer sources correctly.\n if (this._state !== \"playing\" && this.source && !(\"mediaElement\" in this.source)) {\n // For AudioBufferSourceNode only: if seek() didn't restart playback\n // (e.g., state wasn't Playing before seek), start it now.\n this.play();\n }\n\n // Re-trigger fade-in if perLoop config is set\n if (this._fadeInConfig?.perLoop && this.gainNode) {\n this.gainNode.gain.setValueAtTime(0.0001, this.gainNode.context.currentTime);\n this.fadeTo(this._fadeInConfig.targetVolume, this._fadeInConfig.duration, this._fadeInConfig.type);\n }\n }\n }\n\n /**\n * Starts playing the audio.\n * @returns {[this]} Returns the instance of the Playback class for chaining.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a sound that has been cleaned up\");\n }\n\n if (this._state === \"playing\") {\n return [this];\n }\n\n const isResume = this._state === \"paused\";\n\n try {\n let mediaPlayPromise: Promise<void> | undefined;\n\n if (this._state === \"paused\") {\n // If we're resuming from a paused state\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n mediaPlayPromise = this.source.mediaElement.play();\n } else {\n // For non-mediaElement sources, we need to recreate and start the source\n this.recreateSource();\n if (\"start\" in this.source && this.source.start) {\n this.source.start(0, this._offset);\n }\n }\n } else {\n // If we're starting from the beginning or a stopped state\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.currentTime = this._offset;\n mediaPlayPromise = this.source.mediaElement.play();\n } else if (\"start\" in this.source && this.source.start) {\n this.recreateSource();\n this.source.start(0, this._offset);\n }\n }\n\n if (mediaPlayPromise) {\n // For media-element sources, defer state and events until browser accepts playback\n // Stash the pending promise so loopEnded (and any future caller that\n // needs to sequence after the deferred state transition) can await it\n // rather than racing source-state mutations against it.\n const tracked = mediaPlayPromise\n .then(\n () => {\n this._startTime = this.context.currentTime;\n this.emitPlayStarted(isResume);\n },\n (error: Error) => {\n void this.emitAsync(\"error\", {\n error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: true,\n });\n },\n )\n .finally(() => {\n if (this._mediaPlayPromise === tracked) {\n this._mediaPlayPromise = undefined;\n }\n });\n this._mediaPlayPromise = tracked;\n } else {\n // For buffer sources, state transition is immediate (start() is synchronous)\n this._startTime = this.context.currentTime;\n this.emitPlayStarted(isResume);\n }\n\n return [this];\n } catch (error) {\n this.emitAsync(\"error\", {\n error: error as Error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: true,\n });\n throw error;\n }\n }\n\n pause(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n this._offset += elapsed;\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.pause();\n } else if (\"stop\" in this.source) {\n // For AudioBufferSourceNode and OscillatorNode, stop the source.\n // It cannot be restarted; a new one will be created on play().\n this.source.stop();\n }\n\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source) {\n throw new Error(\"Cannot stop a sound that has been cleaned up\");\n }\n if (this._state === \"stopped\" || this._state === \"unplayed\") {\n return;\n }\n\n this.cancelFade();\n\n if (\"stop\" in this.source && this._state === \"playing\") {\n this.source.stop();\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.pause();\n this.source.mediaElement.currentTime = 0;\n }\n\n this._offset = 0;\n this._startTime = 0;\n this.emitStopped();\n }\n\n /**\n * Fades in from silence to the current volume.\n * Optionally stores config to re-trigger the fade on each loop iteration.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @param {object} options - Optional. Set perLoop: true to re-trigger fadeIn on each loop.\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeIn(duration: number, type?: FadeType, options?: { perLoop?: boolean }): Promise<void> {\n if (options?.perLoop) {\n this._fadeInConfig = {\n duration,\n type: type ?? \"linear\",\n perLoop: true,\n targetVolume: this.gainNode!.gain.value,\n };\n }\n return super.fadeIn(duration, type);\n }\n\n /**\n * Configures a fade-out to be applied when the playback ends naturally (last loop iteration).\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n */\n configureFadeOut(duration: number, type?: FadeType): void {\n this._fadeOutConfig = { duration, type: type ?? \"linear\" };\n }\n\n /**\n * Fades out then stops the playback.\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes and playback is stopped.\n */\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n // Guard the chained stop: if an external stop()/cleanup() runs between\n // the fade resolving and this .then, the chained stop() would otherwise\n // throw against torn-down state (\"Cannot stop a sound that has been\n // cleaned up\"). cancelFade() inside stop()/cleanup() resolves the fade\n // promise synchronously, so this .then will still fire -- the guard\n // makes it a no-op when the playback is already stopped or cleaned up.\n return this.fadeOut(duration, type).then(() => {\n if (this._state !== \"stopped\" && this.source) {\n this.stop();\n }\n });\n }\n\n seek(time: number): void {\n if (!this.source || !this.gainNode || !this.panner) {\n throw new Error(\"Cannot seek a sound that has been cleaned up\");\n }\n if (!Number.isFinite(time) || time < 0) {\n throw new Error(\"Invalid time value for seek\");\n }\n\n if (\n this.origin.soundType === \"streaming\" &&\n \"mediaElement\" in this.source &&\n (this.source.mediaElement.seekable?.length ?? 0) === 0\n ) {\n if (this.source.mediaElement.duration === Number.POSITIVE_INFINITY) {\n throw new Error(\"Live media-element streams do not expose a seekable range\");\n }\n throw new Error(\"Media-element stream does not currently expose a seekable range\");\n }\n\n const wasPlaying = this._state === \"playing\";\n if (wasPlaying) {\n this.pause();\n }\n\n this._offset = time;\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.currentTime = time;\n }\n // For non-media elements, play() will handle recreating the source if needed.\n\n if (wasPlaying) {\n this.play();\n }\n }\n\n get currentTime(): number {\n if (this._state === \"playing\") {\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n return this._offset + elapsed;\n } else {\n return this._offset;\n }\n }\n\n private removeFromOrigin(): void {\n const idx = this.origin.playbacks.indexOf(this);\n if (idx !== -1) {\n this.origin.playbacks.splice(idx, 1);\n }\n }\n\n private recreateSource() {\n if (!this.buffer || !this.panner || !this.context || !this.gainNode) {\n throw new Error(\"Cannot recreate source of a sound that has been cleaned up\");\n }\n try {\n if (this.source) {\n // It's crucial to nullify onended of the old source if it's an AudioBufferSourceNode (or similar non-restartable source),\n // as its onended event could otherwise interfere with the new source created for seek/resume.\n // MediaElementAudioSourceNode is handled differently as its underlying element can be paused/played.\n if (!(\"mediaElement\" in this.source) && \"onended\" in this.source) {\n this.source.onended = null;\n }\n this.source.disconnect();\n }\n this.source = this.context.createBufferSource();\n this.source.buffer = this.buffer;\n this.setEffectChainEndpoints(this.source, this.panner);\n this.source.onended = this.loopEnded;\n this.playbackRate = this._playbackRate;\n } catch (error) {\n this.emitAsync(\"error\", {\n error: error as Error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: false,\n });\n throw error;\n }\n }\n\n /**\n * Sets whether the audio source should loop.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n set sourceLoop(loop: boolean) {\n if (!this.source) {\n throw new Error(\"Cannot set loop on a sound that has been cleaned up\");\n }\n if (\"loop\" in this.source) {\n this.source.loop = loop;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.loop = loop;\n }\n }\n\n /**\n * Cleans up resources used by the Playback instance.\n * This method should be called when the audio is no longer needed to free up resources.\n */\n\n cleanup(): void {\n if (!this.source) {\n return; // Already cleaned up\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n const mediaElement = this.source.mediaElement;\n mediaElement.pause();\n try {\n mediaElement.currentTime = 0;\n } catch {\n // Live media without a seekable range can reject currentTime writes.\n }\n mediaElement.loop = false;\n mediaElement.onended = null;\n mediaElement.src = \"\";\n mediaElement.load();\n } else if (\"onended\" in this.source) {\n this.source.onended = null;\n }\n this._offset = 0;\n this._startTime = 0;\n this.markStopped();\n this.currentLoop = 0;\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n\n private assertNotCleanedUp(): void {\n if (!this.source || !this.gainNode || !this.panner) {\n throw new Error(\"Cannot perform operation on a sound that has been cleaned up\");\n }\n }\n\n addFilter(filter: BiquadFilterNode): void {\n this.assertNotCleanedUp();\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n this.assertNotCleanedUp();\n super.removeFilter(filter);\n }\n\n /**\n * Sets or gets the loop count for the audio.\n * @param {LoopCount} loopCount - The number of times the audio should loop. 'infinite' for endless looping.\n * @returns {LoopCount} The loop count if no parameter is provided.\n * @throws {Error} Throws an error if the sound has been cleaned up or if the source type is unsupported.\n */\n\n loop(loopCount?: LoopCount): LoopCount {\n if (!this.source) {\n throw new Error(\"Cannot loop a sound that has been cleaned up\");\n }\n if (loopCount !== undefined) {\n this.loopCount = loopCount === \"infinite\" ? \"infinite\" : Math.max(0, loopCount);\n this.currentLoop = 0;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n const mediaElement = this.source.mediaElement;\n mediaElement.loop = this.loopCount === \"infinite\";\n } else if (\"loop\" in this.source) {\n this.source.loop = this.loopCount === \"infinite\";\n if (this.source.buffer) {\n this.source.loopEnd = this.source.buffer.duration;\n this.source.loopStart = 0;\n }\n } else {\n throw new Error(\"Unsupported source type\");\n }\n return this.loopCount;\n }\n\n /**\n * Sets the pitch-shift factor for this playback, resurrecting the dormant\n * phase-vocoder worklet (Jean Laroche & Mark Dolson, \"New Phase-Vocoder\n * Techniques for Pitch-Shifting, Harmonizing and Other Exotic Effects\",\n * 1999 IEEE WASPAA — peak-based pitch shift with Identity Phase-Locking).\n *\n * On first use (factor !== 1) the phase-vocoder AudioWorkletNode is built and\n * added as an ordinary entry at the tail of the pre-panner effect chain:\n * source → [filters] → phase vocoder → panner → gainNode. The factor is\n * forwarded to the node's `pitchFactor` AudioParam (1 = no shift, 2 = +1\n * octave, 0.5 = -1 octave).\n *\n * @param factor Pitch multiplier (> 0).\n * @throws {Error} if the playback has been cleaned up or factor <= 0.\n */\n async setPitchShift(factor: number): Promise<void> {\n this.assertNotCleanedUp();\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(\"Pitch-shift factor must be greater than 0\");\n }\n this._pitchFactor = factor;\n const effectChain = this._effectChain;\n if (!effectChain) {\n throw new Error(\"Cannot pitch-shift a playback before its effect chain is initialized\");\n }\n let pitchShiftNode = effectChain.nodes.find((node) => {\n const parameters = (node as AudioWorkletNode).parameters;\n return parameters && typeof parameters.get === \"function\" && parameters.get(\"pitchFactor\") !== undefined;\n }) as AudioWorkletNode | undefined;\n\n // factor === 1 is the documented \"no shift\" contract and MUST be a genuine\n // passthrough. The peak/region phase-vocoder pipeline does NOT guarantee\n // identity for peakless / edge-bin / broadband content (it zero-fills and\n // only repopulates detected peak regions), so we cannot leave the node in\n // the chain at factor 1. Remove its ordinary chain entry so the signal\n // bypasses the worklet entirely (source → [filters] → panner). A later\n // non-unity factor rebuilds a fresh node.\n if (factor === 1) {\n if (pitchShiftNode) {\n effectChain.remove(pitchShiftNode);\n }\n return;\n }\n\n if (!pitchShiftNode) {\n const cacophony = this.origin.cacophony;\n if (!cacophony) {\n throw new Error(\"Cannot pitch-shift a playback whose Sound has no Cacophony instance\");\n }\n pitchShiftNode = await cacophony.buildWorkletEffect(WORKLETS.phaseVocoder, {}, this.context);\n effectChain.add(pitchShiftNode);\n }\n\n const pitchParam = pitchShiftNode.parameters?.get(\"pitchFactor\");\n if (pitchParam) {\n pitchParam.value = factor;\n }\n }\n\n /**\n * The current pitch-shift factor (1 = no shift). See {@link setPitchShift}.\n */\n get pitchShift(): number {\n return this._pitchFactor;\n }\n\n /**\n * Gets the output node of this playback's audio graph.\n * This is the final node in the internal chain before connection to destination.\n * Use this to manually wire the playback into custom audio graphs.\n *\n * @returns {GainNode} The gain node that serves as the output of this playback.\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Manual routing through custom effects\n * const playback = sound.play()[0];\n * playback.disconnect(); // Disconnect from default destination\n * playback.connect(reverbNode).connect(context.destination);\n */\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a playback that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n /**\n * Connects this playback's output to an AudioNode or AudioParam.\n * Follows the Web Audio API connection pattern.\n *\n * @returns {AudioNode} The destination node (for chaining).\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Chain multiple effects\n * playback.connect(delay).connect(reverb).connect(context.destination);\n */\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n /**\n * Disconnects this playback's output from a specific destination or from all destinations.\n *\n * @param {AudioNode | AudioParam} [destination] - Optional specific destination to disconnect from.\n * If omitted, disconnects from all destinations.\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Disconnect from all\n * playback.disconnect();\n *\n * @example\n * // Disconnect from specific node\n * playback.disconnect(reverbNode);\n */\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n /**\n * Creates a clone of the current Playback instance with optional overrides for certain properties.\n * This method allows for the creation of a new Playback instance that shares the same audio context\n * and source node but can have different settings such as loop count or pan type.\n * The clone is connected to the origin Sound's primary route and registered with the Sound.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n clone(overrides: Partial<PlaybackCloneOverrides> = {}): Playback {\n if (!this.source || !this.gainNode || !this.context) {\n throw new Error(\"Cannot clone a sound that has been cleaned up\");\n }\n const panType = overrides.panType ?? this.panType;\n // we'll need to create a new gain node\n const gainNode = this.context.createGain();\n gainNode.connect(this.origin._resolveRouteTargetNode());\n // clone the source node\n let source: SourceNode;\n if (\"buffer\" in this.source && this.source.buffer) {\n source = this.context.createBufferSource();\n source.buffer = this.source.buffer;\n } else if (\"mediaElement\" in this.source && this.source.mediaElement) {\n if (!this.context.createMediaElementSource) {\n throw new Error(\"Media element sources are not supported on this audio context.\");\n }\n // The Web Audio spec forbids creating a second MediaElementAudioSourceNode\n // from the same HTMLMediaElement -- the second call throws\n // InvalidStateError. Clone the underlying element first so the new\n // playback owns an independent element that hasn't been bound yet.\n const originalElement = this.source.mediaElement;\n const clonedElement = originalElement.cloneNode(true) as HTMLMediaElement;\n source = this.context.createMediaElementSource(clonedElement);\n } else {\n throw new Error(\"Unsupported source type\");\n }\n const loopCount = overrides.loopCount !== undefined ? overrides.loopCount : this.loopCount;\n const clone = new Playback(this.origin, source, gainNode);\n\n // Copy all relevant properties\n clone.loopCount = loopCount;\n clone.currentLoop = this.currentLoop;\n clone.setPanType(panType, this.context);\n clone.volume = this.volume;\n clone.playbackRate = this._playbackRate;\n clone._offset = this._offset;\n if (this._fadeInConfig) {\n clone._fadeInConfig = { ...this._fadeInConfig };\n }\n if (this._fadeOutConfig) {\n clone._fadeOutConfig = { ...this._fadeOutConfig };\n }\n if (this._state === \"paused\") {\n clone.markPaused();\n } else if (this._state === \"stopped\") {\n clone.markStopped();\n }\n\n // Deep clone filters\n this._filters.forEach((filter) => {\n const clonedFilter = this.context.createBiquadFilter();\n clonedFilter.type = filter.type;\n clonedFilter.frequency.value = filter.frequency.value;\n clonedFilter.Q.value = filter.Q.value;\n clonedFilter.gain.value = filter.gain.value;\n clone.addFilter(clonedFilter as unknown as BiquadFilterNode);\n });\n\n if (this._pitchFactor !== 1) {\n void clone.setPitchShift(this._pitchFactor).catch(() => {\n /* worklet unavailable on this context — clone proceeds unshifted */\n });\n }\n\n // If the original is playing, start the clone\n if (this._state === \"playing\") {\n clone.play();\n }\n\n this.origin.playbacks.push(clone);\n return clone;\n }\n}\n","/**\n * The Sound class represents an audio asset within a web application, providing a high-level interface\n * for loading, manipulating, and playing audio. It supports both buffer-based and media element-based audio,\n * allowing for efficient playback and manipulation of sound resources.\n *\n * A Sound instance can manage multiple Playback instances, which represent individual playbacks of the sound.\n * This allows for the same sound to be played multiple times simultaneously or with different settings (e.g., volume,\n * playback rate, spatial positioning). The Sound class provides methods to control these playbacks collectively or individually.\n *\n * Key features include:\n * - Loading audio from a URL or using a pre-loaded buffer.\n * - Playing, pausing, resuming, and stopping audio playback.\n * - Looping audio a specific number of times or infinitely.\n * - Adjusting volume, playback rate, and spatial positioning (for 3D audio).\n * - Applying audio filters for effects like reverb, equalization, etc.\n * - Cloning the Sound instance for independent manipulation and playback.\n *\n * The relationship between Sound and Playback is central to the design of the audio system. A Sound object acts as a container\n * and manager for one or more Playback objects. Each Playback object represents a single instance of the sound being played,\n * and can be controlled individually. This architecture allows for complex audio behaviors, such as playing multiple overlapping\n * instances of a sound with different settings, without requiring the user to manually manage each playback instance.\n */\n\nimport type {\n BaseSound,\n Cacophony,\n LoopCount,\n PanType,\n PlayOptions,\n SoundCleanupHoldings,\n SoundType,\n StreamCapabilities,\n} from \"./cacophony\";\nimport type { AudioBuffer, BaseContext, BiquadFilterNode, GainNode, SourceNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { SoundEvents } from \"./events\";\nimport type { PanCloneOverrides } from \"./pannerMixin\";\nimport { Playback } from \"./playback\";\nimport type { TimeStretchOptions } from \"./processors/timestretch-core\";\nimport { RoutableSource } from \"./routableSource\";\nimport type { VolumeCloneOverrides } from \"./volumeMixin\";\n\ntype SoundCloneOverrides = PanCloneOverrides &\n VolumeCloneOverrides & {\n loopCount?: LoopCount;\n playbackRate?: number;\n filters?: BiquadFilterNode[];\n };\n\nexport class Sound extends RoutableSource implements BaseSound {\n public declare playbacks: Playback[];\n buffer?: AudioBuffer;\n streamCapabilities?: StreamCapabilities;\n context: BaseContext;\n loopCount: LoopCount = 0;\n private _playbackRate: number = 1;\n /**\n * Pitch-shift factor applied to every playback of this Sound (1 = no shift).\n * Fanned out across live playbacks by {@link setPitchShift} and applied to\n * future playbacks at {@link preplay}. Drives the phase-vocoder worklet\n * (Laroche & Dolson 1999 peak-based pitch shift).\n */\n private _pitchFactor: number = 1;\n private eventEmitter: TypedEventEmitter<SoundEvents> = new TypedEventEmitter<SoundEvents>();\n private _holdings: SoundCleanupHoldings = { sources: [], gainNodes: [], mediaElements: [] };\n private _unregisterToken: object = {};\n private _preparedMediaElement?: HTMLAudioElement;\n /**\n * Per-playback unsubscribe functions for the listeners that Sound attaches\n * to each playback in {@link preplay}. Tracked so the Sound→playback\n * closure cycle (each listener captures `this`) can be broken explicitly\n * when the playback ends, stops, or is cleaned up — not only when the\n * playback's own eventEmitter is torn down. A WeakMap keeps the entry GC-\n * tied to the playback identity.\n */\n private _playbackUnsubscribes: WeakMap<Playback, Array<() => void>> = new WeakMap();\n constructor(\n public url: string,\n buffer: AudioBuffer | undefined,\n context: BaseContext,\n protected globalGainNode: GainNode,\n public soundType: SoundType = \"buffer\",\n public panType: PanType = \"HRTF\",\n private _cacophony?: Cacophony,\n preparedMediaElement?: HTMLAudioElement,\n private _preparedMediaElementCleanup?: () => void,\n ) {\n super();\n this.buffer = buffer;\n this.context = context;\n this._preparedMediaElement = preparedMediaElement;\n this._cacophony?.registerSoundForCleanup(this, this._holdings, this._unregisterToken);\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n get volume(): number {\n return super.volume;\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof SoundEvents>(event: K, listener: (data: SoundEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof SoundEvents>(event: K, listener: (data: SoundEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n protected emit<K extends keyof SoundEvents>(event: K, data: SoundEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n protected async emitAsync<K extends keyof SoundEvents>(event: K, data: SoundEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Routes a media-loader failure through the Sound's existing typed event\n * system. Used by optional media transports after the Sound has loaded.\n */\n reportLoadError(error: Error, recoverable: boolean): void {\n void this.emitAsync(\"soundError\", {\n url: this.url,\n error,\n errorType: \"load\",\n timestamp: Date.now(),\n recoverable,\n });\n }\n\n /**\n * Clones the current Sound instance, creating a deep copy with the option to override specific properties.\n * This method allows for the creation of a new, independent Sound instance based on the current one, with the\n * flexibility to modify certain attributes through the `overrides` parameter. This is particularly useful for\n * creating variations of a sound without affecting the original instance. The cloned instance includes all properties,\n * playback settings, and filters of the original, unless explicitly overridden.\n *\n * @param {SoundCloneOverrides} overrides - An object specifying properties to override in the cloned instance.\n * This can include audio settings like volume, playback rate, and spatial positioning, as well as\n * more complex configurations like 3D audio options and filter adjustments.\n */\n\n clone(overrides: Partial<SoundCloneOverrides> = {}): Sound {\n const panType = overrides.panType ?? this.panType;\n const stereoPan = overrides.stereoPan !== undefined ? overrides.stereoPan : this.stereoPan;\n const loopCount = overrides.loopCount !== undefined ? overrides.loopCount : this.loopCount;\n const playbackRate = overrides.playbackRate ?? this.playbackRate;\n const volume = overrides.volume !== undefined ? overrides.volume : this.volume;\n const position = overrides.position !== undefined ? overrides.position : this.position;\n const filters = overrides.filters?.length ? overrides.filters : this._filters;\n\n const clone = new Sound(\n this.url,\n this.buffer,\n this.context,\n this.globalGainNode,\n this.soundType,\n panType,\n this.cacophony,\n );\n clone.loop(loopCount);\n clone.playbackRate = playbackRate;\n clone.volume = volume;\n if (panType === \"HRTF\") {\n // Apply HRTF override or inherit from source.\n if (overrides.threeDOptions !== undefined) {\n clone.threeDOptions = overrides.threeDOptions;\n } else if (this.panType === \"HRTF\") {\n clone.threeDOptions = this.threeDOptions;\n }\n clone.position = position;\n } else {\n clone.stereoPan = stereoPan;\n }\n clone.addFilters(filters);\n return clone;\n }\n\n /**\n * Generates a Playback instance for the sound without starting playback.\n * This allows for pre-configuration of playback properties such as volume and position before the sound is actually played.\n */\n\n preplay(): Playback[] {\n // Capture array lengths at entry so a throw mid-construction can truncate\n // back to exactly what was here before — this preplay call's pushes get\n // rolled back without touching prior entries.\n const sourcesLen = this._holdings.sources.length;\n const gainNodesLen = this._holdings.gainNodes.length;\n const mediaElementsLen = this._holdings.mediaElements.length;\n const playbacksLen = this.playbacks.length;\n // Track audio nodes created during this call so we can disconnect them on\n // rollback (the finalization registry won't see them since holdings are\n // truncated below).\n let createdSource: SourceNode | undefined;\n let createdGainNode: GainNode | undefined;\n try {\n let source: SourceNode;\n if (this.buffer) {\n source = this.context.createBufferSource();\n source.buffer = this.buffer;\n } else {\n if (!this.context.createMediaElementSource) {\n throw new Error(\n \"Media element sources are not supported on this audio context (e.g. OfflineAudioContext). Use buffer-based sounds instead.\",\n );\n }\n const audio = this._preparedMediaElement ?? new Audio();\n audio.crossOrigin = \"anonymous\";\n audio.src = this.url;\n audio.preload = \"auto\";\n // we have the audio, let's make a buffer source node out of it\n source = this.context.createMediaElementSource(audio);\n if (audio === this._preparedMediaElement) {\n this._preparedMediaElement = undefined;\n }\n }\n createdSource = source;\n const gainNode = this.context.createGain();\n createdGainNode = gainNode;\n // Resolve the primary route target. A null `_routeTarget` means master\n // — which is structurally `globalGainNode`. A destroyed bus falls back\n // to master with a warning.\n const primaryTargetNode = this._resolveRouteTargetNode();\n gainNode.connect(primaryTargetNode);\n const playback = new Playback(this, source, gainNode);\n this._preparePlayback(playback);\n this._holdings.sources.push(source);\n this._holdings.gainNodes.push(gainNode);\n if (\"mediaElement\" in source && source.mediaElement) {\n this._holdings.mediaElements.push(source.mediaElement);\n }\n playback.setGainNode(gainNode);\n playback.volume = this.volume;\n playback.playbackRate = this.playbackRate;\n playback.loop(this.loopCount);\n // Carry the Sound's pitch-shift factor onto the new playback. Building the\n // phase-vocoder worklet node is async; preplay is sync, so this is a\n // fire-and-forget that splices the node in once the worklet resolves\n // (no-op when no shift is set).\n if (this._pitchFactor !== 1) {\n void playback.setPitchShift(this._pitchFactor).catch(() => {\n /* worklet unavailable on this context — playback proceeds unshifted */\n });\n }\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else if (this.panType === \"stereo\") {\n playback.stereoPan = this.stereoPan;\n }\n // Set up event propagation from playback to sound. Each listener captures\n // `this` and is registered on the playback's own emitter — so the\n // playback holds a reference back to the Sound for the lifetime of the\n // listener. Capture the unsubscribe functions so we can break the cycle\n // explicitly when the playback ends (naturally) or is cleaned up.\n const unsubEnded = playback.on(\"ended\", () => {\n this.emit(\"ended\", undefined);\n // Natural end: the playback has fired its terminal event; tear down\n // our subscriptions so it can be GC'd without waiting for cleanup().\n this._unsubscribeFromPlayback(playback);\n });\n playback._loopEndCallback = () => {\n this.emit(\"loopEnd\", undefined);\n };\n const unsubError = playback.on(\"error\", (errorEvent) => {\n this.emitAsync(\"soundError\", {\n url: this.url,\n error: errorEvent.error,\n errorType: \"playback\",\n timestamp: errorEvent.timestamp,\n recoverable: errorEvent.recoverable,\n });\n });\n // Clear-the-callback step counts as an \"unsubscribe\" for the closure\n // _loopEndCallback holds (it captures `this`). Combine all three.\n const clearLoopCallback = () => {\n if (playback._loopEndCallback) {\n playback._loopEndCallback = undefined;\n }\n };\n this._playbackUnsubscribes.set(playback, [unsubEnded, unsubError, clearLoopCallback]);\n\n this.playbacks.push(playback);\n return [playback];\n } catch (error) {\n // Roll back any state pushed during THIS call so a failed preplay does\n // not leave zombie holdings/playbacks behind. Truncate to entry lengths\n // first (safe even if we never reached the pushes), then disconnect the\n // audio nodes we actually created.\n this._holdings.sources.length = sourcesLen;\n this._holdings.gainNodes.length = gainNodesLen;\n this._holdings.mediaElements.length = mediaElementsLen;\n this.playbacks.length = playbacksLen;\n if (createdGainNode) {\n try {\n createdGainNode.disconnect();\n } catch {\n // Disconnect can throw if the node was never connected; the rollback\n // intent is satisfied regardless.\n }\n }\n if (createdSource) {\n try {\n createdSource.disconnect();\n } catch {\n // Same — best-effort cleanup on a failed-construction path.\n }\n }\n const errorEvent = {\n url: this.url,\n error: error as Error,\n errorType: \"playback\" as const,\n timestamp: Date.now(),\n recoverable: true,\n };\n this.emitAsync(\"soundError\", errorEvent);\n throw error;\n }\n }\n\n /**\n * Detaches the Sound-side listeners that were registered on a playback in\n * {@link preplay}. Idempotent — safe to call on a playback whose\n * subscriptions were already torn down. Breaks the Sound↔playback closure\n * cycle described on the entries in {@link _playbackUnsubscribes}.\n */\n private _unsubscribeFromPlayback(playback: Playback): void {\n const unsubs = this._playbackUnsubscribes.get(playback);\n if (!unsubs) {\n return;\n }\n this._playbackUnsubscribes.delete(playback);\n for (const unsub of unsubs) {\n unsub();\n }\n }\n\n play(options?: PlayOptions): ReturnType<this[\"preplay\"]> {\n const playbacks = this.preplay() as ReturnType<this[\"preplay\"]>;\n\n for (const playback of playbacks) {\n const emitSoundPlay = (acceptedPlayback: SoundEvents[\"play\"]) => {\n cleanupPlayListeners();\n this.emit(\"play\", acceptedPlayback);\n };\n const cleanupPlayListeners = () => {\n playback.off(\"play\", emitSoundPlay);\n playback.off(\"error\", cleanupPlayListeners);\n };\n\n playback.on(\"play\", emitSoundPlay);\n playback.on(\"error\", cleanupPlayListeners);\n\n try {\n playback.play();\n } catch (error) {\n cleanupPlayListeners();\n // preplay() already committed `playback` to `this.playbacks` and the\n // Sound→playback listener subscriptions. Remove both so the failed\n // playback is not re-touched by subsequent resume()/loop() iterations\n // and so the closure cycle is broken on the throw path too.\n const idx = this.playbacks.indexOf(playback);\n if (idx !== -1) {\n this.playbacks.splice(idx, 1);\n }\n this._unsubscribeFromPlayback(playback);\n throw error;\n }\n }\n\n if (options) {\n for (const playback of playbacks) {\n if (options.fadeIn !== undefined) {\n playback.fadeIn(options.fadeIn, options.fadeType, { perLoop: options.fadeInPerLoop });\n }\n if (options.fadeOut !== undefined) {\n playback.configureFadeOut(options.fadeOut, options.fadeType);\n }\n }\n }\n\n return playbacks;\n }\n\n stop(): void {\n this._holdings.sources.length = 0;\n this._holdings.gainNodes.length = 0;\n this._holdings.mediaElements.length = 0;\n super.stop();\n this.emit(\"stop\", undefined);\n }\n\n pause(): void {\n super.pause();\n this.emit(\"pause\", undefined);\n }\n\n resume(): void {\n this.playbacks.filter((playback) => playback.isPaused).forEach((playback) => playback.play());\n this.emit(\"resume\", undefined);\n }\n\n /**\n * Seeks to a specific time within the sound's playback.\n * @param { number } time - The time in seconds to seek to.\n * This method iterates through all active `Playback` instances and calls their `seek()` method with the specified time.\n */\n\n seek(time: number): void {\n this.playbacks.forEach((playback) => playback.seek(time));\n }\n\n /**\n * Retrieves the duration of the sound in seconds.\n * If the sound is based on an AudioBuffer, it returns the duration of the buffer.\n * Otherwise, if the sound has not been played and is a MediaElementSource, it returns NaN, indicating that the duration is unknown or not applicable.\n * @returns { number } The duration of the sound in seconds.\n */\n\n get duration() {\n if (this.playbacks.length > 0) {\n return this.playbacks[0].duration;\n }\n return this.buffer?.duration || NaN;\n }\n\n /**\n * OFFLINE independent time-stretch: produce a NEW buffer-based Sound whose\n * tempo is changed by `factor` WITHOUT changing pitch (Phase Gradient Heap\n * Integration, Průša & Holighaus, \"Phase Vocoder Done Right\", EUSIPCO 2017 /\n * arXiv:2202.07382). `factor > 1` is slower/longer, `factor < 1` is\n * faster/shorter. Only valid for buffer-based sounds (a loaded AudioBuffer);\n * streaming / media-element sounds have no decoded buffer to transform.\n *\n * The original Sound is left untouched; a fresh Sound wrapping the stretched\n * buffer is returned. Delegates to {@link Cacophony.timeStretchBuffer}.\n *\n * @param factor Stretch factor (`> 0`); pitch is preserved.\n * @param options Optional PGHI parameters (fftSize, analysisHop, tol, seed).\n * @returns A new buffer-based Sound at the stretched tempo.\n * @throws If this Sound has no AudioBuffer (e.g. streaming/HTML5 audio).\n */\n timeStretch(factor: number, options?: TimeStretchOptions): Sound {\n if (!this.buffer) {\n throw new Error(\"Sound.timeStretch requires a buffer-based sound (a loaded AudioBuffer).\");\n }\n if (!this._cacophony) {\n throw new Error(\"Sound.timeStretch requires the owning Cacophony instance.\");\n }\n const stretched = this._cacophony.timeStretchBuffer(this.buffer, factor, options);\n return new Sound(this.url, stretched, this.context, this.globalGainNode, \"buffer\", this.panType, this._cacophony);\n }\n\n /**\n * Sets or retrieves the loop behavior for the sound.\n * If loopCount is provided, the sound will loop the specified number of times.\n * If loopCount is 'infinite', the sound will loop indefinitely until stopped.\n * If no argument is provided, the method returns the current loop count setting.\n * @param { LoopCount } [loopCount] - The number of times to loop or 'infinite' for indefinite looping.\n * @returns { LoopCount } The current loop count setting if no argument is provided.\n */\n\n loop(loopCount?: LoopCount): LoopCount {\n if (loopCount === undefined) {\n return this.loopCount;\n }\n this.loopCount = loopCount;\n this.playbacks.forEach((p) => p.loop(loopCount));\n return this.loopCount;\n }\n\n get playbackRate(): number {\n return this._playbackRate;\n }\n\n set playbackRate(rate: number) {\n this._playbackRate = rate;\n this.playbacks.forEach((p) => (p.playbackRate = rate));\n this.emit(\"rateChange\", rate);\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n /**\n * Current pitch-shift factor (1 = no shift). See {@link setPitchShift}.\n */\n get pitchShift(): number {\n return this._pitchFactor;\n }\n\n /**\n * Pitch-shifts every live playback of this Sound, resurrecting the dormant\n * phase-vocoder worklet (Jean Laroche & Mark Dolson, 1999 IEEE WASPAA —\n * peak-based pitch shift with Identity Phase-Locking). Mirrors how\n * {@link playbackRate} fans a per-playback control out across `this.playbacks`.\n * The factor is also stored so future playbacks pick it up at preplay.\n *\n * @param factor Pitch multiplier (> 0; 2 = +1 octave, 0.5 = -1 octave).\n * @returns Resolves once every live playback's worklet node has been built\n * and its `pitchFactor` param updated.\n */\n async setPitchShift(factor: number): Promise<void> {\n // Validate UP FRONT, before storing. With no live playbacks `Promise.all([])`\n // resolves immediately, so a missing guard here would let setPitchShift(0)\n // / NaN / negative \"succeed\" and leave an invalid pitchShift on the Sound\n // that future preplay() would silently swallow. Mirrors the per-playback\n // guard in Playback.setPitchShift.\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(\"Pitch-shift factor must be greater than 0\");\n }\n this._pitchFactor = factor;\n await Promise.all(this.playbacks.map((p) => p.setPitchShift(factor)));\n }\n\n cleanup(): void {\n this._cacophony?.unregisterSoundCleanup(this._unregisterToken);\n this._preparedMediaElementCleanup?.();\n this._preparedMediaElementCleanup = undefined;\n if (this._preparedMediaElement) {\n this._preparedMediaElement.pause();\n this._preparedMediaElement.src = \"\";\n this._preparedMediaElement.load();\n this._preparedMediaElement = undefined;\n }\n this._holdings.sources.length = 0;\n this._holdings.gainNodes.length = 0;\n this._holdings.mediaElements.length = 0;\n // Explicitly tear down the Sound→playback listener subscriptions before\n // calling p.cleanup() — p.cleanup()'s removeAllListeners() also clears\n // them, but doing it here keeps the closure cycle's break point local to\n // Sound and ensures the WeakMap entries are released promptly.\n this.playbacks.forEach((p) => {\n this._unsubscribeFromPlayback(p);\n p.cleanup();\n });\n this.playbacks = [];\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { OscillatorNode } from \"./context\";\n\nexport type OscillatorCloneOverrides = {\n oscillatorOptions?: Partial<OscillatorOptions>;\n};\n\nexport abstract class OscillatorMixin extends BasePlayback {\n _oscillatorOptions: Partial<OscillatorOptions> = {};\n public declare source?: OscillatorNode;\n\n get oscillatorOptions(): Partial<OscillatorOptions> {\n return this._oscillatorOptions;\n }\n\n set oscillatorOptions(options: Partial<OscillatorOptions>) {\n this._oscillatorOptions = options;\n if (this.source) {\n if (this.oscillatorOptions.detune !== undefined) this.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined)\n this.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) this.source.type = this.oscillatorOptions.type;\n }\n }\n\n get frequency(): number {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.frequency.value;\n }\n\n set frequency(frequency: number) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.frequency.value = frequency;\n this.oscillatorOptions.frequency = frequency;\n }\n\n get detune(): number {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.detune.value;\n }\n\n set detune(detune: number) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.detune.value = detune;\n this.oscillatorOptions.detune = detune;\n }\n\n get type(): OscillatorType {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.type;\n }\n\n set type(type: OscillatorType) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.type = type;\n this.oscillatorOptions.type = type;\n }\n}\n","import type { BaseSound } from \"./cacophony\";\nimport type { AudioNode, AudioParam, BaseContext, GainNode, OscillatorNode } from \"./context\";\nimport { OscillatorMixin } from \"./oscillatorMixin\";\nimport type { Synth } from \"./synth\";\n\nexport class SynthPlayback extends OscillatorMixin implements BaseSound {\n context: BaseContext;\n /**\n * Live oscillator node. Set in the constructor; cleared to `undefined` in\n * {@link cleanup} (matches Playback's `source?` shape so post-cleanup\n * checks of `!this.source` work uniformly).\n */\n public declare source?: OscillatorNode;\n constructor(\n public origin: Synth,\n source: OscillatorNode,\n gainNode: GainNode,\n ) {\n super(origin);\n this.context = origin.context;\n this.source = source;\n this.setPanType(origin.panType, origin.context);\n // setPanType synchronously assigns this.panner; capture locally so TS\n // narrows to non-undefined for the connect calls below.\n const panner = this.panner;\n if (!panner) {\n throw new Error(\"setPanType did not produce a panner node\");\n }\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(source, panner);\n panner.connect(gainNode);\n this.oscillatorOptions = {\n detune: source.detune.value,\n frequency: source.frequency.value,\n type: source.type,\n };\n }\n\n play(): [this] {\n if (!this.source || !this.panner) {\n throw new Error(\"Cannot play a synth that has been cleaned up\");\n }\n\n if (this._state === \"playing\") {\n return [this];\n }\n\n const isResume = this._state === \"paused\";\n if (isResume || this._state === \"stopped\") {\n this.recreateSource();\n }\n\n if (this.oscillatorOptions.detune !== undefined) this.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) this.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) this.source.type = this.oscillatorOptions.type;\n\n this.source.start();\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n this.source.stop();\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source || this._state === \"unplayed\" || this._state === \"stopped\") {\n return;\n }\n\n if (this._state === \"playing\") {\n this.source.stop();\n }\n\n this.emitStopped();\n }\n\n cleanup(): void {\n if (!this.source) {\n // Already cleaned up — same idempotency guard as Playback.cleanup.\n return;\n }\n if (this.panner && this.gainNode) {\n try {\n this.panner.disconnect();\n } catch {}\n }\n this.source = undefined;\n this.panner = undefined;\n this.markStopped();\n this.eventEmitter.removeAllListeners();\n // super.cleanup() (VolumeMixin) disconnects + clears `gainNode`; after\n // this call `outputNode` throws (parity with Playback.cleanup).\n super.cleanup();\n }\n\n /**\n * Gets the output node of this synth playback's audio graph — the final\n * GainNode before connection to the destination. Use this to manually\n * wire the playback into custom audio graphs.\n *\n * @throws if the playback has been cleaned up.\n */\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a synth playback that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n /**\n * Connects this synth playback's output to an AudioNode or AudioParam. Mirrors\n * Playback.connect.\n *\n * @returns The destination node (for chaining).\n * @throws if the playback has been cleaned up.\n */\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n /**\n * Disconnects this synth playback's output from a specific destination\n * or from all destinations. Mirrors Playback.disconnect.\n *\n * @throws if the playback has been cleaned up.\n */\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n private recreateSource(): void {\n if (!this.panner || !this.source) {\n throw new Error(\"Cannot recreate source of a synth that has been cleaned up\");\n }\n\n this.source.disconnect();\n this.source = this.context.createOscillator();\n this.setEffectChainEndpoints(this.source, this.panner);\n }\n}\n","import type { BaseSound, Cacophony, PanType, Position, SoundType } from \"./cacophony\";\nimport type { BaseContext, GainNode, OscillatorNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { SynthEvents } from \"./events\";\nimport type { FilterCloneOverrides } from \"./filters\";\nimport type { OscillatorCloneOverrides } from \"./oscillatorMixin\";\nimport type { PanCloneOverrides } from \"./pannerMixin\";\nimport { RoutableSource } from \"./routableSource\";\nimport { SynthPlayback } from \"./synthPlayback\";\nimport type { VolumeCloneOverrides } from \"./volumeMixin\";\n\ntype SynthCloneOverrides = FilterCloneOverrides & OscillatorCloneOverrides & PanCloneOverrides & VolumeCloneOverrides;\n\nexport class Synth extends RoutableSource implements BaseSound {\n _oscillatorOptions: Partial<OscillatorOptions>;\n playbacks: SynthPlayback[] = [];\n private eventEmitter: TypedEventEmitter<SynthEvents> = new TypedEventEmitter<SynthEvents>();\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof SynthEvents>(event: K, listener: (data: SynthEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof SynthEvents>(event: K, listener: (data: SynthEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n protected emit<K extends keyof SynthEvents>(event: K, data: SynthEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n protected async emitAsync<K extends keyof SynthEvents>(event: K, data: SynthEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n constructor(\n public context: BaseContext,\n protected globalGainNode: GainNode,\n public soundType: SoundType = \"oscillator\",\n public panType: PanType = \"HRTF\",\n oscillatorOptions: Partial<OscillatorOptions> = {},\n private _cacophony?: Cacophony,\n ) {\n super();\n this.context = context;\n this._oscillatorOptions = oscillatorOptions;\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n /**\n * Clones the current Synth instance, creating a deep copy with the option to override specific properties.\n * This method allows for the creation of a new, independent Synth instance based on the current one, with the\n * flexibility to modify certain attributes through the `overrides` parameter. This is particularly useful for\n * creating variations of a synth without affecting the original instance. The cloned instance includes all properties,\n * playback settings, and filters of the original, unless explicitly overridden.\n *\n * @param {SynthCloneOverrides} overrides - An object specifying properties to override in the cloned instance.\n * This can include audio settings like volume, playback rate, and spatial positioning, as well as\n * more complex configurations like 3D audio options and filter adjustments.\n */\n clone(overrides: Partial<SynthCloneOverrides> = {}): Synth {\n const panType = overrides.panType ?? this.panType;\n const stereoPan = overrides.stereoPan !== undefined ? overrides.stereoPan : this.stereoPan;\n const volume = overrides.volume !== undefined ? overrides.volume : this.volume;\n const position = overrides.position !== undefined ? overrides.position : this.position;\n const filters = overrides.filters !== undefined ? overrides.filters : this._filters;\n const oscillatorOptions = { ...(overrides.oscillatorOptions ?? this._oscillatorOptions) };\n\n const clone = new Synth(\n this.context,\n this.globalGainNode,\n this.soundType,\n panType,\n oscillatorOptions,\n this._cacophony,\n );\n clone._volume = volume;\n clone._position = [...position] as Position;\n clone._stereoPan = stereoPan;\n // Apply HRTF override (if provided) through the canonical setter so the\n // discriminated `_threeDOptions` invariant is preserved. Without an override\n // the new clone keeps its default HRTF storage initialized in the container.\n if (overrides.threeDOptions !== undefined) {\n clone.threeDOptions = overrides.threeDOptions;\n } else if (this.panType === \"HRTF\") {\n clone.threeDOptions = this.threeDOptions;\n }\n if (panType === \"HRTF\") {\n clone.position = [...position] as Position;\n }\n clone.addFilters(filters);\n return clone;\n }\n\n /**\n * Generates a Playback instance for the synth without starting playback.\n * This allows for pre-configuration of playback properties such as volume and position before the synth is actually played.\n */\n preplay(): SynthPlayback[] {\n const oscillator = this.context.createOscillator();\n const playbacks = this.createPlayback(oscillator);\n return playbacks;\n }\n\n private createPlayback(oscillator: OscillatorNode): SynthPlayback[] {\n if (this.oscillatorOptions.detune !== undefined) oscillator.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) oscillator.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) oscillator.type = this.oscillatorOptions.type;\n\n const gainNode = this.context.createGain();\n const primaryTargetNode = this._resolveRouteTargetNode();\n gainNode.connect(primaryTargetNode);\n const playback = new SynthPlayback(this, oscillator, gainNode);\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else if (this.panType === \"stereo\") {\n playback.stereoPan = this.stereoPan;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n play(): ReturnType<this[\"preplay\"]> {\n const playbacks = super.play() as ReturnType<this[\"preplay\"]>;\n this.emit(\"play\", playbacks[0]);\n return playbacks;\n }\n\n stop(): void {\n super.stop();\n this.emit(\"stop\", undefined);\n }\n\n pause(): void {\n super.pause();\n this.emit(\"pause\", undefined);\n }\n\n resume(): void {\n this.playbacks.filter((playback) => playback.isPaused).forEach((playback) => playback.play());\n this.emit(\"resume\", undefined);\n }\n\n get volume(): number {\n return super.volume;\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n get oscillatorOptions(): Partial<OscillatorOptions> {\n return this._oscillatorOptions;\n }\n\n set oscillatorOptions(options: Partial<OscillatorOptions>) {\n this._oscillatorOptions = options;\n this.playbacks.forEach((p) => {\n if (p.source) {\n if (this.oscillatorOptions.detune !== undefined) p.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) p.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) p.source.type = this.oscillatorOptions.type;\n }\n });\n }\n\n get frequency(): number {\n return (this.oscillatorOptions.frequency as number) ?? 440;\n }\n\n set frequency(frequency: number) {\n this._oscillatorOptions.frequency = frequency;\n this.playbacks.forEach((p) => (p.frequency = frequency));\n this.emit(\"frequencyChange\", frequency);\n }\n\n get detune(): number {\n return this.oscillatorOptions.detune ?? 0;\n }\n\n set detune(detune: number) {\n this._oscillatorOptions.detune = detune;\n this.playbacks.forEach((p) => (p.detune = detune));\n this.emit(\"detuneChange\", detune);\n }\n\n get type(): OscillatorType {\n return (this.oscillatorOptions.type as OscillatorType) ?? \"sine\";\n }\n\n set type(type: OscillatorType) {\n this._oscillatorOptions.type = type;\n this.playbacks.forEach((p) => (p.type = type));\n this.emit(\"typeChange\", type);\n }\n}\n","import { ADTS, AudioSampleSink, FLAC, Input, type InputAudioTrack, MP3, MP4, OGG, UrlSource, WAVE } from \"mediabunny\";\nimport type { StreamCapabilities } from \"./cacophony\";\nimport type { PcmStreamPullSource, PcmStreamSound } from \"./pcmStream\";\n\ninterface DecoderSession {\n channelCount: number;\n duration: number;\n input: Input;\n live: boolean;\n sampleRate: number;\n seekable: boolean;\n sink: AudioSampleSink;\n}\n\ninterface OpenSessionResult {\n session: DecoderSession | null;\n unsupported: boolean;\n}\n\nexport type WebCodecsStreamSound = PcmStreamSound & {\n readonly soundType: \"streaming\";\n readonly streamCapabilities: StreamCapabilities;\n};\n\n/**\n * Pulls encoded URL media through Mediabunny's demuxers and WebCodecs-backed\n * audio sink, then writes interleaved float PCM into the existing ring buffer.\n */\nexport class WebCodecsPullAdapter implements PcmStreamPullSource {\n private currentRun?: AbortController;\n private disposed = false;\n private pendingInput?: Input;\n private session?: DecoderSession;\n private sound?: PcmStreamSound;\n\n private readonly handleAbort = (): void => {\n this.cleanup();\n };\n\n private constructor(\n private readonly url: string,\n private readonly targetSampleRate: number,\n private readonly signal?: AbortSignal,\n ) {\n this.signal?.addEventListener(\"abort\", this.handleAbort, { once: true });\n }\n\n static async open(url: string, targetSampleRate: number, signal?: AbortSignal): Promise<WebCodecsPullAdapter | null> {\n signal?.throwIfAborted();\n const adapter = new WebCodecsPullAdapter(url, targetSampleRate, signal);\n try {\n const result = await adapter.openSession();\n if (result.unsupported || !result.session) {\n adapter.cleanup();\n return null;\n }\n adapter.session = result.session;\n return adapter;\n } catch (error) {\n adapter.cleanup();\n signal?.throwIfAborted();\n if ((error as { name?: string } | null)?.name === \"UnsupportedInputFormatError\") {\n return null;\n }\n throw error;\n }\n }\n\n get channelCount(): number {\n if (!this.session) {\n throw new Error(\"WebCodecs stream adapter is not initialized\");\n }\n return this.session.channelCount;\n }\n\n get capabilities(): StreamCapabilities {\n if (!this.session) {\n throw new Error(\"WebCodecs stream adapter is not initialized\");\n }\n return {\n duration: this.session.duration,\n live: this.session.live,\n seekable: this.session.seekable,\n transport: \"webcodecs\",\n };\n }\n\n attach(sound: PcmStreamSound): WebCodecsStreamSound {\n if (this.disposed || !this.session) {\n throw new Error(\"Cannot attach a disposed WebCodecs stream adapter\");\n }\n this.sound = sound;\n sound.attachPullSource(this, this.capabilities);\n this.startPump(this.session, 0);\n return sound as WebCodecsStreamSound;\n }\n\n seek(time: number): void {\n if (!Number.isFinite(time) || time < 0) {\n throw new RangeError(\"Stream seek time must be a finite non-negative number\");\n }\n if (this.disposed) {\n throw new Error(\"Cannot seek a WebCodecs stream that has been cleaned up\");\n }\n if (!this.sound) {\n throw new Error(\"Cannot seek a WebCodecs stream before it has been attached\");\n }\n if (!this.session) {\n throw new Error(\"Cannot seek while another WebCodecs seek is pending\");\n }\n if (this.session.live) {\n throw new Error(\"Live streams do not support seeking\");\n }\n if (!this.session.seekable) {\n throw new Error(\"This stream's server does not support range seeking\");\n }\n\n const wasPlaying = this.sound.isPlaying;\n this.sound.stop();\n if (wasPlaying) {\n this.sound.play();\n }\n this.currentRun?.abort();\n this.session.input.dispose();\n this.session = undefined;\n void this.reopenAndPump(time);\n }\n\n cleanup(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.signal?.removeEventListener(\"abort\", this.handleAbort);\n this.currentRun?.abort();\n this.pendingInput?.dispose();\n this.pendingInput = undefined;\n this.session?.input.dispose();\n this.session = undefined;\n }\n\n private async reopenAndPump(time: number): Promise<void> {\n try {\n const result = await this.openSession();\n if (this.disposed) {\n result.session?.input.dispose();\n return;\n }\n if (!result.session) {\n return;\n }\n const soundChannelCount = this.channelCountForSound();\n if (soundChannelCount !== undefined && result.session.channelCount !== soundChannelCount) {\n result.session.input.dispose();\n throw new Error(\"The audio channel count changed while seeking\");\n }\n this.session = result.session;\n this.startPump(result.session, time);\n } catch (error) {\n if (!this.disposed && !this.signal?.aborted) {\n this.sound?.handlePullError(error);\n }\n }\n }\n\n private channelCountForSound(): number | undefined {\n return this.sound?.inputChannelCount;\n }\n\n private startPump(session: DecoderSession, startTime: number): void {\n this.currentRun?.abort();\n const run = new AbortController();\n this.currentRun = run;\n void this.pump(session, startTime, run.signal).catch((error: unknown) => {\n if (!run.signal.aborted && !this.disposed && !this.signal?.aborted) {\n this.sound?.handlePullError(error);\n }\n });\n }\n\n private async pump(session: DecoderSession, startTime: number, runSignal: AbortSignal): Promise<void> {\n const sound = this.sound;\n if (!sound) {\n return;\n }\n const resampler = new StreamingPcmResampler(session.sampleRate, this.targetSampleRate, session.channelCount);\n\n for await (const sample of session.sink.samples(startTime)) {\n if (runSignal.aborted || this.disposed) {\n sample.close();\n return;\n }\n try {\n const options = { format: \"f32\" as const, planeIndex: 0 };\n const data = new ArrayBuffer(sample.allocationSize(options));\n sample.copyTo(data, options);\n const output = resampler.add(new Float32Array(data));\n await this.enqueue(sound, output, session.channelCount, runSignal);\n } finally {\n sample.close();\n }\n }\n\n const tail = resampler.finalize();\n await this.enqueue(sound, tail, session.channelCount, runSignal);\n if (!runSignal.aborted && !this.disposed) {\n sound.end();\n }\n }\n\n private async enqueue(\n sound: PcmStreamSound,\n samples: Float32Array,\n channelCount: number,\n signal: AbortSignal,\n ): Promise<void> {\n const maximumFrames = Math.max(1, Math.floor(this.targetSampleRate / 4));\n const maximumSamples = maximumFrames * channelCount;\n for (let offset = 0; offset < samples.length; offset += maximumSamples) {\n const chunk = samples.slice(offset, Math.min(samples.length, offset + maximumSamples));\n let written = false;\n while (!written) {\n signal.throwIfAborted();\n const before = sound.bufferedDuration;\n const hasCapacity = sound.write(chunk);\n written = sound.bufferedDuration > before;\n if (!hasCapacity) {\n await waitForDrain(sound, signal);\n }\n }\n }\n }\n\n private async openSession(): Promise<OpenSessionResult> {\n this.signal?.throwIfAborted();\n let sawRangeResponse = false;\n const source = new UrlSource(this.url, {\n fetchFn: async (input, init) => {\n const response = await globalThis.fetch(input, init);\n if (response.status === 206 || response.headers.get(\"Accept-Ranges\")?.toLowerCase() === \"bytes\") {\n sawRangeResponse = true;\n }\n return response;\n },\n });\n const input = new Input({ formats: [MP3, ADTS, MP4, OGG, FLAC, WAVE], source });\n this.pendingInput = input;\n try {\n const track = await input.getPrimaryAudioTrack();\n if (!track || !(await track.canDecode())) {\n this.pendingInput = undefined;\n input.dispose();\n return { session: null, unsupported: true };\n }\n const session = await this.describeSession(input, track, sawRangeResponse);\n this.signal?.throwIfAborted();\n if (this.disposed) {\n this.pendingInput = undefined;\n input.dispose();\n return { session: null, unsupported: false };\n }\n this.pendingInput = undefined;\n return { session, unsupported: false };\n } catch (error) {\n if (this.pendingInput === input) {\n this.pendingInput = undefined;\n input.dispose();\n }\n throw error;\n }\n }\n\n private async describeSession(\n input: Input,\n track: InputAudioTrack,\n sawRangeResponse: boolean,\n ): Promise<DecoderSession> {\n const [channelCount, sampleRate, live, metadataDuration] = await Promise.all([\n track.getNumberOfChannels(),\n track.getSampleRate(),\n track.isLive(),\n track.getDurationFromMetadata({ skipLiveWait: true }),\n ]);\n return {\n channelCount,\n duration: live || metadataDuration === null ? Number.POSITIVE_INFINITY : metadataDuration,\n input,\n live,\n sampleRate,\n seekable: !live && sawRangeResponse,\n sink: new AudioSampleSink(track),\n };\n }\n}\n\nfunction waitForDrain(sound: PcmStreamSound, signal: AbortSignal): Promise<void> {\n signal.throwIfAborted();\n return new Promise((resolve, reject) => {\n const unsubscribe = sound.on(\"drain\", () => {\n cleanup();\n resolve();\n });\n const handleAbort = () => {\n cleanup();\n reject(signal.reason);\n };\n const cleanup = () => {\n unsubscribe();\n signal.removeEventListener(\"abort\", handleAbort);\n };\n signal.addEventListener(\"abort\", handleAbort, { once: true });\n });\n}\n\n/**\n * Stateful linear-interpolation resampler for decoded stream chunks.\n *\n * This v1 implementation has no anti-aliasing filter. Downsampling can fold\n * source frequencies above the target Nyquist frequency into the audible band;\n * use a band-limited resampler if that quality trade-off is unacceptable.\n */\nclass StreamingPcmResampler {\n private inputFrames = 0;\n private nextSourcePosition = 0;\n private previousFrame?: Float32Array;\n\n constructor(\n private readonly sourceRate: number,\n private readonly targetRate: number,\n private readonly channelCount: number,\n ) {}\n\n add(input: Float32Array): Float32Array {\n if (this.sourceRate === this.targetRate) {\n this.inputFrames += input.length / this.channelCount;\n return input;\n }\n const frameCount = input.length / this.channelCount;\n const firstFrame = this.inputFrames;\n const lastFrame = firstFrame + frameCount - 1;\n const sourceFramesPerOutput = this.sourceRate / this.targetRate;\n const outputFrameCount =\n this.nextSourcePosition > lastFrame\n ? 0\n : Math.floor((lastFrame - this.nextSourcePosition) / sourceFramesPerOutput) + 1;\n const output = new Float32Array(outputFrameCount * this.channelCount);\n let outputOffset = 0;\n\n for (let outputFrame = 0; outputFrame < outputFrameCount; outputFrame += 1) {\n const leftFrame = Math.floor(this.nextSourcePosition);\n const fraction = this.nextSourcePosition - leftFrame;\n for (let channel = 0; channel < this.channelCount; channel += 1) {\n const left = this.sampleAt(input, firstFrame, leftFrame, channel);\n const right = this.sampleAt(input, firstFrame, Math.min(leftFrame + 1, lastFrame), channel);\n output[outputOffset] = left + (right - left) * fraction;\n outputOffset += 1;\n }\n this.nextSourcePosition += sourceFramesPerOutput;\n }\n\n this.inputFrames += frameCount;\n this.previousFrame = input.slice(input.length - this.channelCount);\n return output;\n }\n\n finalize(): Float32Array {\n if (this.sourceRate === this.targetRate || !this.previousFrame) {\n return new Float32Array();\n }\n const sourceFramesPerOutput = this.sourceRate / this.targetRate;\n const remainingSourceFrames = this.inputFrames - this.nextSourcePosition;\n const outputFrameCount = remainingSourceFrames > 0 ? Math.ceil(remainingSourceFrames / sourceFramesPerOutput) : 0;\n const output = new Float32Array(outputFrameCount * this.channelCount);\n for (let outputFrame = 0; outputFrame < outputFrameCount; outputFrame += 1) {\n output.set(this.previousFrame, outputFrame * this.channelCount);\n this.nextSourcePosition += sourceFramesPerOutput;\n }\n return output;\n }\n\n private sampleAt(input: Float32Array, firstFrame: number, frame: number, channel: number): number {\n if (frame < firstFrame) {\n return this.previousFrame?.[channel] ?? input[channel] ?? 0;\n }\n return input[(frame - firstFrame) * this.channelCount + channel] ?? 0;\n }\n}\n","import foaHrirUrl from \"./assets/sh_hrir_order_1.wav?url\";\nimport { installAutoplayUnlock } from \"./autoplayUnlock\";\nimport { Bus } from \"./bus\";\nimport { AudioCache, type ICache } from \"./cache\";\nimport type {\n AudioBuffer,\n AudioListener,\n AudioNode,\n AudioParam,\n AudioWorkletNode,\n BaseContext,\n BiquadFilterNode,\n ChannelMergerNode,\n ChannelSplitterNode,\n GainNode,\n PannerNode,\n} from \"./context\";\nimport {\n BarberpoleEffect,\n type BarberpoleOptions,\n type CacophonyEffect,\n DynamicsEffect,\n type DynamicsOptions,\n FdnReverbEffect,\n type FdnReverbOptions,\n FoaDecoder,\n FoaDecoderEffect,\n type FoaDecoderOptions,\n FrequencyShifterEffect,\n type FrequencyShifterOptions,\n HarmonizerEffect,\n type HarmonizerOptions,\n ImpulseResponseEffect,\n type ImpulseResponseOptions,\n type ImpulseResponseSource,\n ModulatedDelayEffect,\n type ModulatedDelayOptions,\n markAsCacophonyBiquad,\n PhaserEffect,\n type PhaserOptions,\n ReverbEffect,\n type ReverbOptions,\n ShareEffect,\n SpectralFreezeEffect,\n type SpectralFreezeOptions,\n StereoWidenerEffect,\n type StereoWidenerOptions,\n TremoloEffect,\n type TremoloOptions,\n WaveshaperEffect,\n type WaveshaperOptions,\n} from \"./effects\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { CacophonyEvents } from \"./events\";\nimport { Group } from \"./group\";\nimport { HlsAdapter } from \"./hlsAdapter\";\nimport { type CacophonyLogger, consoleLogger, noopLogger } from \"./logger\";\nimport { MediaStreamSound, type MediaStreamSoundOptions } from \"./mediaStream\";\nimport { LoudnessMeter } from \"./meters/loudness-meter\";\nimport { MicrophoneStream, type MicrophoneStreamOptions } from \"./microphone\";\nimport type { ThreeDOptions } from \"./pannerMixin\";\nimport { PcmStreamSound, type PcmStreamSoundOptions } from \"./pcmStream\";\nimport { GATE_DEFAULT_RATIO } from \"./processors/dynamics-core\";\nimport { DATTORRO_INV_SQRT2 } from \"./processors/modulated-delay-core\";\nimport { type TimeStretchOptions, timeStretch } from \"./processors/timestretch-core\";\nimport { Sound } from \"./sound\";\nimport { Synth } from \"./synth\";\nimport { WebCodecsPullAdapter, type WebCodecsStreamSound } from \"./webCodecsStream\";\nimport { ALL_WORKLETS, WORKLETS, type WorkletModule } from \"./worklets\";\n\nexport type SoundType = \"html\" | \"streaming\" | \"buffer\" | \"oscillator\";\n\nexport interface StreamCapabilities {\n transport: \"webcodecs\" | \"media-element\";\n seekable: boolean;\n live: boolean;\n duration: number;\n}\n\n/**\n * Represents a 3D position in space.\n * @typedef {Array<number>} Position - An array of three numbers representing the x, y, and z coordinates.\n */\nexport type Position = [x: number, y: number, z: number];\n\n/**\n * Represents the orientation of an object in 3D space.\n * @typedef {Object} Orientation - An object containing two positions: forward and up.\n * @property {Position} forward - The forward direction of the object.\n * @property {Position} up - The up direction of the object.\n */\nexport type Orientation = {\n forward: Position;\n up: Position;\n};\n\n/**\n * Represents the number of times a sound should loop.\n * @typedef {number | 'infinite'} LoopCount - The number of loops, or 'infinite' for endless looping.\n */\nexport type LoopCount = number | \"infinite\";\n\n/**\n * Represents the type of fade effect to apply.\n * @typedef {'linear' | 'exponential'} FadeType - The fade type, either 'linear' or 'exponential'.\n */\nexport type FadeType = \"linear\" | \"exponential\";\n\n/**\n * Represents the type of panning effect to apply.\n * @typedef {'HRTF' | 'stereo'} PanType - The pan type, either 'HRTF' for 3D audio or 'stereo' for traditional stereo panning.\n */\nexport type PanType = \"HRTF\" | \"stereo\";\n\n/**\n * Options for configuring fade behavior when starting playback via Sound.play().\n * @interface PlayOptions\n */\nexport interface PlayOptions {\n fadeIn?: number; // duration in ms\n fadeOut?: number; // duration in ms\n fadeType?: FadeType; // applies to both fadeIn and fadeOut\n fadeInPerLoop?: boolean; // re-trigger fadeIn on each loop iteration\n}\n\n/**\n * The base interface for any sound-producing entity, including individual sounds, groups, and playbacks.\n * @interface BaseSound\n */\nexport interface BaseSound {\n isPlaying: boolean;\n streamCapabilities?: StreamCapabilities;\n play(): BaseSound[];\n seek?(time: number): void;\n stop(): void;\n pause(): void;\n addFilter(filter: BiquadFilterNode): void;\n removeFilter(filter: BiquadFilterNode): void;\n volume: number;\n position?: Position;\n threeDOptions?: ThreeDOptions;\n fadeTo?(value: number, duration: number, type?: FadeType): Promise<void>;\n fadeIn?(duration: number, type?: FadeType): Promise<void>;\n fadeOut?(duration: number, type?: FadeType): Promise<void>;\n stopWithFade?(duration: number, type?: FadeType): Promise<void>;\n}\n\n/**\n * Options for creating an offline audio context.\n */\nexport interface OfflineOptions {\n numberOfChannels: number;\n length: number;\n sampleRate: number;\n context?: BaseContext & { startRendering(): Promise<AudioBuffer> };\n}\n\nexport interface RuntimeOptions {\n createAudioWorkletNode?: (context: BaseContext, name: string, options?: AudioWorkletNodeOptions) => any;\n /**\n * Optional hook to remap a worklet module URL just before it is handed to\n * `audioWorklet.addModule`. Receives the worklet's {@link WorkletModule.name}\n * and its default {@link WorkletModule.url}; return the URL to load (return\n * `url` unchanged to keep the default).\n *\n * The library build inlines every worklet bundle as a base64 `data:` URL,\n * which the browser loads directly but `node-web-audio-api` cannot resolve.\n * The `cacophony/node` adapter installs a resolver here that points\n * `addModule` at the bundle file on disk instead, so worklet-backed effects\n * work headless. Has no effect in the browser, where the default is used.\n */\n resolveWorkletUrl?: (name: string, url: string) => string | Promise<string>;\n /**\n * If `true` (the default), Cacophony installs one-time `touchend` / `click` /\n * `keydown` listeners on `document.body` whenever the audio context is\n * constructed in `suspended` state. The first user gesture resumes the\n * context, plays a silent primer buffer (required by iOS Safari for the\n * context to truly unlock), removes the listeners, and emits the `unlock`\n * event on the Cacophony instance.\n *\n * Set to `false` to opt out — you are then responsible for calling\n * `cacophony.resume()` yourself in response to a user gesture.\n *\n * Has no effect when the context is already running, on offline contexts,\n * or in non-browser environments (`document === undefined`).\n *\n * @default true\n */\n autoUnlock?: boolean;\n /**\n * Optional logger for Cacophony's host-side diagnostic output (the\n * `[cacophony/worklet]` messages emitted while loading AudioWorklet\n * modules, plus the \"AudioWorklet not supported\" warning).\n *\n * When provided, all such output is routed through this object instead of\n * the global `console`. Useful for capturing or redirecting logs in Node /\n * headless / CLI hosts. Takes precedence over {@link RuntimeOptions.quiet}.\n *\n * @default consoleLogger (forwards to `console`)\n */\n logger?: CacophonyLogger;\n /**\n * If `true`, suppresses all of Cacophony's host-side diagnostic output by\n * installing a no-op logger. Equivalent to passing `logger: noopLogger`.\n *\n * Ignored when an explicit {@link RuntimeOptions.logger} is also provided.\n *\n * @default false\n */\n quiet?: boolean;\n}\n\n/**\n * Resources belonging to a Sound that must be released when the Sound is\n * garbage collected without an explicit cleanup() call. The record is a plain\n * data bag with no back-references to the Sound or its Playbacks — a\n * FinalizationRegistry strongly retains its held value, so holding the Sound\n * itself would prevent the target from ever becoming collectable.\n */\nexport interface SoundCleanupHoldings {\n sources: Array<{ disconnect(): void }>;\n gainNodes: GainNode[];\n mediaElements: HTMLMediaElement[];\n}\n\nconst WORKLET_LOG_PREFIX = \"[cacophony/worklet]\";\n\n/**\n * Extension → MIME type map used by `createSound` format fallback to query\n * `HTMLAudioElement.canPlayType`. Keys are lower-cased extensions without\n * the leading dot.\n */\nconst EXTENSION_MIME_MAP: Readonly<Record<string, string>> = Object.freeze({\n webm: \"audio/webm\",\n mp3: \"audio/mpeg\",\n ogg: \"audio/ogg\",\n wav: \"audio/wav\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n opus: \"audio/ogg; codecs=opus\",\n});\n\n/**\n * Returns the MIME type for a URL based on its file extension, or `null` if\n * the extension is not recognised. Query strings and fragments are ignored.\n */\nfunction mimeTypeForUrl(url: string): string | null {\n const withoutQuery = url.split(/[?#]/, 1)[0];\n const dotIndex = withoutQuery.lastIndexOf(\".\");\n if (dotIndex < 0) {\n return null;\n }\n const ext = withoutQuery.slice(dotIndex + 1).toLowerCase();\n return EXTENSION_MIME_MAP[ext] ?? null;\n}\n\n/** Web Audio default listener pose when AudioParams are absent. */\nconst DEFAULT_LISTENER_POSITION: Position = [0, 0, 0];\nconst DEFAULT_LISTENER_FORWARD: Position = [0, 0, -1];\nconst DEFAULT_LISTENER_UP: Position = [0, 1, 0];\n\ntype ListenerOrientationParams = {\n readonly forwardX: AudioParam;\n readonly forwardY: AudioParam;\n readonly forwardZ: AudioParam;\n readonly upX: AudioParam;\n readonly upY: AudioParam;\n readonly upZ: AudioParam;\n};\n\ntype ListenerPositionParams = {\n readonly positionX: AudioParam;\n readonly positionY: AudioParam;\n readonly positionZ: AudioParam;\n};\n\nfunction isAudioParam(param: unknown): param is AudioParam {\n return typeof param === \"object\" && param !== null && \"value\" in param && typeof param.value === \"number\";\n}\n\nfunction isSchedulableAudioParam(param: unknown): param is AudioParam {\n return isAudioParam(param) && \"setValueAtTime\" in param && typeof param.setValueAtTime === \"function\";\n}\n\n/**\n * Real hosts are all-or-nothing: Chrome/Safari/SAC expose the full orientation\n * param set, Firefox exposes none. One predicate covers that split.\n */\nfunction hasListenerOrientationParams(listener: AudioListener): listener is AudioListener & ListenerOrientationParams {\n return (\n isAudioParam(listener.forwardX) &&\n isAudioParam(listener.forwardY) &&\n isAudioParam(listener.forwardZ) &&\n isAudioParam(listener.upX) &&\n isAudioParam(listener.upY) &&\n isAudioParam(listener.upZ)\n );\n}\n\nfunction hasListenerPositionParams(listener: AudioListener): listener is AudioListener & ListenerPositionParams {\n return (\n isSchedulableAudioParam(listener.positionX) &&\n isSchedulableAudioParam(listener.positionY) &&\n isSchedulableAudioParam(listener.positionZ)\n );\n}\n\nfunction readListenerOrientation(listener: ListenerOrientationParams): { forward: Position; up: Position } {\n return {\n forward: [listener.forwardX.value, listener.forwardY.value, listener.forwardZ.value],\n up: [listener.upX.value, listener.upY.value, listener.upZ.value],\n };\n}\n\nfunction readListenerPosition(listener: ListenerPositionParams): Position {\n return [listener.positionX.value, listener.positionY.value, listener.positionZ.value];\n}\n\n/**\n * Write listener orientation through AudioParams, or `setOrientation` when\n * those params are missing.\n */\nfunction writeListenerOrientation(listener: AudioListener, forward: Position, up: Position): void {\n const [forwardX, forwardY, forwardZ] = forward;\n const [upX, upY, upZ] = up;\n if (hasListenerOrientationParams(listener)) {\n listener.forwardX.value = forwardX;\n listener.forwardY.value = forwardY;\n listener.forwardZ.value = forwardZ;\n listener.upX.value = upX;\n listener.upY.value = upY;\n listener.upZ.value = upZ;\n return;\n }\n if (typeof listener.setOrientation === \"function\") {\n listener.setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ);\n return;\n }\n throw new Error(\"AudioListener does not support orientation updates\");\n}\n\n/**\n * Write listener position through AudioParams, or `setPosition` when those\n * params are missing.\n */\nfunction writeListenerPosition(listener: AudioListener, position: Position, currentTime: number): void {\n const [x, y, z] = position;\n if (hasListenerPositionParams(listener)) {\n listener.positionX.setValueAtTime(x, currentTime);\n listener.positionY.setValueAtTime(y, currentTime);\n listener.positionZ.setValueAtTime(z, currentTime);\n return;\n }\n if (typeof listener.setPosition === \"function\") {\n listener.setPosition(x, y, z);\n return;\n }\n throw new Error(\"AudioListener does not support position updates\");\n}\n\nexport class Cacophony {\n context: BaseContext;\n globalGainNode: GainNode;\n /**\n * The master bus — `master.input` IS `globalGainNode` (literally the same\n * GainNode), so every existing `.connect(globalGainNode)` call in the\n * codebase remains correct. `cacophony.volume` and `cacophony.mute` still\n * read and write `master.input.gain.value` through that alias.\n */\n master: Bus;\n listener: AudioListener;\n /**\n * Last-known listener pose. Used by getters when AudioParams are unreadable\n * (seeded from params or Web Audio defaults in the constructor).\n */\n private cachedListenerPosition: Position = [...DEFAULT_LISTENER_POSITION];\n private cachedListenerForward: Position = [...DEFAULT_LISTENER_FORWARD];\n private cachedListenerUp: Position = [...DEFAULT_LISTENER_UP];\n private prevVolume: number = 1;\n private isMuted: boolean = false;\n /**\n * Per-context cache of \"module-name has been loaded on this BaseContext\".\n * Keyed on the context itself because `AudioWorklet.addModule()` registers\n * the module against ONE context — a module loaded on context A is NOT\n * loaded on context B. A previous host-scoped `Set<string>` short-circuited\n * `loadAudioWorkletModule()` on name alone, so cross-context\n * {@link ReverbEffect.build} would skip `addModule` on the new context and\n * the second construct would throw with no module registered.\n */\n private loadedAudioWorklets: WeakMap<BaseContext, Set<string>> = new WeakMap();\n /**\n * Per-context cache of the decoded order-1 SH-HRIR `AudioBuffer` used by\n * {@link FoaDecoder}. Keyed on the context (like\n * {@link loadedAudioWorklets}) because `decodeAudioData` produces a buffer\n * bound to ONE context's sample rate; a buffer decoded on context A must not\n * be reused on context B. Stores the in-flight Promise so concurrent\n * `loadFoaHrir` calls share a single fetch/decode.\n */\n private foaHrirCache: WeakMap<BaseContext, Promise<AudioBuffer>> = new WeakMap();\n /**\n * Per-context, per-URL decoded impulse-response cache. Stores in-flight\n * promises so concurrent effect builds for the same context/URL share the\n * same fetch/decode. Rejected promises are evicted so the caller can retry.\n */\n private impulseResponseCache: WeakMap<BaseContext, Map<string, Promise<AudioBuffer>>> = new WeakMap();\n private finalizationRegistry: FinalizationRegistry<SoundCleanupHoldings>;\n private eventEmitter: TypedEventEmitter<CacophonyEvents> = new TypedEventEmitter<CacophonyEvents>();\n private cache: ICache;\n private createAudioWorkletNode: (context: BaseContext, name: string, options?: AudioWorkletNodeOptions) => any;\n private resolveWorkletUrl?: (name: string, url: string) => string | Promise<string>;\n private logger: CacophonyLogger;\n /**\n * Named-bus registry. Populated by {@link createBus} when a name is\n * supplied; entries are removed by the bus's onDestroy hook.\n */\n private _busRegistry: Map<string, Bus> = new Map();\n // Tracks the lifecycle state we have explicitly transitioned to. This avoids\n // duplicate wrapper-driven suspend calls, but resume() still delegates because\n // the underlying AudioContext can be suspended externally.\n private suspendState: \"unknown\" | \"running\" | \"suspended\" = \"unknown\";\n // Cleanup function for the autoplay-unlock state watcher and gesture\n // listeners. No-op when autoUnlock is disabled or unsupported.\n private autoplayUnlockCleanup: () => void = () => {};\n\n /**\n * Constructs a new Cacophony instance.\n *\n * If the supplied (or auto-constructed) audio context is in `suspended`\n * state and `runtimeOptions.autoUnlock` is `true` (default), one-time\n * `touchend` / `click` / `keydown` listeners are installed on\n * `document.body` so the first user gesture transparently unlocks audio\n * — matching Howler's `autoUnlock` behavior. See\n * {@link RuntimeOptions.autoUnlock} for the opt-out and the iOS primer\n * rationale.\n *\n * @param context - Audio context to use. If omitted, a fresh `AudioContext`\n * is constructed (which on mobile will be `suspended` until a user\n * gesture, triggering the auto-unlock path described above).\n * @param cache - Optional cache implementation. Defaults to `AudioCache`.\n * @param runtimeOptions - Optional runtime configuration including the\n * `autoUnlock` opt-out and a `createAudioWorkletNode` factory override.\n */\n constructor(context?: BaseContext, cache?: ICache, runtimeOptions: RuntimeOptions = {}) {\n this.context = context ?? new AudioContext();\n this.listener = this.context.listener;\n if (hasListenerPositionParams(this.listener)) {\n this.cachedListenerPosition = readListenerPosition(this.listener);\n }\n if (hasListenerOrientationParams(this.listener)) {\n const orientation = readListenerOrientation(this.listener);\n this.cachedListenerForward = orientation.forward;\n this.cachedListenerUp = orientation.up;\n }\n this.globalGainNode = this.context.createGain();\n // master bus wraps globalGainNode as its input — same node, two accessors.\n // master is exempt from the named-bus registry (its name 'master' is\n // reserved and not user-creatable via createBus).\n // The Bus constructor wires input → output. We then connect master.output\n // to context.destination so the full audible path is:\n // master.input (= globalGainNode) → [filters] → master.output → destination.\n // Critical: connect master.output AFTER constructing the Bus, NOT\n // globalGainNode directly. If globalGainNode were pre-connected to\n // destination, adding a master filter would call _refreshFilters() which\n // disconnects master.input's outgoing edges, severing the audible path.\n this.master = new Bus(this.context, \"master\", this.globalGainNode, undefined, false);\n this.master.output.connect(this.context.destination);\n this.cache = cache ?? new AudioCache();\n this.createAudioWorkletNode =\n runtimeOptions.createAudioWorkletNode ??\n ((workletContext, name, options) => new AudioWorkletNode(workletContext as any, name, options));\n this.resolveWorkletUrl = runtimeOptions.resolveWorkletUrl;\n this.logger = runtimeOptions.logger ?? (runtimeOptions.quiet ? noopLogger : consoleLogger);\n\n this.finalizationRegistry = new FinalizationRegistry((holdings) => {\n for (const source of holdings.sources) {\n source.disconnect();\n }\n for (const gainNode of holdings.gainNodes) {\n gainNode.disconnect();\n }\n for (const media of holdings.mediaElements) {\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n }\n });\n\n // Install autoplay unlock — guarded against offline contexts here and\n // unsupported/non-browser environments inside installAutoplayUnlock.\n // Offline contexts cannot suspend in the same way, so unlock makes no\n // sense for them.\n const autoUnlock = runtimeOptions.autoUnlock !== false;\n if (autoUnlock && !this.isOffline) {\n this.autoplayUnlockCleanup = installAutoplayUnlock({\n context: this.context,\n onUnlock: () => {\n this.suspendState = \"running\";\n this.emit(\"unlock\", undefined);\n },\n });\n }\n }\n\n /**\n * Returns `true` while the audio context is suspended — i.e. the library\n * cannot produce sound until the context is resumed (typically by a user\n * gesture). After the auto-unlock fires (or `resume()` is called manually),\n * this becomes `false`.\n *\n * Mirrors Howler's `Howler.ctx.state === 'suspended'` check via the\n * `Howler` global's `_audioUnlocked`. Use this to gate UI that hints to\n * the user that an interaction is required to start audio.\n */\n get locked(): boolean {\n const state = (this.context as unknown as { state?: string }).state;\n return state === \"suspended\";\n }\n\n /** @internal */\n registerSoundForCleanup(sound: object, holdings: SoundCleanupHoldings, unregisterToken: object): void {\n this.finalizationRegistry.register(sound, holdings, unregisterToken);\n }\n\n /** @internal */\n unregisterSoundCleanup(unregisterToken: object): void {\n this.finalizationRegistry.unregister(unregisterToken);\n }\n\n /**\n * Creates a Cacophony instance backed by an OfflineAudioContext.\n * Use this for rendering, bouncing, precomputing processed output,\n * or non-realtime scenarios.\n *\n * @param options - Offline context configuration (channels, length, sampleRate)\n * @param cache - Optional cache implementation\n * @param runtimeOptions - Optional runtime configuration\n * @returns A Cacophony instance backed by OfflineAudioContext\n */\n static createOffline(options: OfflineOptions, cache?: ICache, runtimeOptions?: RuntimeOptions): Cacophony {\n const offlineContext =\n options.context ?? new OfflineAudioContext(options.numberOfChannels, options.length, options.sampleRate);\n return new Cacophony(offlineContext, cache, runtimeOptions);\n }\n\n /**\n * Returns true if this instance is backed by an offline audio context\n * (i.e., the context has a startRendering method).\n */\n get isOffline(): boolean {\n return typeof this.context.startRendering === \"function\";\n }\n\n /**\n * Renders the offline audio graph to a buffer.\n * Only available when the context has a startRendering method.\n *\n * @returns Promise that resolves to the rendered AudioBuffer\n * @throws Error if the context does not support offline rendering\n */\n async startRendering(): Promise<AudioBuffer> {\n if (typeof this.context.startRendering !== \"function\") {\n throw new Error(\n \"startRendering() is only available on offline audio contexts. Use Cacophony.createOffline() to create one.\",\n );\n }\n return this.context.startRendering();\n }\n\n /**\n * OFFLINE independent time-stretch: change an AudioBuffer's tempo WITHOUT\n * changing its pitch, returning a NEW AudioBuffer of length ≈\n * `round(buffer.length · factor)` at the same sample rate.\n *\n * Algorithm: Phase Gradient Heap Integration (PGHI) per Zdeněk Průša & Nicki\n * Holighaus, \"Phase Vocoder Done Right\" (EUSIPCO 2017 / arXiv:2202.07382).\n * The signal is STFT'd, the synthesis phase is reconstructed by integrating\n * the analysis-phase time/frequency gradients along the magnitude ridges\n * (max-heap), then overlap-added at the stretched synthesis hop. No peak\n * picking and no transient detection. Each channel is processed independently.\n *\n * This is an OFFLINE buffer transform, NOT a real-time worklet: the project's\n * OLA worklet base is unity-rate (analysis hop == synthesis hop, fixed to the\n * 128-sample render quantum) and cannot change the time base in real time. A\n * genuine independent stretch needs analysis hop ≠ synthesis hop, which only\n * the whole-buffer offline path provides.\n *\n * @param buffer The source AudioBuffer to time-stretch.\n * @param factor Stretch factor (`> 0`). `> 1` lengthens (slower tempo), `< 1`\n * shortens (faster tempo); pitch is preserved either way.\n * @param options Optional PGHI parameters (fftSize, analysisHop, tol, seed).\n * @returns A new, time-stretched AudioBuffer.\n * @throws If the context cannot create buffers, or `factor <= 0`.\n */\n timeStretchBuffer(buffer: AudioBuffer, factor: number, options?: TimeStretchOptions): AudioBuffer {\n if (typeof this.context.createBuffer !== \"function\") {\n throw new Error(\"timeStretchBuffer requires an audio context that supports createBuffer().\");\n }\n if (!(factor > 0)) {\n throw new Error(`timeStretchBuffer: factor must be > 0, got ${factor}`);\n }\n\n const numberOfChannels = buffer.numberOfChannels;\n const outLength = Math.max(1, Math.round(buffer.length * factor));\n const output = this.context.createBuffer(numberOfChannels, outLength, buffer.sampleRate);\n\n for (let ch = 0; ch < numberOfChannels; ch++) {\n // Copy out of the (possibly mocked) AudioBuffer into a plain Float32Array\n // the pure core can consume, then write the stretched result back.\n const inData = buffer.getChannelData(ch);\n const input = inData instanceof Float32Array ? inData : Float32Array.from(inData);\n const stretched = timeStretch(input, factor, options);\n // The core returns exactly round(input.length·factor) samples; copy what\n // fits the output buffer (lengths match by construction).\n output.copyToChannel(stretched.subarray(0, outLength), ch);\n }\n\n return output;\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n emit<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n async emitAsync<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Eagerly registers every bundled AudioWorklet module on this context. This is\n * OPTIONAL — effects load their own worklet lazily in `build`, and the\n * pitch-shift path loads the phase-vocoder on first use. Call this up front to\n * pay the registration cost ahead of time. Idempotent per context (each module\n * short-circuits via the per-context {@link loadedAudioWorklets} set).\n */\n async loadWorklets(signal?: AbortSignal): Promise<void> {\n if (!this.context.audioWorklet) {\n this.logger.warn(\"AudioWorklet not supported\");\n return;\n }\n for (const worklet of ALL_WORKLETS) {\n await this.loadAudioWorkletModule(worklet.name, worklet.url, signal);\n }\n }\n\n async loadStereoToBFormatWorklet(signal?: AbortSignal): Promise<void> {\n await this.loadAudioWorkletModule(WORKLETS.stereoToBFormat.name, WORKLETS.stereoToBFormat.url, signal);\n }\n\n /**\n * The single worklet-effect construction seam ({@link WorkletEffectHost}).\n * Idempotently registers `worklet`'s module on `context` — or this host's own\n * context when omitted, honoring the cross-context contract that effects'\n * `build(context)` promises — then constructs the AudioWorkletNode with\n * `parameterData`. Every worklet-backed {@link CacophonyEffect} routes through\n * here, as does the phase-vocoder pitch-shift path ({@link Playback.setPitchShift}).\n */\n async buildWorkletEffect(\n worklet: WorkletModule,\n parameterData: Record<string, number>,\n context?: BaseContext,\n nodeOptions?: AudioWorkletNodeOptions,\n ): Promise<AudioWorkletNode> {\n await this.loadAudioWorkletModule(worklet.name, worklet.url, undefined, context);\n return this.createWorkletNode(\n worklet.name,\n worklet.url,\n undefined,\n { ...nodeOptions, parameterData: { ...(nodeOptions?.parameterData ?? {}), ...parameterData } },\n context,\n );\n }\n\n /**\n * Fetches and decodes the bundled order-1 SH-HRIR\n * (`sh_hrir_order_1.wav`, from Omnitone, Apache-2.0 — see\n * `src/assets/NOTICE`) into an `AudioBuffer`, memoized per context. The\n * buffer is the 4-channel (ACN rows W,Y,Z,X) SH-domain HRIR consumed by\n * {@link FoaDecoder} to drive its WY/ZX stereo ConvolverNodes\n * (Ahrens 2022 eq.31 decode).\n *\n * The WAV is 48 kHz; `decodeAudioData` resamples it to the context's sample\n * rate automatically when they differ (standard Web Audio behavior, which\n * Omnitone relies on).\n *\n * @param context Optional BaseContext to decode on. Defaults to this host's\n * own `context`. Supplied so a {@link FoaDecoder} added to a bus on\n * a different context decodes the HRIR on the right context.\n */\n async loadFoaHrir(context?: BaseContext): Promise<AudioBuffer> {\n const ctx = context ?? this.context;\n const cached = this.foaHrirCache.get(ctx);\n if (cached) {\n return cached;\n }\n const pending = (async () => {\n const response = await fetch(foaHrirUrl);\n const encoded = await response.arrayBuffer();\n return ctx.decodeAudioData(encoded);\n })();\n this.foaHrirCache.set(ctx, pending);\n try {\n return await pending;\n } catch (err) {\n // Drop the rejected promise so a later call can retry the fetch/decode.\n this.foaHrirCache.delete(ctx);\n throw err;\n }\n }\n\n /**\n * Fetches and decodes an impulse response URL on the requested audio context,\n * memoized per context and URL. Buffers decoded by one context are not reused\n * on another context, matching Web Audio's context-bound decode behavior.\n *\n * @param url Impulse-response audio URL.\n * @param context Optional decode context. Defaults to this Cacophony instance.\n * @param signal Optional abort signal for the fetch. Decode itself is not\n * abortable in Web Audio, but an already-aborted signal is honored before\n * decode starts.\n */\n async loadImpulseResponseBuffer(url: string, context?: BaseContext, signal?: AbortSignal): Promise<AudioBuffer> {\n const ctx = context ?? this.context;\n let cacheForContext = this.impulseResponseCache.get(ctx);\n if (!cacheForContext) {\n cacheForContext = new Map<string, Promise<AudioBuffer>>();\n this.impulseResponseCache.set(ctx, cacheForContext);\n }\n const cached = cacheForContext.get(url);\n if (cached) {\n return this.waitForImpulseResponseLoad(cached, signal);\n }\n if (signal?.aborted) {\n throw this.createAbortError();\n }\n\n const pending = (async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to load impulse response from ${url}: ${response.status} ${response.statusText}`);\n }\n const encoded = await response.arrayBuffer();\n return ctx.decodeAudioData(encoded);\n })();\n cacheForContext.set(url, pending);\n void pending.catch(() => {\n if (cacheForContext.get(url) === pending) {\n cacheForContext.delete(url);\n }\n });\n return this.waitForImpulseResponseLoad(pending, signal);\n }\n\n async createWorkletNode(\n name: string,\n url: string,\n signal?: AbortSignal,\n options?: AudioWorkletNodeOptions,\n context?: BaseContext,\n ): Promise<AudioWorkletNode> {\n // Use the supplied context for cross-context worklet construction\n // (e.g. ReverbEffect built against a context other than this host's\n // own). Default to this host's `context` for the common single-context\n // case so all existing callers keep working without a change.\n const ctx = context ?? this.context;\n // ensure audioWorklet has been loaded\n if (!ctx.audioWorklet) {\n throw new Error(\"AudioWorklet not supported\");\n }\n try {\n const node = this.createAudioWorkletNode(ctx, name, options);\n this.logger.info(`${WORKLET_LOG_PREFIX} construct succeeded`, {\n name,\n loaded: this.isWorkletLoadedOn(ctx, name),\n });\n return node;\n } catch (err) {\n this.logger.warn(`${WORKLET_LOG_PREFIX} construct failed`, {\n name,\n loaded: this.isWorkletLoadedOn(ctx, name),\n error: err,\n });\n try {\n await this.loadAudioWorkletModule(name, url, signal, ctx);\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} load failed`, {\n name,\n error: err,\n });\n throw err; // Preserve original error (including AbortError)\n }\n\n try {\n const node = this.createAudioWorkletNode(ctx, name, options);\n this.logger.info(`${WORKLET_LOG_PREFIX} construct after load succeeded`, { name });\n return node;\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} construct after load failed`, {\n name,\n error: err,\n });\n throw err;\n }\n }\n }\n\n async createStereoToBFormatNode(signal?: AbortSignal): Promise<AudioWorkletNode> {\n return this.createWorkletNode(WORKLETS.stereoToBFormat.name, WORKLETS.stereoToBFormat.url, signal, {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [4],\n channelCount: 2,\n channelCountMode: \"explicit\",\n channelInterpretation: \"speakers\",\n });\n }\n\n /**\n * Lookup helper: has worklet `name` been loaded on `ctx` (per the\n * per-context {@link loadedAudioWorklets} cache).\n */\n private isWorkletLoadedOn(ctx: BaseContext, name: string): boolean {\n return this.loadedAudioWorklets.get(ctx)?.has(name) ?? false;\n }\n\n /**\n * Record `name` as loaded on `ctx`, lazily allocating the per-context Set.\n */\n private markWorkletLoadedOn(ctx: BaseContext, name: string): void {\n let names = this.loadedAudioWorklets.get(ctx);\n if (!names) {\n names = new Set<string>();\n this.loadedAudioWorklets.set(ctx, names);\n }\n names.add(name);\n }\n\n private async loadAudioWorkletModule(\n name: string,\n url: string,\n signal?: AbortSignal,\n context?: BaseContext,\n ): Promise<void> {\n const ctx = context ?? this.context;\n if (!ctx.audioWorklet) {\n throw new Error(\"AudioWorklet not supported\");\n }\n // Per-context membership: a module loaded on context A is NOT loaded on\n // context B. The previous host-scoped check by name only would skip the\n // addModule on B after A had loaded the same name, leaving B without\n // the worklet registered.\n if (this.isWorkletLoadedOn(ctx, name)) {\n this.logger.info(`${WORKLET_LOG_PREFIX} load skipped`, { name });\n return;\n }\n // Host seam: the browser loads the inlined `data:` bundle directly, but the\n // Node backend remaps to the on-disk bundle file (see RuntimeOptions.resolveWorkletUrl).\n const resolvedUrl = this.resolveWorkletUrl ? await this.resolveWorkletUrl(name, url) : url;\n this.logger.info(`${WORKLET_LOG_PREFIX} addModule start`, {\n name,\n url: resolvedUrl,\n aborted: signal?.aborted ?? false,\n });\n try {\n await ctx.audioWorklet.addModule(resolvedUrl, {\n credentials: \"same-origin\",\n ...(signal && { signal }),\n });\n this.markWorkletLoadedOn(ctx, name);\n this.logger.info(`${WORKLET_LOG_PREFIX} addModule resolved`, { name });\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} addModule rejected`, {\n name,\n error: err,\n });\n throw err;\n }\n }\n\n private createAbortError(): DOMException {\n return new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n private waitForImpulseResponseLoad(pending: Promise<AudioBuffer>, signal?: AbortSignal): Promise<AudioBuffer> {\n if (!signal) {\n return pending;\n }\n if (signal.aborted) {\n return Promise.reject(this.createAbortError());\n }\n return new Promise((resolve, reject) => {\n const handleAbort = () => {\n cleanup();\n reject(this.createAbortError());\n };\n const cleanup = () => {\n signal.removeEventListener(\"abort\", handleAbort);\n };\n signal.addEventListener(\"abort\", handleAbort, { once: true });\n pending.then(\n (buffer) => {\n cleanup();\n resolve(buffer);\n },\n (err: unknown) => {\n cleanup();\n reject(err);\n },\n );\n });\n }\n\n private createMediaSound(\n url: string,\n soundType: \"html\" | \"streaming\",\n panType: PanType,\n signal?: AbortSignal,\n preparedAudio?: HTMLAudioElement,\n useHlsJs: boolean = false,\n ): Promise<Sound> {\n if (signal?.aborted) {\n return Promise.reject(this.createAbortError());\n }\n\n return new Promise<Sound>((resolve, reject) => {\n const audio = preparedAudio ?? new Audio();\n let hlsAdapter: HlsAdapter | undefined;\n let sound: Sound | undefined;\n let settled = false;\n const pendingHlsErrors: Array<{ error: Error; recoverable: boolean }> = [];\n\n const cleanup = () => {\n audio.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.removeEventListener(\"error\", handleError);\n signal?.removeEventListener(\"abort\", handleAbort);\n };\n\n const teardown = () => {\n hlsAdapter?.destroy();\n audio.pause();\n audio.src = \"\";\n audio.load();\n };\n\n const settle = (callback: () => void) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n callback();\n };\n\n const handleLoadedMetadata = () => {\n settle(() => {\n sound = new Sound(\n url,\n undefined,\n this.context,\n this.globalGainNode,\n soundType,\n panType,\n this,\n audio,\n hlsAdapter ? () => hlsAdapter?.destroy() : undefined,\n );\n if (soundType === \"streaming\") {\n sound.streamCapabilities = {\n duration: Number.isFinite(audio.duration) ? audio.duration : Number.POSITIVE_INFINITY,\n live: audio.duration === Number.POSITIVE_INFINITY,\n seekable: (audio.seekable?.length ?? 0) > 0,\n transport: \"media-element\",\n };\n }\n for (const { error, recoverable } of pendingHlsErrors) {\n sound.reportLoadError(error, recoverable);\n }\n resolve(sound);\n });\n };\n\n const handleHlsError = (error: Error, recoverable: boolean) => {\n if (sound) {\n sound.reportLoadError(error, recoverable);\n } else if (recoverable) {\n pendingHlsErrors.push({ error, recoverable });\n } else {\n settle(() => {\n teardown();\n reject(error);\n });\n }\n };\n\n const handleError = () => {\n const error = new Error(`Failed to load audio from ${url}`);\n settle(() => {\n teardown();\n reject(error);\n });\n };\n\n const handleAbort = () => {\n const error = this.createAbortError();\n settle(() => {\n teardown();\n reject(error);\n });\n };\n\n audio.crossOrigin = \"anonymous\";\n audio.preload = \"auto\";\n audio.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.addEventListener(\"error\", handleError);\n signal?.addEventListener(\"abort\", handleAbort, { once: true });\n if (useHlsJs) {\n void HlsAdapter.create(handleHlsError).then(\n (adapter) => {\n hlsAdapter = adapter;\n if (settled) {\n adapter.destroy();\n return;\n }\n try {\n adapter.attach(audio, url);\n } catch (error) {\n settle(() => {\n teardown();\n reject(error);\n });\n }\n },\n (error: unknown) => {\n settle(() => {\n teardown();\n reject(error);\n });\n },\n );\n } else {\n audio.src = url;\n audio.load();\n }\n });\n }\n\n private createHlsSound(url: string, panType: PanType, signal?: AbortSignal): Promise<Sound> {\n const audio = new Audio();\n const nativeHls = audio.canPlayType(\"application/vnd.apple.mpegurl\") || audio.canPlayType(\"application/x-mpegURL\");\n return this.createMediaSound(url, \"streaming\", panType, signal, audio, !nativeHls);\n }\n\n clearMemoryCache(): void {\n this.cache.clearMemoryCache();\n }\n\n createOscillator(options: OscillatorOptions, panType: PanType = \"HRTF\"): Synth {\n const synth = new Synth(this.context, this.globalGainNode, \"oscillator\", panType, options, this);\n return synth;\n }\n\n /**\n * Creates a Sound instance from an AudioBuffer or URL.\n *\n * @param bufferOrUrl - AudioBuffer instance or URL string to create sound from\n * @param soundType - Type of sound (Buffer, HTML, Streaming)\n * @param panType - Type of panning (HRTF or stereo)\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to a Sound instance\n */\n async createSound(\n buffer: AudioBuffer,\n soundType?: SoundType,\n panType?: PanType,\n signal?: AbortSignal,\n ): Promise<Sound>;\n\n async createSound(url: string, soundType?: SoundType, panType?: PanType, signal?: AbortSignal): Promise<Sound>;\n\n /**\n * Creates a Sound from the first playable URL in a Howler-style fallback\n * array. The browser's `HTMLAudioElement.canPlayType` is queried per\n * extension; the first source it reports as supported (`'probably'` or\n * `'maybe'`) is fetched and decoded. If decoding rejects for a candidate,\n * the next playable candidate is tried — a `'maybe'` can still fail at\n * decode time.\n *\n * MIME types are inferred from file extensions: `.webm` → `audio/webm`,\n * `.mp3` → `audio/mpeg`, `.ogg` → `audio/ogg`, `.wav` → `audio/wav`,\n * `.flac` → `audio/flac`, `.m4a` → `audio/mp4`, `.aac` → `audio/aac`,\n * `.opus` → `audio/ogg; codecs=opus`.\n *\n * Cache and loading events fire only for the URL actually fetched.\n *\n * v1 limitation: only the default `'buffer'` sound type participates in\n * fallback. Passing an array with any non-`'buffer'` soundType (e.g.\n * `'html'`, `'streaming'`, `'oscillator'`) rejects with a clear \"not yet\n * supported\" error.\n *\n * @param urls - Non-empty array of candidate URLs in priority order.\n * @throws If the array is empty, if `soundType` is not `'buffer'`, or if\n * no candidate is playable (codec unsupported or every decode failed).\n * The error message names the URLs that were tried.\n */\n async createSound(urls: string[], soundType?: SoundType, panType?: PanType, signal?: AbortSignal): Promise<Sound>;\n\n async createSound(\n bufferOrUrl: AudioBuffer | string | string[],\n soundType: SoundType = \"buffer\",\n panType: PanType = \"HRTF\",\n signal?: AbortSignal,\n ): Promise<Sound> {\n if (Array.isArray(bufferOrUrl)) {\n return this.createSoundFromUrlArray(bufferOrUrl, soundType, panType, signal);\n }\n if (typeof bufferOrUrl === \"object\") {\n return Promise.resolve(new Sound(\"\", bufferOrUrl, this.context, this.globalGainNode, soundType, panType, this));\n }\n const url = bufferOrUrl;\n if (soundType === \"html\") {\n return this.createMediaSound(url, \"html\", panType, signal);\n }\n if (soundType === \"streaming\") {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n return this.loadBufferSound(url, soundType, panType, signal);\n }\n\n private async loadBufferSound(\n url: string,\n soundType: SoundType,\n panType: PanType,\n signal?: AbortSignal,\n ): Promise<Sound> {\n const buffer = await this.cache.getAudioBuffer(this.context, url, signal, {\n onLoadingStart: (event) => this.emitAsync(\"loadingStart\", event),\n onLoadingProgress: (event) => this.emitAsync(\"loadingProgress\", event),\n onLoadingComplete: (event) => this.emitAsync(\"loadingComplete\", event),\n onLoadingError: (event) => this.emitAsync(\"loadingError\", event),\n onCacheHit: (event) => this.emitAsync(\"cacheHit\", event),\n onCacheMiss: (event) => this.emitAsync(\"cacheMiss\", event),\n onCacheError: (event) => this.emitAsync(\"cacheError\", event),\n });\n return new Sound(url, buffer, this.context, this.globalGainNode, soundType, panType, this);\n }\n\n /**\n * Returns true if the error came from `AudioContext.decodeAudioData`.\n *\n * Per the Web Audio spec, decode failures throw a `DOMException` with name\n * `EncodingError`. We deliberately do NOT treat plain `Error` instances as\n * decode failures: fetch/cache/network errors must propagate so the caller\n * sees the real cause instead of silently falling through to a different\n * format. See `reports/format-fallback-codex-review.md` (Major 1).\n */\n private static isDecodeError(error: unknown): boolean {\n if (typeof DOMException === \"undefined\") {\n return false;\n }\n return error instanceof DOMException && error.name === \"EncodingError\";\n }\n\n private async createSoundFromUrlArray(\n urls: string[],\n soundType: SoundType,\n panType: PanType,\n signal?: AbortSignal,\n ): Promise<Sound> {\n if (urls.length === 0) {\n throw new Error(\"createSound: URL array is empty; provide at least one URL\");\n }\n if (soundType !== \"buffer\") {\n throw new Error(\n `createSound: URL array with soundType='${soundType}' is not yet supported. ` +\n `Format fallback is only available for buffer sounds in this version.`,\n );\n }\n const candidateReasons = new Map<string, string>();\n const playable: string[] = [];\n for (const url of urls) {\n if (this.canPlaySource(url)) {\n playable.push(url);\n } else {\n const ext = url.match(/\\.([a-z0-9]+)(?:\\?|#|$)/i)?.[1]?.toLowerCase();\n candidateReasons.set(url, ext ? `codec unsupported: .${ext}` : \"codec unsupported: unknown extension\");\n }\n }\n if (playable.length === 0) {\n const detail = urls.map((u) => `${u}: ${candidateReasons.get(u) ?? \"codec unsupported\"}`).join(\"; \");\n throw new Error(\n `createSound: no playable source found among [${urls.join(\", \")}] ` +\n `(no candidate's MIME type was reported as supported by HTMLAudioElement.canPlayType). ` +\n `Reasons: ${detail}`,\n );\n }\n for (const url of playable) {\n try {\n // Array path always produces buffer sounds; html/streaming were rejected above.\n return await this.loadBufferSound(url, \"buffer\", panType, signal);\n } catch (error) {\n if ((error as { name?: string } | null)?.name === \"AbortError\") {\n throw error;\n }\n if (!Cacophony.isDecodeError(error)) {\n // Fetch/cache/network failure of the selected source — propagate.\n // Only decode failures fall through to the next candidate.\n throw error;\n }\n candidateReasons.set(url, `decode failed: ${(error as Error)?.message ?? String(error)}`);\n }\n }\n const detail = urls.map((u) => `${u}: ${candidateReasons.get(u) ?? \"unknown\"}`).join(\"; \");\n throw new Error(\n `createSound: every playable candidate failed to decode. Tried [${urls.join(\", \")}]. Reasons: ${detail}`,\n );\n }\n\n private canPlaySource(url: string): boolean {\n if (typeof Audio === \"undefined\") {\n return true;\n }\n const mime = mimeTypeForUrl(url);\n if (!mime) {\n return false;\n }\n try {\n const probe = new Audio();\n const result = probe.canPlayType(mime);\n return result === \"probably\" || result === \"maybe\";\n } catch {\n return false;\n }\n }\n\n async createGroup(sounds: Sound[]): Promise<Group> {\n const group = new Group();\n sounds.forEach((sound) => group.addSound(sound));\n return group;\n }\n\n /**\n * Creates a Group containing Sound instances loaded from multiple URLs.\n *\n * @param urls - Array of URL strings to load as sounds\n * @param soundType - Type of sound (Buffer, HTML, Streaming)\n * @param panType - Type of panning (HRTF or stereo)\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to a Group containing all loaded sounds\n */\n async createGroupFromUrls(\n urls: string[],\n soundType: SoundType = \"buffer\",\n panType: PanType = \"HRTF\",\n signal?: AbortSignal,\n ): Promise<Group> {\n const group = new Group();\n const sounds = await Promise.all(urls.map((url) => this.createSound(url, soundType, panType, signal)));\n sounds.forEach((sound) => group.addSound(sound));\n return group;\n }\n\n /**\n * Creates a sample-accurate URL stream when WebCodecs can decode the primary\n * audio track. Encoded media is fetched and demuxed incrementally, then its\n * PCM is fed into the same routable AudioWorklet source returned by\n * {@link createPcmStreamSound}. Browsers without WebCodecs, or without a\n * decoder for the track, fall back to the media-element streaming tier.\n * @param url - Encoded audio stream URL.\n * @param signal - Optional cancellation signal retained as the second\n * parameter for backwards compatibility.\n * @param panType - Type of panning (HRTF or stereo).\n */\n async createStream(\n url: string,\n signal?: AbortSignal,\n panType: PanType = \"HRTF\",\n ): Promise<Sound | WebCodecsStreamSound> {\n if (/\\.m3u8(?:$|[?#])/i.test(url)) {\n return this.createHlsSound(url, panType, signal);\n }\n\n if (typeof globalThis.AudioDecoder !== \"function\") {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n\n const adapter = await WebCodecsPullAdapter.open(url, this.context.sampleRate, signal);\n if (!adapter) {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n try {\n const sound = await this.createPcmStreamSound({\n channelCount: adapter.channelCount,\n panType,\n signal,\n });\n return adapter.attach(sound);\n } catch (error) {\n adapter.cleanup();\n throw error;\n }\n }\n\n createMediaStreamSound(stream: MediaStream, options?: MediaStreamSoundOptions): MediaStreamSound {\n return new MediaStreamSound(stream, this.context, this.globalGainNode, options, this);\n }\n\n /**\n * Creates a push-based PCM source backed by a fixed-size AudioWorklet ring\n * buffer. Each write is an interleaved Float32Array at this context's sample\n * rate.\n */\n async createPcmStreamSound(options: PcmStreamSoundOptions = {}): Promise<PcmStreamSound> {\n options.signal?.throwIfAborted();\n const channelCount = options.channelCount ?? 1;\n const bufferDuration = options.bufferDuration ?? 1;\n const latency = options.latency ?? 0.05;\n if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 32) {\n throw new RangeError(\"PCM channelCount must be an integer between 1 and 32\");\n }\n if (!Number.isFinite(bufferDuration) || bufferDuration <= 0) {\n throw new RangeError(\"PCM bufferDuration must be greater than zero\");\n }\n if (!Number.isFinite(latency) || latency < 0 || latency > bufferDuration) {\n throw new RangeError(\"PCM latency must be between zero and bufferDuration\");\n }\n\n const capacityFrames = Math.ceil(this.context.sampleRate * bufferDuration);\n const latencyFrames = Math.ceil(this.context.sampleRate * latency);\n const node = await this.createWorkletNode(WORKLETS.pcmStream.name, WORKLETS.pcmStream.url, options.signal, {\n numberOfInputs: 0,\n numberOfOutputs: 1,\n outputChannelCount: [channelCount],\n channelCount,\n channelCountMode: \"explicit\",\n processorOptions: {\n capacityFrames,\n channelCount,\n latencyFrames,\n },\n });\n const sound = new PcmStreamSound(\n node,\n this.context,\n this.globalGainNode,\n {\n ...options,\n channelCount,\n bufferDuration,\n latency,\n },\n this,\n );\n if (options.signal?.aborted) {\n sound.cleanup();\n options.signal.throwIfAborted();\n }\n return sound;\n }\n\n createBiquadFilter = ({ type, frequency, gain, Q }: BiquadFilterOptions): BiquadFilterNode => {\n if (frequency === undefined) {\n frequency = 350;\n }\n const filter = this.context.createBiquadFilter();\n filter.type = type ?? \"lowpass\";\n filter.frequency.value = frequency;\n filter.gain.value = gain ?? 0;\n filter.Q.value = Q ?? 1;\n markAsCacophonyBiquad(filter);\n return filter;\n };\n\n /**\n * Wraps a raw AudioNode in a {@link CacophonyEffect} so it can be added\n * to a {@link Bus} filter chain. By default `Bus.addFilter` rejects raw\n * AudioNodes — wrapping via `shareEffect` is the explicit opt-in that\n * signals \"yes, I understand this single node will be shared across every\n * bus I add it to.\"\n */\n shareEffect(node: AudioNode): CacophonyEffect {\n return new ShareEffect(node);\n }\n\n /**\n * Creates a DattorroReverb {@link CacophonyEffect}. The effect's `build`\n * lazily registers the worklet module (no-op if already loaded) and\n * constructs the AudioWorkletNode with the supplied {@link ReverbOptions}\n * as `parameterData`. Add the returned effect to a {@link Bus}'s filter\n * chain via `bus.addFilter(effect)`.\n */\n createReverb(options: ReverbOptions = {}): ReverbEffect {\n return new ReverbEffect(this, options);\n }\n\n /**\n * Creates a native ConvolverNode impulse-response effect. Pass an AudioBuffer\n * for an already-decoded IR or a URL to fetch/decode through the per-context\n * IR cache. The default is wet-only (`dry: 0`, `wet: 1`) and returns a single\n * ConvolverNode when added to a bus; setting `dry` or non-unity `wet` builds\n * an owned dry/wet endpoint graph exposing `dry` and `wet` automation params.\n */\n createImpulseResponse(source: ImpulseResponseSource, options: ImpulseResponseOptions = {}): ImpulseResponseEffect {\n return new ImpulseResponseEffect(this, source, options);\n }\n\n /**\n * Creates a Feedback Delay Network (FDN) {@link CacophonyEffect} — an\n * algorithmic reverb with a lossless degree-0 paraunitary Hadamard feedback\n * matrix (Schlecht & Habets 2019), per-delay-line absorption filters setting\n * the reverberation time T60 (Jot & Chaigne 1991), and multiplication-free\n * velvet-noise input diffusion (Fagerström et al. 2020). The effect's\n * `build` lazily registers the worklet module (no-op if already loaded) and\n * constructs the AudioWorkletNode with the supplied {@link FdnReverbOptions}\n * as `parameterData`. Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createFdnReverb(options: FdnReverbOptions = {}): FdnReverbEffect {\n return new FdnReverbEffect(this, options);\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} configured as a COMPRESSOR\n * (ratio > 1 reduces the level of signals above threshold). Implements the\n * feed-forward design of Giannoulis, Massberg & Reiss 2012. Add the returned\n * effect to a {@link Bus} via `bus.addFilter(effect)`. The same machinery\n * (gain computer + log-domain smooth-branching detector) backs the limiter\n * and gate presets below.\n */\n createCompressor(options: DynamicsOptions = {}): DynamicsEffect {\n return new DynamicsEffect(this, options);\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} preset as a LIMITER — a\n * compressor with an effectively infinite ratio so output is clamped at the\n * threshold (Giannoulis 2012 eqs 18-19). The ratio is fixed to the worklet's\n * limiter sentinel; caller-supplied `ratio` is ignored. Other params\n * (threshold, knee, attack, release, makeup) remain configurable.\n */\n createLimiter(options: Omit<DynamicsOptions, \"ratio\"> = {}): DynamicsEffect {\n return new DynamicsEffect(this, { ...options, ratio: 1000 });\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} preset as a downward EXPANDER /\n * GATE — ratio < 1 so signals BELOW the threshold are pushed further down\n * (Giannoulis 2012 p.403). The default `ratio` of 0.1 gives gate-like\n * downward expansion; pass a `ratio` closer to 1 for gentler expansion.\n */\n createGate(options: DynamicsOptions = {}): DynamicsEffect {\n return new DynamicsEffect(this, { ratio: GATE_DEFAULT_RATIO, ...options });\n }\n\n /**\n * Creates a {@link WaveshaperEffect} — an antialiased distortion/waveshaper\n * implementing first-order Antiderivative Antialiasing (Parker, Zavalishin &\n * Le Bivic 2016, DAFx-16): y[n] = (F0(x_n) - F0(x_{n-1}))/(x_n - x_{n-1})\n * (eq.9), with an f(midpoint) fallback at the 0/0 singularity (eq.10). Ships\n * two nonlinearities — `shape: 0` hard clip (polynomial F0, eq.25), `shape: 1`\n * tanh soft clip (F0 = log cosh, eq.20). Carries an inherent 0.5-sample group\n * delay (eq.17). Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createWaveshaper(options: WaveshaperOptions = {}): WaveshaperEffect {\n return new WaveshaperEffect(this, options);\n }\n\n /**\n * Creates a {@link WaveshaperEffect} preset as a DISTORTION — the tanh soft\n * clipper (`shape: 1`, F0 = log cosh, Parker 2016 eq.20) with a default drive\n * of 4 for an audible saturated tone. A convenience wrapper over\n * {@link createWaveshaper}; caller-supplied options override the defaults.\n */\n createDistortion(options: WaveshaperOptions = {}): WaveshaperEffect {\n return new WaveshaperEffect(this, { drive: 4, shape: 1, ...options });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a DELAY / ECHO — Dattorro's\n * unified modulated-delay circuit (JAES 1997, Fig. 36) with the modulation OFF\n * (`depth: 0`, `rate: 0`). Echo knobs (Table 6): full dry `blend: 1` plus a\n * unity wet tap `feedforward: 1`; feedback defaults to 0 (a single tap) but is\n * caller-configurable for a regenerating echo (|feedback| < 1, Table 6). The\n * 250 ms tap sits in the echo range (Table 7). All knobs are exposed.\n */\n createDelay(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: 1,\n feedforward: 1,\n feedback: 0,\n delayTime: 250,\n depth: 0,\n rate: 0,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a (white) CHORUS — Dattorro\n * Table 6 white-chorus knobs `blend: 0.7071`, `feedforward: 1`,\n * `feedback: 0.7071` (blend = feedback, feedforward = 1, the negative-feedback\n * path that minimizes the spectral aberration of a plain dry+wet chorus). A\n * 9 ms tap modulated 4 ms at 0.5 Hz sits in the chorus range (Table 7).\n *\n * NOTE: Dattorro's white chorus (p.776) combines this negative-feedback path\n * with ALLPASS fractional-delay interpolation. This implementation uses the\n * white-chorus knob settings but substitutes cubic-Lagrange interpolation\n * (Laakso 1996) — transient-safe under modulation (Laakso p.52), trading some\n * high-frequency trough depth versus a true allpass-interpolated white chorus.\n */\n createChorus(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: 1,\n feedback: DATTORRO_INV_SQRT2,\n delayTime: 9,\n depth: 4,\n rate: 0.5,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a FLANGER — Dattorro Table 6\n * flanger knobs `blend: 0.7071`, `feedforward: 0.7071` (blend = feedforward for\n * the deepest comb trough), `feedback: -0.7071` (negative feedback deepens the\n * troughs). A short 5 ms tap swept 4 ms at 0.25 Hz gives the classic flange\n * (Table 7).\n */\n createFlanger(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: DATTORRO_INV_SQRT2,\n feedback: -DATTORRO_INV_SQRT2,\n delayTime: 5,\n depth: 4,\n rate: 0.25,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a VIBRATO — Dattorro Table 6\n * vibrato knobs `blend: 0`, `feedforward: 1`, `feedback: 0` (100% wet, no dry\n * path, no feedback), so only the pitch-modulated delayed signal is heard. A\n * 5 ms tap swept 3 ms at 5 Hz (Table 7).\n */\n createVibrato(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: 0,\n feedforward: 1,\n feedback: 0,\n delayTime: 5,\n depth: 3,\n rate: 5,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as DOUBLING (\"double tracking\")\n * — Dattorro Table 6 doubling knobs `blend: 0.7071`, `feedforward: 0.7071`,\n * `feedback: 0` (no feedback). A ~20 ms tap (Table 7 doubling nominal) with a\n * gentle 1 ms / 0.4 Hz wobble fattens a single take into two.\n */\n createDoubling(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: DATTORRO_INV_SQRT2,\n feedback: 0,\n delayTime: 20,\n depth: 1,\n rate: 0.4,\n ...options,\n });\n }\n\n /**\n * Creates a {@link PhaserEffect} — a classic MXR/Univibe-style phase shifter: a\n * cascade of first-order allpass sections at a common LFO-swept break frequency\n * summed additively with the dry signal to sweep notches through the spectrum\n * (Smith STAN-M-21; PASP §8.9). Two allpass sections per notch (default 4\n * sections = 2 notches). Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createPhaser(options: PhaserOptions = {}): PhaserEffect {\n return new PhaserEffect(this, options);\n }\n\n /**\n * Creates a {@link TremoloEffect} — LFO-driven amplitude modulation (a VCA\n * swung by a low-frequency oscillator). The gain swings between (1 - depth)\n * and 1, never inverting (a true tremolo, not ring modulation). Anchored to\n * standard AM theory, Dattorro 1997 (p.776) for the quadrature stereo LFO, and\n * Mitcheltree et al. (DAFx23) for the LFO-driven-effect framing. `shape` selects\n * the LFO waveform (0 = sine, 1 = triangle, 2 = square); `stereoPhase` offsets\n * the per-channel LFO. Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createTremolo(options: TremoloOptions = {}): TremoloEffect {\n return new TremoloEffect(this, options);\n }\n\n /**\n * Creates a {@link TremoloEffect} preset as an AUTO-PAN — a tremolo with\n * `stereoPhase: 180`, so the left and right channel gains modulate in\n * anti-phase (the sound swings hard between the speakers). The 180-deg\n * per-channel LFO offset is Dattorro's stereo-modulation convention (p.776). A\n * convenience wrapper over {@link createTremolo}; caller options override the\n * preset.\n */\n createAutoPan(options: TremoloOptions = {}): TremoloEffect {\n return new TremoloEffect(this, { stereoPhase: 180, ...options });\n }\n\n /**\n * Creates a Hilbert-transform single-sideband frequency shifter (Wardle,\n * DAFx-98). Unlike pitch shifting, every partial moves by the same signed\n * number of hertz, deliberately breaking harmonic ratios.\n */\n createFrequencyShifter(options: FrequencyShifterOptions = {}): FrequencyShifterEffect {\n return new FrequencyShifterEffect(this, options);\n }\n\n /**\n * Creates the spectral-delay SSB barberpole effect from Esqueda, Valimaki &\n * Parker (DAFx-15, Fig. 12): notches travel indefinitely in the direction\n * selected by the sign of `rate`, rather than reversing like a normal phaser.\n */\n createBarberpole(options: BarberpoleOptions = {}): BarberpoleEffect {\n return new BarberpoleEffect(this, options);\n }\n\n /** Adds two identity-phase-locked pitch voices in one Laroche-Dolson STFT pass. */\n createHarmonizer(options: HarmonizerOptions = {}): HarmonizerEffect {\n return new HarmonizerEffect(this, options);\n }\n\n /**\n * Captures and sustains a spectrum while continuing each bin's measured\n * phase advance. Automate the `freeze` AudioParam to capture/release.\n */\n createSpectralFreeze(options: SpectralFreezeOptions = {}): SpectralFreezeEffect {\n return new SpectralFreezeEffect(this, options);\n }\n\n /** Creates an explicit-stereo sparse decorrelator with transient protection. */\n createStereoWidener(options: StereoWidenerOptions = {}): StereoWidenerEffect {\n return new StereoWidenerEffect(this, options);\n }\n\n /** The default audio context for this Cacophony instance (used by\n * {@link FoaDecoder} when no explicit context is supplied). */\n defaultContext(): BaseContext {\n return this.context;\n }\n\n /**\n * Creates a {@link FoaDecoder} — a standalone first-order ambisonic (FOA) ->\n * binaural FORMAT CONVERTER built from native Web Audio nodes (no worklet).\n * It decodes a 4-channel ACN/SN3D `[W, Y, Z, X]` signal to headphone stereo\n * via the per-SH-channel HRIR decode of Ahrens 2022 (eq.31), using\n * Omnitone's WY/ZX 2-stereo-ConvolverNode packing and the bundled order-1\n * SH-HRIR.\n *\n * It is 4-channel-in / 2-channel-out; this method returns the explicit\n * endpoint object for custom graph wiring:\n * feed FOA into `decoder.input` (4-ch) and route `decoder.output` (2-ch\n * stereo) downstream:\n * ```ts\n * const decoder = await cacophony.createFoaDecoder();\n * foaSource.connect(decoder.input);\n * decoder.output.connect(bus.input); // or context.destination\n * ```\n * Build is async (the bundled HRIR is fetched + decoded). Pair it with\n * {@link encodeMonoToFoaSN3D} (clean, physically correct) or with\n * `createStereoToBFormatNode` (the perceptual, approximate stereo-upmix path).\n */\n async createFoaDecoder(options: FoaDecoderOptions = {}, context?: BaseContext): Promise<FoaDecoder> {\n return FoaDecoder.create(this, options, context);\n }\n\n /**\n * Creates a bus-filter wrapper around {@link FoaDecoder}. Use this on a\n * dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only\n * filter that converts that bus to stereo binaural output. For custom manual\n * wiring, use {@link createFoaDecoder} instead.\n */\n createFoaDecoderEffect(options: FoaDecoderOptions = {}): FoaDecoderEffect {\n return new FoaDecoderEffect(this, options);\n }\n\n /**\n * Creates an ITU-R BS.1770-5 {@link LoudnessMeter} that TAPS the output of a\n * target node without altering the audible path. The meter reports momentary\n * (400 ms), short-term (3 s), and gated integrated loudness in LKFS/LUFS, plus\n * true-peak level in dBTP (Annex 2 4× oversampling).\n *\n * The tap is a BRANCH: the target's output is connected to the metering\n * worklet in ADDITION to its existing forward edge, and the worklet's output\n * is routed through an owned zero-gain sink to keep the branch silent and live.\n * By default the target is the\n * master bus output (`master.output`), measuring everything that reaches the\n * destination — the integrated-loudness target. Pass a {@link Bus} to meter\n * one bus's output, or any {@link AudioNode} to meter that node.\n *\n * Lazily registers the `loudness-meter` worklet module (no-op if already\n * loaded) on the target's context, then constructs the node and branch-taps.\n *\n * @param target Node or bus whose output to meter. Defaults to the master bus.\n * @returns A {@link LoudnessMeter} handle exposing the live readings.\n */\n async createLoudnessMeter(target: Bus | AudioNode = this.master): Promise<LoudnessMeter> {\n const sourceNode: AudioNode = target instanceof Bus ? target.output : target;\n // Load AND construct the worklet on the SOURCE node's OWN context, not always\n // `this.context`. A caller may pass an AudioNode (or Bus) living on a\n // different BaseContext; building the meter on this host's context would\n // cross-connect two contexts (illegal in Web Audio). The source node carries\n // its full context at runtime (native + standardized-audio-context both do);\n // the structural `AudioNode.context` type is narrowed, so widen it here.\n const targetContext = (sourceNode.context as BaseContext | undefined) ?? this.context;\n await this.loadAudioWorkletModule(\n WORKLETS.loudnessMeter.name,\n WORKLETS.loudnessMeter.url,\n undefined,\n targetContext,\n );\n const node = await this.createWorkletNode(\n WORKLETS.loudnessMeter.name,\n WORKLETS.loudnessMeter.url,\n undefined,\n { numberOfInputs: 1, numberOfOutputs: 1 },\n targetContext,\n );\n const silentSink = targetContext.createGain();\n return new LoudnessMeter(node, sourceNode, silentSink, targetContext.destination);\n }\n\n /**\n * Creates a new {@link Bus}. If `name` is provided, the bus is registered\n * so {@link getBus} can look it up by name. Anonymous buses (no name) are\n * not registered and cannot be retrieved by name.\n *\n * @throws if a named bus with the same name already exists, or if the\n * reserved name 'master' is used (the master bus is auto-created).\n */\n createBus(name?: string): Bus {\n if (name === \"master\") {\n throw new Error(\"The name 'master' is reserved — use cacophony.master to access the master bus.\");\n }\n if (name !== undefined && this._busRegistry.has(name)) {\n throw new Error(`A bus named '${name}' already exists.`);\n }\n const onDestroy = name !== undefined ? () => this._busRegistry.delete(name) : undefined;\n const bus = new Bus(this.context, name ?? null, undefined, onDestroy);\n if (name !== undefined) {\n this._busRegistry.set(name, bus);\n }\n // New buses default-route to master so audio flows out somewhere unless\n // the user wires it elsewhere. master is intentionally not destroyable.\n bus.connect(this.master);\n return bus;\n }\n\n /**\n * Retrieves a bus by name from the named-bus registry. Returns `undefined`\n * if no bus with that name exists. The special name `'master'` returns\n * the auto-created master bus.\n */\n getBus(name: string): Bus | undefined {\n if (name === \"master\") {\n return this.master;\n }\n return this._busRegistry.get(name);\n }\n\n /**\n * Returns the names of every registered named bus. Anonymous buses are\n * not included. The master bus's name (`'master'`) is included.\n */\n listBuses(): string[] {\n return [\"master\", ...this._busRegistry.keys()];\n }\n\n createSplitter(numChannels: number = 2): ChannelSplitterNode {\n if (!this.context.createChannelSplitter) {\n throw new Error(\"ChannelSplitterNode not supported\");\n }\n return this.context.createChannelSplitter(numChannels);\n }\n\n createMerger(numChannels: number = 2): ChannelMergerNode {\n if (!this.context.createChannelMerger) {\n throw new Error(\"ChannelMergerNode not supported\");\n }\n return this.context.createChannelMerger(numChannels);\n }\n\n /**\n * Creates a PannerNode with the specified options.\n * @param {PannerOptions} options - An object containing the options to use when creating the PannerNode.\n * @returns {PannerNode} A new PannerNode instance with the specified options.\n * @example\n * const panner = audio.createPanner({\n * positionX: 0,\n * positionY: 0,\n * positionZ: 0,\n * orientationX: 0,\n * orientationY: 0,\n * orientationZ: 0,\n * });\n */\n\n createPanner({\n coneInnerAngle,\n coneOuterAngle,\n coneOuterGain,\n distanceModel,\n maxDistance,\n channelCount,\n channelCountMode,\n channelInterpretation,\n panningModel,\n refDistance,\n rolloffFactor,\n positionX,\n positionY,\n positionZ,\n orientationX,\n orientationY,\n orientationZ,\n }: Partial<PannerOptions>): PannerNode {\n const panner = this.context.createPanner();\n panner.coneInnerAngle = coneInnerAngle ?? 360;\n panner.coneOuterAngle = coneOuterAngle ?? 360;\n panner.coneOuterGain = coneOuterGain ?? 0;\n panner.distanceModel = distanceModel ?? \"inverse\";\n panner.maxDistance = maxDistance ?? 10000;\n panner.channelCount = channelCount ?? 2;\n panner.channelCountMode = channelCountMode ?? \"clamped-max\";\n panner.channelInterpretation = channelInterpretation ?? \"speakers\";\n panner.panningModel = panningModel ?? \"HRTF\";\n panner.refDistance = refDistance ?? 1;\n panner.rolloffFactor = rolloffFactor ?? 1;\n panner.positionX.value = positionX ?? 0;\n panner.positionY.value = positionY ?? 0;\n panner.positionZ.value = positionZ ?? 0;\n panner.orientationX.value = orientationX ?? 0;\n panner.orientationY.value = orientationY ?? 0;\n panner.orientationZ.value = orientationZ ?? 0;\n return panner;\n }\n\n /**\n * Suspends the audio context.\n *\n * Resolves after the underlying AudioContext transition completes, then\n * emits the `suspend` event. If the context is already suspended (per this\n * instance's view), resolves immediately as a no-op. If the underlying\n * `suspend()` call rejects, the rejection is propagated and no event fires.\n */\n async pause(): Promise<void> {\n if (!this.context.suspend) {\n return;\n }\n if (this.suspendState === \"suspended\") {\n return;\n }\n await this.context.suspend();\n this.suspendState = \"suspended\";\n this.emit(\"suspend\", undefined);\n }\n\n /**\n * Resumes the audio context.\n * This method is required to resume the audio context on mobile devices.\n * On desktop, the audio context will automatically resume when a sound is played.\n *\n * Resolves after the underlying AudioContext transition completes, then\n * emits the `resume` event. If the underlying `resume()` call rejects, the\n * rejection is propagated and no event fires.\n */\n async resume(): Promise<void> {\n if (!this.context.resume) {\n return;\n }\n await this.context.resume();\n this.suspendState = \"running\";\n this.emit(\"resume\", undefined);\n }\n\n setGlobalVolume(volume: number) {\n if (this.muted) {\n this.prevVolume = volume;\n return;\n }\n if (this.globalGainNode.gain.value === volume) {\n return;\n }\n this.globalGainNode.gain.value = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n get volume(): number {\n return this.globalGainNode.gain.value;\n }\n\n set volume(volume: number) {\n if (this.muted) {\n this.prevVolume = volume;\n return;\n }\n this.setGlobalVolume(volume);\n }\n\n mute() {\n if (!this.muted) {\n this.prevVolume = this.globalGainNode.gain.value;\n this.isMuted = true;\n if (this.globalGainNode.gain.value !== 0) {\n this.globalGainNode.gain.value = 0;\n this.emit(\"volumeChange\", 0);\n }\n this.emit(\"mute\", undefined);\n }\n }\n\n unmute() {\n if (this.muted) {\n this.isMuted = false;\n this.setGlobalVolume(this.prevVolume);\n this.emit(\"unmute\", undefined);\n }\n }\n\n get muted(): boolean {\n return this.isMuted;\n }\n\n set muted(muted: boolean) {\n if (muted !== this.muted) {\n if (muted) {\n this.mute();\n } else {\n this.unmute();\n }\n }\n }\n\n async getMicrophoneStream(options: MicrophoneStreamOptions = {}): Promise<MicrophoneStream> {\n return MicrophoneStream.request(this.context, this.globalGainNode, options, this);\n }\n\n get listenerOrientation(): Orientation {\n return {\n forward: [...this.cachedListenerForward],\n up: [...this.cachedListenerUp],\n };\n }\n\n set listenerOrientation(orientation: Orientation) {\n const forward: Position = [orientation.forward[0], orientation.forward[1], orientation.forward[2]];\n const up: Position = [orientation.up[0], orientation.up[1], orientation.up[2]];\n writeListenerOrientation(this.listener, forward, up);\n this.cachedListenerForward = forward;\n this.cachedListenerUp = up;\n }\n\n get listenerUpOrientation(): Position {\n return [...this.cachedListenerUp];\n }\n\n set listenerUpOrientation(up: Position) {\n const nextUp: Position = [up[0], up[1], up[2]];\n writeListenerOrientation(this.listener, this.cachedListenerForward, nextUp);\n this.cachedListenerUp = nextUp;\n }\n\n get listenerForwardOrientation(): Position {\n return [...this.cachedListenerForward];\n }\n\n set listenerForwardOrientation(forward: Position) {\n const nextForward: Position = [forward[0], forward[1], forward[2]];\n writeListenerOrientation(this.listener, nextForward, this.cachedListenerUp);\n this.cachedListenerForward = nextForward;\n }\n\n get listenerPosition(): Position {\n return [...this.cachedListenerPosition];\n }\n\n set listenerPosition(position: Position) {\n const nextPosition: Position = [position[0], position[1], position[2]];\n writeListenerPosition(this.listener, nextPosition, this.context.currentTime);\n this.cachedListenerPosition = nextPosition;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cacophony-PShY6dPK.cjs","names":[],"sources":["../src/bundles/barberpole-bundle.js?url","../src/bundles/dattorro-reverb-bundle.js?url","../src/bundles/dynamics-bundle.js?url","../src/bundles/fdn-reverb-bundle.js?url","../src/bundles/frequency-shifter-bundle.js?url","../src/bundles/harmonizer-bundle.js?url","../src/bundles/loudness-meter-bundle.js?url","../src/bundles/modulated-delay-bundle.js?url","../src/bundles/pcm-stream-bundle.js?url","../src/bundles/phase-vocoder-bundle.js?url","../src/bundles/phaser-bundle.js?url","../src/bundles/spectral-freeze-bundle.js?url","../src/bundles/stereo-to-bformat-bundle.js?url","../src/bundles/stereo-widener-bundle.js?url","../src/bundles/tremolo-bundle.js?url","../src/bundles/waveshaper-bundle.js?url","../src/worklets.ts","../src/effects.ts","../src/effectChain.ts","../src/bus.ts","../src/cache.ts","../src/assets/sh_hrir_order_1.wav?url","../src/autoplayUnlock.ts","../src/eventEmitter.ts","../src/group.ts","../src/hlsAdapter.ts","../src/logger.ts","../src/filters.ts","../src/pannerMixin.ts","../src/volumeMixin.ts","../src/basePlayback.ts","../src/container.ts","../src/routableSource.ts","../src/mediaStream.ts","../src/meters/loudness-meter.ts","../src/microphone.ts","../src/pcmStream.ts","../src/processors/dynamics-core.ts","../src/processors/modulated-delay-core.ts","../src/processors/timestretch-core.ts","../src/playback.ts","../src/sound.ts","../src/oscillatorMixin.ts","../src/synthPlayback.ts","../src/synth.ts","../src/webCodecsStream.ts","../src/cacophony.ts"],"sourcesContent":["export default \"data:text/javascript;base64,dmFyIGJhcmJlcnBvbGUgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIEZJUi1IaWxiZXJ0IHNpbmdsZS1zaWRlYmFuZCBmcmVxdWVuY3kgc2hpZnRlci4KICAgICAqCiAgICAgKiBTY290dCBXYXJkbGUsICJBIEhpbGJlcnQtVHJhbnNmb3JtZXIgRnJlcXVlbmN5IFNoaWZ0ZXIgZm9yIEF1ZGlvIiwKICAgICAqIERBRngtOTgsIGVxLiA1YzoKICAgICAqCiAgICAgKiAgIHkodCkgPSB4KHQpIGNvcyh3X2MgdCkgLSBIe3godCl9IHNpbih3X2MgdCkKICAgICAqCiAgICAgKiBUaGUgcmVhbCBwYXRoIGlzIGRlbGF5ZWQgYnkgdGhlIEZJUiBncm91cCBkZWxheSBzbyBpdCByZW1haW5zIGluIHF1YWRyYXR1cmUKICAgICAqIHdpdGggdGhlIEhpbGJlcnQgcGF0aC4gQSBzaWduZWQgY2FycmllciBmcmVxdWVuY3kgY2hvb3NlcyB1cHBlci9sb3dlcgogICAgICogc2lkZWJhbmQgdHJhbnNsYXRpb24gd2l0aG91dCBjaGFuZ2luZyB0aGUgYWxnb3JpdGhtLgogICAgICovCiAgICBjb25zdCBERUZBVUxUX1RBUFMgPSAxMjc7CiAgICAvKiogV2luZG93ZWQgaWRlYWwgb2RkLXN5bW1ldHJpYyBIaWxiZXJ0LXRyYW5zZm9ybSBGSVIuICovCiAgICBmdW5jdGlvbiBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgaWYgKHRhcENvdW50IDwgNyB8fCB0YXBDb3VudCAlIDIgPT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIkhpbGJlcnQgRklSIHRhcCBjb3VudCBtdXN0IGJlIGFuIG9kZCBpbnRlZ2VyID49IDciKTsKICAgICAgICB9CiAgICAgICAgY29uc3QgY2VudGVyID0gKHRhcENvdW50IC0gMSkgLyAyOwogICAgICAgIGNvbnN0IGNvZWZmaWNpZW50cyA9IG5ldyBGbG9hdDY0QXJyYXkodGFwQ291bnQpOwogICAgICAgIGZvciAobGV0IG4gPSAwOyBuIDwgdGFwQ291bnQ7IG4rKykgewogICAgICAgICAgICBjb25zdCBtID0gbiAtIGNlbnRlcjsKICAgICAgICAgICAgaWYgKG0gIT09IDAgJiYgTWF0aC5hYnMobSkgJSAyID09PSAxKSB7CiAgICAgICAgICAgICAgICBjb25zdCB3aW5kb3cgPSAwLjQyIC0gMC41ICogTWF0aC5jb3MoKDIgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSkgKyAwLjA4ICogTWF0aC5jb3MoKDQgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSk7CiAgICAgICAgICAgICAgICBjb2VmZmljaWVudHNbbl0gPSAoMiAvIChNYXRoLlBJICogbSkpICogd2luZG93OwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiBjb2VmZmljaWVudHM7CiAgICB9CiAgICAvKiogU3RyZWFtaW5nIEZJUiBIaWxiZXJ0IHRyYW5zZm9ybWVyIHdpdGggYW4gYWxpZ25lZCBkaXJlY3QgdGFwLiAqLwogICAgY2xhc3MgSGlsYmVydFRyYW5zZm9ybWVyIHsKICAgICAgICBsYXRlbmN5U2FtcGxlczsKICAgICAgICBjb2VmZmljaWVudHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodGFwQ291bnQgPSBERUZBVUxUX1RBUFMpIHsKICAgICAgICAgICAgdGhpcy5jb2VmZmljaWVudHMgPSBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5U2FtcGxlcyA9ICh0YXBDb3VudCAtIDEpIC8gMjsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0YXBDb3VudCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQ2NEFycmF5KHNpemUpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBzaXplIC0gMTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlclt0aGlzLndyaXRlSW5kZXhdID0gaW5wdXQ7CiAgICAgICAgICAgIGxldCBxdWFkcmF0dXJlID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNvZWZmaWNpZW50cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgcXVhZHJhdHVyZSArPSB0aGlzLmNvZWZmaWNpZW50c1tpXSAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSBpKSAmIHRoaXMubWFza107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGlyZWN0ID0gdGhpcy5idWZmZXJbKHRoaXMud3JpdGVJbmRleCAtIHRoaXMubGF0ZW5jeVNhbXBsZXMpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4geyBkaXJlY3QsIHF1YWRyYXR1cmUgfTsKICAgICAgICB9CiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIC8qKiBGaXJzdC1vcmRlciBhbGxwYXNzIEEoeik9KGErel4tMSkvKDErYSB6Xi0xKSwgREFGeC0xNSBlcS4gMTQuICovCiAgICBjbGFzcyBGaXJzdE9yZGVyQWxscGFzcyB7CiAgICAgICAgeDEgPSAwOwogICAgICAgIHkxID0gMDsKICAgICAgICBwcm9jZXNzKGlucHV0LCBjb2VmZmljaWVudCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBjb2VmZmljaWVudCAqIGlucHV0ICsgdGhpcy54MSAtIGNvZWZmaWNpZW50ICogdGhpcy55MTsKICAgICAgICAgICAgdGhpcy54MSA9IGlucHV0OwogICAgICAgICAgICB0aGlzLnkxID0gb3V0cHV0OwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgIH0KICAgIC8qKgogICAgICogU1NCIGJhcmJlcnBvbGUgcGhhc2VyIGZyb20gRXNxdWVkYSwgVmFsaW1ha2kgJiBQYXJrZXIsIERBRngtMTUgRmlnLiAxMi4KICAgICAqIFRoZSBIaWxiZXJ0IHNoaWZ0ZXIgc3VwcGxpZXMgdGhlIG1vdmluZyBjb3B5OyBhIGNhc2NhZGVkIGFsbHBhc3Mgc3BlY3RyYWwKICAgICAqIGRlbGF5IHdhcnBzIHRoZSBub3RjaCBzcGFjaW5nIHRvd2FyZCB0aGUgU2hlcGFyZC1SaXNzZXQgb2N0YXZlIGRpc3RyaWJ1dGlvbi4KICAgICAqLwogICAgY2xhc3MgQmFyYmVycG9sZUNvcmUgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgaGlsYmVydCA9IG5ldyBIaWxiZXJ0VHJhbnNmb3JtZXIoKTsKICAgICAgICBhbGxwYXNzZXMgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiA2NCB9LCAoKSA9PiBuZXcgRmlyc3RPcmRlckFsbHBhc3MoKSk7CiAgICAgICAgcGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5zYW1wbGVSYXRlID0gc2FtcGxlUmF0ZTsKICAgICAgICB9CiAgICAgICAgcHJvY2Vzc1NhbXBsZShpbnB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IHBhaXIgPSB0aGlzLmhpbGJlcnQucHJvY2VzcyhpbnB1dCk7CiAgICAgICAgICAgIGxldCBzaGlmdGVkID0gcGFpci5kaXJlY3QgKiBNYXRoLmNvcyh0aGlzLnBoYXNlKSAtIHBhaXIucXVhZHJhdHVyZSAqIE1hdGguc2luKHRoaXMucGhhc2UpOwogICAgICAgICAgICBjb25zdCBzdGFnZXMgPSBNYXRoLm1heCgyLCBNYXRoLm1pbih0aGlzLmFsbHBhc3Nlcy5sZW5ndGgsIE1hdGgucm91bmQocGFyYW1zLnN0YWdlcykpKTsKICAgICAgICAgICAgY29uc3QgY29lZmZpY2llbnQgPSBNYXRoLm1heCgtMC45NSwgTWF0aC5taW4oMC45NSwgcGFyYW1zLmNvZWZmaWNpZW50KSk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhZ2VzOyBpKyspCiAgICAgICAgICAgICAgICBzaGlmdGVkID0gdGhpcy5hbGxwYXNzZXNbaV0ucHJvY2VzcyhzaGlmdGVkLCBjb2VmZmljaWVudCk7CiAgICAgICAgICAgIGNvbnN0IHJhdGUgPSBNYXRoLm1heCgtMiwgTWF0aC5taW4oMiwgcGFyYW1zLnJhdGUpKTsKICAgICAgICAgICAgdGhpcy5waGFzZSArPSAoMiAqIE1hdGguUEkgKiByYXRlKSAvIHRoaXMuc2FtcGxlUmF0ZTsKICAgICAgICAgICAgaWYgKHRoaXMucGhhc2UgPiBNYXRoLlBJIHx8IHRoaXMucGhhc2UgPCAtTWF0aC5QSSkKICAgICAgICAgICAgICAgIHRoaXMucGhhc2UgLT0gTWF0aC5yb3VuZCh0aGlzLnBoYXNlIC8gKDIgKiBNYXRoLlBJKSkgKiAyICogTWF0aC5QSTsKICAgICAgICAgICAgY29uc3QgYmFyYmVycG9sZSA9IDAuNSAqIChwYWlyLmRpcmVjdCArIHNoaWZ0ZWQpOwogICAgICAgICAgICBjb25zdCBtaXggPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBwYXJhbXMubWl4KSk7CiAgICAgICAgICAgIHJldHVybiBwYWlyLmRpcmVjdCAqICgxIC0gbWl4KSArIGJhcmJlcnBvbGUgKiBtaXg7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5wdXQubGVuZ3RoOyBpKyspCiAgICAgICAgICAgICAgICBvdXRwdXRbaV0gPSB0aGlzLnByb2Nlc3NTYW1wbGUoaW5wdXRbaV0sIHBhcmFtcyk7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIEJhcmJlcnBvbGVXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJyYXRlIiwgZGVmYXVsdFZhbHVlOiAwLjEsIG1pblZhbHVlOiAtMiwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICAgICAgeyBuYW1lOiAic3RhZ2VzIiwgZGVmYXVsdFZhbHVlOiAzMiwgbWluVmFsdWU6IDIsIG1heFZhbHVlOiA2NCwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJjb2VmZmljaWVudCIsIGRlZmF1bHRWYWx1ZTogLTAuNSwgbWluVmFsdWU6IC0wLjk1LCBtYXhWYWx1ZTogMC45NSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJtaXgiLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgIF07CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdID8/IFtdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdID8/IFtdOwogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICByYXRlOiBwYXJhbWV0ZXJzLnJhdGVbcGFyYW1ldGVycy5yYXRlLmxlbmd0aCAtIDFdLAogICAgICAgICAgICAgICAgc3RhZ2VzOiBwYXJhbWV0ZXJzLnN0YWdlc1twYXJhbWV0ZXJzLnN0YWdlcy5sZW5ndGggLSAxXSwKICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50OiBwYXJhbWV0ZXJzLmNvZWZmaWNpZW50W3BhcmFtZXRlcnMuY29lZmZpY2llbnQubGVuZ3RoIC0gMV0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4W3BhcmFtZXRlcnMubWl4Lmxlbmd0aCAtIDFdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dC5sZW5ndGg7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgY29uc3Qgc291cmNlID0gaW5wdXRbY2hhbm5lbF0gPz8gaW5wdXRbMF07CiAgICAgICAgICAgICAgICBpZiAoIXNvdXJjZSkKICAgICAgICAgICAgICAgICAgICBvdXRwdXRbY2hhbm5lbF0uZmlsbCgwKTsKICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgICAgICAodGhpcy5jb3Jlc1tjaGFubmVsXSA/Pz0gbmV3IEJhcmJlcnBvbGVDb3JlKHNhbXBsZVJhdGUpKS5wcm9jZXNzKHNvdXJjZSwgb3V0cHV0W2NoYW5uZWxdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJiYXJiZXJwb2xlIiwgQmFyYmVycG9sZVdvcmtsZXRQcm9jZXNzb3IpOwoKICAgIGV4cG9ydHMuQmFyYmVycG9sZVdvcmtsZXRQcm9jZXNzb3IgPSBCYXJiZXJwb2xlV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9YmFyYmVycG9sZS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIGRhdHRvcnJvUmV2ZXJiID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgIERhdHRvcnJvIFJldmVyYiBBdWRpb1dvcmtsZXQgSW1wbGVtZW50YXRpb24KICAgIE9yaWdpbmFsIEphdmFTY3JpcHQgaW1wbGVtZW50YXRpb24gYnkgS2jDtGkgTmd1eeG7hW4gKGtob2luKQogICAgaHR0cHM6Ly9naXRodWIuY29tL2tob2luL0RhdHRvcnJvUmV2ZXJiTm9kZQoKICAgIEJhc2VkIG9uIEpvbiBEYXR0b3JybydzIDE5OTcgQUVTIHBhcGVyOgogICAgIkVmZmVjdCBEZXNpZ24gUGFydCAxOiBSZXZlcmJlcmF0b3IgYW5kIE90aGVyIEZpbHRlcnMiCgogICAgSW4ganVyaXNkaWN0aW9ucyB0aGF0IHJlY29nbml6ZSBjb3B5cmlnaHQgbGF3cywgdGhpcyBzb2Z0d2FyZSBpcyB0bwogICAgYmUgcmVsZWFzZWQgaW50byB0aGUgcHVibGljIGRvbWFpbi4KCiAgICBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORC4KICAgIFRIRSBBVVRIT1IoUykgU0hBTEwgTk9UIEJFIExJQUJMRSBGT1IgQU5ZVEhJTkcsIEFSSVNJTkcgRlJPTSwgT1IgSU4KICAgIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIERJU1RSSUJVVElPTiBPRiBUSEUgU09GVFdBUkUuCiAgICAqLwogICAgLyoqCiAgICAgKiBEYXR0b3JybyBQbGF0ZSBSZXZlcmIgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yCiAgICAgKgogICAgICogQmFzZWQgb24gSm9uIERhdHRvcnJvJ3MgMTk5NyBBRVMgcGFwZXIgIkVmZmVjdCBEZXNpZ24gUGFydCAxOiBSZXZlcmJlcmF0b3IgYW5kIE90aGVyIEZpbHRlcnMiCiAgICAgKiBJbXBsZW1lbnRzIGEgaGlnaC1xdWFsaXR5IHBsYXRlIHJldmVyYiBhbGdvcml0aG0gd2l0aCBtb2R1bGF0ZWQgZGVsYXkgbGluZXMuCiAgICAgKgogICAgICogQHNlZSBodHRwczovL2Njcm1hLnN0YW5mb3JkLmVkdS9+ZGF0dG9ycm8vRWZmZWN0RGVzaWduUGFydDEucGRmCiAgICAgKi8KICAgIGNsYXNzIERhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBfRGVsYXlzID0gW107CiAgICAgICAgX3BETGVuZ3RoOwogICAgICAgIF9wcmVEZWxheTsKICAgICAgICBfcERXcml0ZSA9IDA7CiAgICAgICAgX2xwMSA9IDAuMDsKICAgICAgICBfbHAyID0gMC4wOwogICAgICAgIF9scDMgPSAwLjA7CiAgICAgICAgX2V4Y1BoYXNlID0gMC4wOwogICAgICAgIF90YXBzOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbInByZURlbGF5IiwgMCwgMCwgc2FtcGxlUmF0ZSAtIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiYmFuZHdpZHRoIiwgMC45OTk5LCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImlucHV0RGlmZnVzaW9uMSIsIDAuNzUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiaW5wdXREaWZmdXNpb24yIiwgMC42MjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVjYXkiLCAwLjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVjYXlEaWZmdXNpb24xIiwgMC43LCAwLCAwLjk5OTk5OSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkZWNheURpZmZ1c2lvbjIiLCAwLjUsIDAsIDAuOTk5OTk5LCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImRhbXBpbmciLCAwLjAwNSwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJleGN1cnNpb25SYXRlIiwgMC41LCAwLCAyLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImV4Y3Vyc2lvbkRlcHRoIiwgMC43LCAwLCAyLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbIndldCIsIDAuMywgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkcnkiLCAwLjYsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKICAgICAgICAgICAgc3VwZXIob3B0aW9ucyk7CiAgICAgICAgICAgIC8vIFByZS1kZWxheSBpcyBhbHdheXMgb25lLXNlY29uZCBsb25nLCByb3VuZGVkIHRvIHRoZSBuZWFyZXN0IDEyOC1jaHVuawogICAgICAgICAgICB0aGlzLl9wRExlbmd0aCA9IHNhbXBsZVJhdGUgKyAoMTI4IC0gKHNhbXBsZVJhdGUgJSAxMjgpKTsKICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXkgPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuX3BETGVuZ3RoKTsKICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZSBkZWxheSBsaW5lcyB3aXRoIHNwZWNpZmllZCBsZW5ndGhzIChpbiBzZWNvbmRzKQogICAgICAgICAgICBbCiAgICAgICAgICAgICAgICAwLjAwNDc3MTM0NSwgMC4wMDM1OTUzMDksIDAuMDEyNzM0Nzg3LCAwLjAwOTMwNzQ4MywgMC4wMjI1Nzk4ODYsIDAuMTQ5NjI1MzQ5LCAwLjA2MDQ4MTgzOSwgMC4xMjQ5OTU4LCAwLjAzMDUwOTcyNywKICAgICAgICAgICAgICAgIDAuMTQxNjk1NTA4LCAwLjA4OTI0NDMxMywgMC4xMDYyODAwMzEsCiAgICAgICAgICAgIF0uZm9yRWFjaCgobGVuZ3RoKSA9PiB0aGlzLm1ha2VEZWxheShsZW5ndGgpKTsKICAgICAgICAgICAgLy8gSW5pdGlhbGl6ZSB0YXAgcG9zaXRpb25zIChpbiBzZWNvbmRzKSBmb3Igc3RlcmVvIG91dHB1dAogICAgICAgICAgICB0aGlzLl90YXBzID0gSW50MTZBcnJheS5mcm9tKFsKICAgICAgICAgICAgICAgIDAuMDA4OTM3ODcyLCAwLjA5OTkyOTQzOCwgMC4wNjQyNzg3NTQsIDAuMDY3MDY3NjM5LCAwLjA2Njg2NjAzMywgMC4wMDYyODMzOTEsIDAuMDM1ODE4Njg5LCAwLjAxMTg2MTE2MSwKICAgICAgICAgICAgICAgIDAuMTIxODcwOTA1LCAwLjA0MTI2MjA1NCwgMC4wODk4MTU1MywgMC4wNzA5MzE3NTYsIDAuMDExMjU2MzQyLCAwLjAwNDA2NTcyNCwKICAgICAgICAgICAgXSwgKGxlbmd0aCkgPT4gTWF0aC5yb3VuZChsZW5ndGggKiBzYW1wbGVSYXRlKSk7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIENyZWF0ZXMgYSBkZWxheSBsaW5lIHdpdGggdGhlIHNwZWNpZmllZCBsZW5ndGgKICAgICAgICAgKiBVc2VzIHBvd2VyLW9mLTIgc2l6aW5nIGZvciBlZmZpY2llbnQgd3JhcHBpbmcgd2l0aCBiaXR3aXNlIEFORAogICAgICAgICAqLwogICAgICAgIG1ha2VEZWxheShsZW5ndGgpIHsKICAgICAgICAgICAgY29uc3QgbGVuID0gTWF0aC5yb3VuZChsZW5ndGggKiBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgY29uc3QgbmV4dFBvdzIgPSAyICoqIE1hdGguY2VpbChNYXRoLmxvZzIobGVuKSk7CiAgICAgICAgICAgIHRoaXMuX0RlbGF5cy5wdXNoKFsKICAgICAgICAgICAgICAgIG5ldyBGbG9hdDMyQXJyYXkobmV4dFBvdzIpLAogICAgICAgICAgICAgICAgbGVuIC0gMSwgLy8gd3JpdGUgaW5kZXgKICAgICAgICAgICAgICAgIDAsIC8vIHJlYWQgaW5kZXgKICAgICAgICAgICAgICAgIG5leHRQb3cyIC0gMSwgLy8gbWFzayBmb3Igd3JhcHBpbmcKICAgICAgICAgICAgXSk7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFdyaXRlcyBhIHNhbXBsZSB0byBhIGRlbGF5IGxpbmUgYW5kIHJldHVybnMgdGhlIHdyaXR0ZW4gdmFsdWUKICAgICAgICAgKi8KICAgICAgICB3cml0ZURlbGF5KGluZGV4LCBkYXRhKSB7CiAgICAgICAgICAgIHJldHVybiAodGhpcy5fRGVsYXlzW2luZGV4XVswXVt0aGlzLl9EZWxheXNbaW5kZXhdWzFdXSA9IGRhdGEpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBSZWFkcyB0aGUgY3VycmVudCBzYW1wbGUgZnJvbSBhIGRlbGF5IGxpbmUKICAgICAgICAgKi8KICAgICAgICByZWFkRGVsYXkoaW5kZXgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuX0RlbGF5c1tpbmRleF1bMF1bdGhpcy5fRGVsYXlzW2luZGV4XVsyXV07CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFJlYWRzIGEgc2FtcGxlIGF0IGEgc3BlY2lmaWMgb2Zmc2V0IGZyb20gdGhlIGN1cnJlbnQgcmVhZCBwb3NpdGlvbgogICAgICAgICAqLwogICAgICAgIHJlYWREZWxheUF0KGluZGV4LCBvZmZzZXQpIHsKICAgICAgICAgICAgY29uc3QgZCA9IHRoaXMuX0RlbGF5c1tpbmRleF07CiAgICAgICAgICAgIHJldHVybiBkWzBdWyhkWzJdICsgb2Zmc2V0KSAmIGRbM11dOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBSZWFkcyBhIHNhbXBsZSB3aXRoIGN1YmljIGludGVycG9sYXRpb24gYXQgYSBmcmFjdGlvbmFsIG9mZnNldAogICAgICAgICAqIFVzZXMgT2xsaSBOaWVtaXRhbG8ncyBvcHRpbWFsIGN1YmljIGludGVycG9sYXRpb24KICAgICAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lm11c2ljZHNwLm9yZy9lbi9sYXRlc3QvT3RoZXIvNDktY3ViaWMtaW50ZXJwb2xsYXRpb24uaHRtbAogICAgICAgICAqLwogICAgICAgIHJlYWREZWxheUNBdChpbmRleCwgb2Zmc2V0KSB7CiAgICAgICAgICAgIGNvbnN0IGQgPSB0aGlzLl9EZWxheXNbaW5kZXhdOwogICAgICAgICAgICBjb25zdCBmcmFjID0gb2Zmc2V0IC0gfn5vZmZzZXQ7CiAgICAgICAgICAgIGxldCBpbnQgPSB+fm9mZnNldCArIGRbMl0gLSAxOwogICAgICAgICAgICBjb25zdCBtYXNrID0gZFszXTsKICAgICAgICAgICAgY29uc3QgeDAgPSBkWzBdW2ludCsrICYgbWFza107CiAgICAgICAgICAgIGNvbnN0IHgxID0gZFswXVtpbnQrKyAmIG1hc2tdOwogICAgICAgICAgICBjb25zdCB4MiA9IGRbMF1baW50KysgJiBtYXNrXTsKICAgICAgICAgICAgY29uc3QgeDMgPSBkWzBdW2ludCAmIG1hc2tdOwogICAgICAgICAgICBjb25zdCBhID0gKDMgKiAoeDEgLSB4MikgLSB4MCArIHgzKSAvIDI7CiAgICAgICAgICAgIGNvbnN0IGIgPSAyICogeDIgKyB4MCAtICg1ICogeDEgKyB4MykgLyAyOwogICAgICAgICAgICBjb25zdCBjID0gKHgyIC0geDApIC8gMjsKICAgICAgICAgICAgcmV0dXJuICgoYSAqIGZyYWMgKyBiKSAqIGZyYWMgKyBjKSAqIGZyYWMgKyB4MTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhdWRpbyBzYW1wbGVzCiAgICAgICAgICogRmlyc3QgaW5wdXQgd2lsbCBiZSBkb3dubWl4ZWQgdG8gbW9ubyBpZiBudW1iZXIgb2YgY2hhbm5lbHMgaXMgbm90IDIKICAgICAgICAgKiBPdXRwdXRzIHN0ZXJlbwogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IHBkID0gfn5wYXJhbWV0ZXJzLnByZURlbGF5WzBdOwogICAgICAgICAgICBjb25zdCBidyA9IHBhcmFtZXRlcnMuYmFuZHdpZHRoWzBdOwogICAgICAgICAgICBjb25zdCBmaSA9IHBhcmFtZXRlcnMuaW5wdXREaWZmdXNpb24xWzBdOwogICAgICAgICAgICBjb25zdCBzaSA9IHBhcmFtZXRlcnMuaW5wdXREaWZmdXNpb24yWzBdOwogICAgICAgICAgICBjb25zdCBkYyA9IHBhcmFtZXRlcnMuZGVjYXlbMF07CiAgICAgICAgICAgIGNvbnN0IGZ0ID0gcGFyYW1ldGVycy5kZWNheURpZmZ1c2lvbjFbMF07CiAgICAgICAgICAgIGNvbnN0IHN0ID0gcGFyYW1ldGVycy5kZWNheURpZmZ1c2lvbjJbMF07CiAgICAgICAgICAgIGNvbnN0IGRwID0gMSAtIHBhcmFtZXRlcnMuZGFtcGluZ1swXTsKICAgICAgICAgICAgY29uc3QgZXggPSBwYXJhbWV0ZXJzLmV4Y3Vyc2lvblJhdGVbMF0gLyBzYW1wbGVSYXRlOwogICAgICAgICAgICBjb25zdCBlZCA9IChwYXJhbWV0ZXJzLmV4Y3Vyc2lvbkRlcHRoWzBdICogc2FtcGxlUmF0ZSkgLyAxMDAwOwogICAgICAgICAgICBjb25zdCB3ZSA9IHBhcmFtZXRlcnMud2V0WzBdICogMC42OyAvLyBsbyAmIHJvIGJvdGggbXVsdC4gYnkgMC42IGFueXdheXMKICAgICAgICAgICAgY29uc3QgZHIgPSBwYXJhbWV0ZXJzLmRyeVswXTsKICAgICAgICAgICAgLy8gV3JpdGUgdG8gcHJlZGVsYXkgYW5kIGRyeSBvdXRwdXQKICAgICAgICAgICAgaWYgKGlucHV0c1swXS5sZW5ndGggPT09IDIpIHsKICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAxMjc7IGkgPj0gMDsgaS0tKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXlbdGhpcy5fcERXcml0ZSArIGldID0gKGlucHV0c1swXVswXVtpXSArIGlucHV0c1swXVsxXVtpXSkgKiAwLjU7CiAgICAgICAgICAgICAgICAgICAgb3V0cHV0c1swXVswXVtpXSA9IGlucHV0c1swXVswXVtpXSAqIGRyOwogICAgICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMV1baV0gPSBpbnB1dHNbMF1bMV1baV0gKiBkcjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIGlmIChpbnB1dHNbMF0ubGVuZ3RoID4gMCkgewogICAgICAgICAgICAgICAgdGhpcy5fcHJlRGVsYXkuc2V0KGlucHV0c1swXVswXSwgdGhpcy5fcERXcml0ZSk7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMTI3OyBpID49IDA7IGktLSkgewogICAgICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMF1baV0gPSBvdXRwdXRzWzBdWzFdW2ldID0gaW5wdXRzWzBdWzBdW2ldICogZHI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICB0aGlzLl9wcmVEZWxheS5zZXQobmV3IEZsb2F0MzJBcnJheSgxMjgpLCB0aGlzLl9wRFdyaXRlKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBsZXQgaSA9IDA7CiAgICAgICAgICAgIHdoaWxlIChpIDwgMTI4KSB7CiAgICAgICAgICAgICAgICBsZXQgbG8gPSAwLjA7CiAgICAgICAgICAgICAgICBsZXQgcm8gPSAwLjA7CiAgICAgICAgICAgICAgICAvLyBJbnB1dCBsb3ctcGFzcyBmaWx0ZXIgKGJhbmR3aWR0aCkKICAgICAgICAgICAgICAgIHRoaXMuX2xwMSArPSBidyAqICh0aGlzLl9wcmVEZWxheVsodGhpcy5fcERMZW5ndGggKyB0aGlzLl9wRFdyaXRlIC0gcGQgKyBpKSAlIHRoaXMuX3BETGVuZ3RoXSAtIHRoaXMuX2xwMSk7CiAgICAgICAgICAgICAgICAvLyBQcmUtdGFuayBkaWZmdXNpb24gKDQgYWxsLXBhc3MgZmlsdGVycykKICAgICAgICAgICAgICAgIGxldCBwcmUgPSB0aGlzLndyaXRlRGVsYXkoMCwgdGhpcy5fbHAxIC0gZmkgKiB0aGlzLnJlYWREZWxheSgwKSk7CiAgICAgICAgICAgICAgICBwcmUgPSB0aGlzLndyaXRlRGVsYXkoMSwgZmkgKiAocHJlIC0gdGhpcy5yZWFkRGVsYXkoMSkpICsgdGhpcy5yZWFkRGVsYXkoMCkpOwogICAgICAgICAgICAgICAgcHJlID0gdGhpcy53cml0ZURlbGF5KDIsIGZpICogcHJlICsgdGhpcy5yZWFkRGVsYXkoMSkgLSBzaSAqIHRoaXMucmVhZERlbGF5KDIpKTsKICAgICAgICAgICAgICAgIHByZSA9IHRoaXMud3JpdGVEZWxheSgzLCBzaSAqIChwcmUgLSB0aGlzLnJlYWREZWxheSgzKSkgKyB0aGlzLnJlYWREZWxheSgyKSk7CiAgICAgICAgICAgICAgICBjb25zdCBzcGxpdCA9IHNpICogcHJlICsgdGhpcy5yZWFkRGVsYXkoMyk7CiAgICAgICAgICAgICAgICAvLyBNb2R1bGF0ZWQgZXhjdXJzaW9ucyBmb3IgY2hvcnVzIGVmZmVjdAogICAgICAgICAgICAgICAgY29uc3QgZXhjID0gZWQgKiAoMSArIE1hdGguY29zKHRoaXMuX2V4Y1BoYXNlICogNi4yOCkpOwogICAgICAgICAgICAgICAgY29uc3QgZXhjMiA9IGVkICogKDEgKyBNYXRoLnNpbih0aGlzLl9leGNQaGFzZSAqIDYuMjg0NykpOwogICAgICAgICAgICAgICAgLy8gTGVmdCBsb29wICh0YW5rIGRpZmZ1c2UgMSAtPiBsb25nIGRlbGF5IDEgLT4gZGFtcCAxIC0+IHRhbmsgZGlmZnVzZSAyIC0+IGxvbmcgZGVsYXkgMikKICAgICAgICAgICAgICAgIGxldCB0ZW1wID0gdGhpcy53cml0ZURlbGF5KDQsIHNwbGl0ICsgZGMgKiB0aGlzLnJlYWREZWxheSgxMSkgKyBmdCAqIHRoaXMucmVhZERlbGF5Q0F0KDQsIGV4YykpOwogICAgICAgICAgICAgICAgdGhpcy53cml0ZURlbGF5KDUsIHRoaXMucmVhZERlbGF5Q0F0KDQsIGV4YykgLSBmdCAqIHRlbXApOwogICAgICAgICAgICAgICAgdGhpcy5fbHAyICs9IGRwICogKHRoaXMucmVhZERlbGF5KDUpIC0gdGhpcy5fbHAyKTsKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoNiwgZGMgKiB0aGlzLl9scDIgLSBzdCAqIHRoaXMucmVhZERlbGF5KDYpKTsKICAgICAgICAgICAgICAgIHRoaXMud3JpdGVEZWxheSg3LCB0aGlzLnJlYWREZWxheSg2KSArIHN0ICogdGVtcCk7CiAgICAgICAgICAgICAgICAvLyBSaWdodCBsb29wICh0YW5rIGRpZmZ1c2UgMyAtPiBsb25nIGRlbGF5IDMgLT4gZGFtcCAyIC0+IHRhbmsgZGlmZnVzZSA0IC0+IGxvbmcgZGVsYXkgNCkKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoOCwgc3BsaXQgKyBkYyAqIHRoaXMucmVhZERlbGF5KDcpICsgZnQgKiB0aGlzLnJlYWREZWxheUNBdCg4LCBleGMyKSk7CiAgICAgICAgICAgICAgICB0aGlzLndyaXRlRGVsYXkoOSwgdGhpcy5yZWFkRGVsYXlDQXQoOCwgZXhjMikgLSBmdCAqIHRlbXApOwogICAgICAgICAgICAgICAgdGhpcy5fbHAzICs9IGRwICogKHRoaXMucmVhZERlbGF5KDkpIC0gdGhpcy5fbHAzKTsKICAgICAgICAgICAgICAgIHRlbXAgPSB0aGlzLndyaXRlRGVsYXkoMTAsIGRjICogdGhpcy5fbHAzIC0gc3QgKiB0aGlzLnJlYWREZWxheSgxMCkpOwogICAgICAgICAgICAgICAgdGhpcy53cml0ZURlbGF5KDExLCB0aGlzLnJlYWREZWxheSgxMCkgKyBzdCAqIHRlbXApOwogICAgICAgICAgICAgICAgLy8gTGVmdCBvdXRwdXQ6IHN1bSBvZiB0YXBzIGZyb20gYm90aCBsb29wcwogICAgICAgICAgICAgICAgbG8gPQogICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoOSwgdGhpcy5fdGFwc1swXSkgKwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDksIHRoaXMuX3RhcHNbMV0pIC0KICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCgxMCwgdGhpcy5fdGFwc1syXSkgKwogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDExLCB0aGlzLl90YXBzWzNdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoNSwgdGhpcy5fdGFwc1s0XSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDYsIHRoaXMuX3RhcHNbNV0pIC0KICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg3LCB0aGlzLl90YXBzWzZdKTsKICAgICAgICAgICAgICAgIC8vIFJpZ2h0IG91dHB1dDogc3VtIG9mIHRhcHMgZnJvbSBib3RoIGxvb3BzCiAgICAgICAgICAgICAgICBybyA9CiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg1LCB0aGlzLl90YXBzWzddKSArCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoNSwgdGhpcy5fdGFwc1s4XSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDYsIHRoaXMuX3RhcHNbOV0pICsKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWFkRGVsYXlBdCg3LCB0aGlzLl90YXBzWzEwXSkgLQogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlYWREZWxheUF0KDksIHRoaXMuX3RhcHNbMTFdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoMTAsIHRoaXMuX3RhcHNbMTJdKSAtCiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucmVhZERlbGF5QXQoMTEsIHRoaXMuX3RhcHNbMTNdKTsKICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMF1baV0gKz0gbG8gKiB3ZTsKICAgICAgICAgICAgICAgIG91dHB1dHNbMF1bMV1baV0gKz0gcm8gKiB3ZTsKICAgICAgICAgICAgICAgIHRoaXMuX2V4Y1BoYXNlICs9IGV4OwogICAgICAgICAgICAgICAgaSsrOwogICAgICAgICAgICAgICAgLy8gQWR2YW5jZSBhbGwgZGVsYXkgbGluZSBpbmRpY2VzCiAgICAgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuX0RlbGF5cy5sZW5ndGg7IGorKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGQgPSB0aGlzLl9EZWxheXNbal07CiAgICAgICAgICAgICAgICAgICAgZFsxXSA9IChkWzFdICsgMSkgJiBkWzNdOwogICAgICAgICAgICAgICAgICAgIGRbMl0gPSAoZFsyXSArIDEpICYgZFszXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBVcGRhdGUgcHJlRGVsYXkgaW5kZXgKICAgICAgICAgICAgdGhpcy5fcERXcml0ZSA9ICh0aGlzLl9wRFdyaXRlICsgMTI4KSAlIHRoaXMuX3BETGVuZ3RoOwogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3NvcigiZGF0dG9ycm8tcmV2ZXJiIiwgRGF0dG9ycm9SZXZlcmJQcm9jZXNzb3IpOwogICAgY29uc29sZS5sb2coIkRhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yIHJlZ2lzdGVyZWQiKTsKCiAgICBleHBvcnRzLkRhdHRvcnJvUmV2ZXJiUHJvY2Vzc29yID0gRGF0dG9ycm9SZXZlcmJQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWRhdHRvcnJvLXJldmVyYi1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIGR5bmFtaWNzID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgICAqIER5bmFtaWNzIHByb2Nlc3NvciBjb3JlIOKAlCBjb250ZXh0LWZyZWUgRFNQIG1hdGggZm9yIGEgZmVlZC1mb3J3YXJkIGR5bmFtaWMKICAgICAqIHJhbmdlIGNvbXByZXNzb3IgLyBsaW1pdGVyIC8gZXhwYW5kZXIgLyBnYXRlLgogICAgICoKICAgICAqIEltcGxlbWVudHMgdGhlIGNhbm9uaWNhbCBkZXNpZ24gZnJvbToKICAgICAqICAgRC4gR2lhbm5vdWxpcywgTS4gTWFzc2JlcmcsIEouIEQuIFJlaXNzLAogICAgICogICAiRGlnaXRhbCBEeW5hbWljIFJhbmdlIENvbXByZXNzb3IgRGVzaWduIOKAlCBBIFR1dG9yaWFsIGFuZCBBbmFseXNpcyIsCiAgICAgKiAgIEouIEF1ZGlvIEVuZy4gU29jLiwgVm9sLiA2MCwgTm8uIDYsIHBwLiAzOTktNDA4ICgyMDEyKS4KICAgICAqCiAgICAgKiBUb3BvbG9neSAodGhlIHBhcGVyJ3MgcmVjb21tZW5kZWQgYmVzdCBwcmFjdGljZSwgcC4zOTkgYWJzdHJhY3QgLyBwcC40MDQtNDA1KToKICAgICAqICAgLSBmZWVkLWZvcndhcmQgKHNpZGUtY2hhaW4gZHJpdmVuIGZyb20gdGhlIElOUFVUKSwKICAgICAqICAgLSBnYWluIGNvbXB1dGVyID0gc3RhdGljIGRCLT5kQiBjdXJ2ZSAoZXFzIDItNCksCiAgICAgKiAgIC0gbGV2ZWwgZGV0ZWN0b3IgcGxhY2VkIGluIHRoZSBMT0cgZG9tYWluIEFGVEVSIHRoZSBnYWluIGNvbXB1dGVyCiAgICAgKiAgICAgKGVxIDIzKTogc21vb3RoIHRoZSBnYWluLXJlZHVjdGlvbiBjb250cm9sIHNpZ25hbCwgbm90IHRoZSByYXcgbGV2ZWwsCiAgICAgKiAgIC0gc21vb3RoIGJyYW5jaGluZyBwZWFrIGRldGVjdG9yIGZvciB0aGUgYmFsbGlzdGljcyAoZXEgMTYpLgogICAgICoKICAgICAqIEEgU0lOR0xFIGdhaW4gY29tcHV0ZXIsIHBhcmFtZXRlcml6ZWQgYnkgdGhyZXNob2xkIFQsIHJhdGlvIFIgYW5kIGtuZWUgVywKICAgICAqIHNlcnZlcyBhbGwgZm91ciBiZWhhdmlvdXJzIChHaWFubm91bGlzIDIwMTIsIHAuNDAzOyBzZWUgbm90ZXMubWQKICAgICAqICJFeHBhbmRlciAvIEdhdGUgZnJvbSB0aGUgU0FNRSBtYWNoaW5lcnkiKToKICAgICAqICAgLSBSID4gMSAgICAgICAgICAgIC0+IGNvbXByZXNzb3IgKHNsb3BlIDEvUiA8IDEgYWJvdmUgVCksCiAgICAgKiAgIC0gUiAtPiBpbmZpbml0eSAgICAgLT4gbGltaXRlciAoc2xvcGUgLT4gMCBhYm92ZSBULCBlcXMgMTgtMTkpLAogICAgICogICAtIFIgPCAxICAgICAgICAgICAgLT4gZG93bndhcmQgZXhwYW5kZXIgKHNsb3BlID4gMSBCRUxPVyBUKSwKICAgICAqICAgLSBleHRyZW1lIFIgPCAxICAgIC0+IGdhdGUgKHZlcnkgc3RlZXAgZG93bndhcmQgZXhwYW5zaW9uIGJlbG93IFQpLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChvcGVyYXRlcyBvbiBwbGFpbiBudW1iZXJzIGFuZAogICAgICogRmxvYXQzMkFycmF5KS4gSXQgaGFzIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMgc28gaXQgY2FuIGJlCiAgICAgKiB1bml0LXRlc3RlZCBkaXJlY3RseSAodGhlIHdvcmtsZXQgc2hlbGwgaW4gZHluYW1pY3MudHMgZGVsZWdhdGVzIHRvIGl0KS4KICAgICAqLwogICAgLyoqCiAgICAgKiBEZWZhdWx0IEF1ZGlvUGFyYW0gdmFsdWVzIGZvciB0aGUgZHluYW1pY3Mgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncyBvd24KICAgICAqIHVuaXRzLiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AKICAgICAqIChkeW5hbWljcy50cykgYnVpbGRzIGl0cyBkZXNjcmlwdG9ycyBmcm9tIHRoaXMgdGFibGUsIGFuZCB0ZXN0cyB0aGF0IG5lZWQgdGhlCiAgICAgKiBzaGlwcGVkIGRlZmF1bHRzIGltcG9ydCBpdCBoZXJlIHJhdGhlciB0aGFuIHJlLXR5cGluZyBsaXRlcmFscy4gTGl2aW5nIGluIHRoZQogICAgICogd29ya2xldC1nbG9iYWwtZnJlZSBjb3JlIGtlZXBzIGl0IGltcG9ydGFibGUgZnJvbSBwbGFpbiBOb2RlIHRlc3QgcnVubmVycy4KICAgICAqCiAgICAgKiBEZWZhdWx0czogYSBnZW50bGUsIGF1ZGlibGUtYnV0LXNhZmUgY29tcHJlc3Nvci4gUmF0aW9zIGNvdmVyIGNvbXByZXNzb3IKICAgICAqIChyYXRpbyA+PSAxKSwgbGltaXRlciAocmF0aW8gLT4gbGFyZ2UpIGFuZCBkb3dud2FyZCBleHBhbmRlci9nYXRlIChyYXRpbyA8IDEpCiAgICAgKiBmcm9tIG9uZSBwYXJhbWV0ZXIgc2V0LgogICAgICovCiAgICBjb25zdCBEWU5BTUlDU19ERUZBVUxUUyA9IHsKICAgICAgICB0aHJlc2hvbGQ6IC0yNCwKICAgICAgICByYXRpbzogNCwKICAgICAgICBrbmVlOiA2LAogICAgICAgIGF0dGFjazogMC4wMDMsCiAgICAgICAgcmVsZWFzZTogMC4yNSwKICAgICAgICBtYWtldXA6IDAsCiAgICB9OwogICAgLyoqCiAgICAgKiBTbWFsbGVzdCBsaW5lYXIgbGV2ZWwgd2UgYXJlIHdpbGxpbmcgdG8gdGFrZSBhIGxvZ2FyaXRobSBvZi4gQmVsb3cgdGhpcyB0aGUKICAgICAqIGlucHV0IGlzIHRyZWF0ZWQgYXMgc2lsZW5jZSAoZ2Fpbi1yZWR1Y3Rpb24gY29udHJvbCA9IDApLCBhdm9pZGluZyAtSW5maW5pdHkKICAgICAqIGRCIGZyb20gbG9nMTAoMCkuCiAgICAgKi8KICAgIGNvbnN0IExFVkVMX0ZMT09SID0gMWUtMTI7CiAgICAvKioKICAgICAqIFN0YXRpYyBnYWluIGNvbXB1dGVyIOKAlCBtYXBzIGFuIGlucHV0IGxldmVsIHhfRyAoZEIpIHRvIGFuIG91dHB1dCBsZXZlbAogICAgICogeV9HIChkQikgZ2l2ZW4gdGhyZXNob2xkIFQsIHJhdGlvIFIgYW5kIGtuZWUgd2lkdGggVy4KICAgICAqCiAgICAgKiBIYXJkIGtuZWUgKFcgPSAwKSwgR2lhbm5vdWxpcyAyMDEyIGVxLjM6CiAgICAgKiAgIHlfRyA9IHhfRyAgICAgICAgICAgICAgICAgICAgICAgZm9yIHhfRyA8PSBUCiAgICAgKiAgIHlfRyA9IFQgKyAoeF9HIC0gVCkgLyBSICAgICAgICAgZm9yIHhfRyA+ICBUCiAgICAgKgogICAgICogU29mdCBrbmVlIChXID4gMCksIGVxLjQg4oCUIHF1YWRyYXRpYyBpbnRlcnBvbGF0aW9uIGFjcm9zcyArLy0gVy8yIGFyb3VuZCBUOgogICAgICogICB5X0cgPSB4X0cgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMih4X0ctVCkgPCAtVwogICAgICogICB5X0cgPSB4X0cgKyAoMS9SIC0gMSkoeF9HIC0gVCArIFcvMileMiAvICgyVykgICAgICAgICAgfDIoeF9HLVQpfCA8PSBXCiAgICAgKiAgIHlfRyA9IFQgKyAoeF9HIC0gVCkgLyBSICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyKHhfRy1UKSA+ICBXCiAgICAgKgogICAgICogQSBsaW1pdGVyIGlzIFIgLT4gaW5maW5pdHk6IDEvUiAtPiAwLCBzbyBhYm92ZSBUIHRoZSBjdXJ2ZSBmbGF0dGVucyB0byB5X0cgPSBUCiAgICAgKiAoZXFzIDE4LTE5KS4gV2UgaGFuZGxlIHRoZSBpbmZpbml0ZS96ZXJvLXNsb3BlIGNhc2UgZXhwbGljaXRseSB0byBhdm9pZAogICAgICogZGl2aXNpb24gYnkgaW5maW5pdHkuCiAgICAgKgogICAgICogRG93bndhcmQgRVhQQU5TSU9OIChSIDwgMSwgR2lhbm5vdWxpcyAyMDEyIHAuNDAzICJmb3IgcmF0aW9zIDwgMSAuLi4gdGhlCiAgICAgKiB2YXJpYWJsZSBzaW1wbHkgYmVjb21lcyBzbWFsbGVyIHRoYW4gLTEiKTogdGhlIHNsb3BlLWFsdGVyZWQgcmVnaW9uIGlzIEJFTE9XCiAgICAgKiB0aGUgdGhyZXNob2xkIHJhdGhlciB0aGFuIGFib3ZlIGl0LiBXaXRoIHNsb3BlIDEvUiA+IDEgYXBwbGllZCBmb3IgeF9HIDwgVCwKICAgICAqIHlfRyA9IFQgKyAoeF9HIC0gVCkvUiBpcyBtb3JlIG5lZ2F0aXZlIHRoYW4geF9HLCBzbyBxdWlldCBzaWduYWxzIGFyZSBwdXNoZWQKICAgICAqIGZ1cnRoZXIgZG93biAoaW5jcmVhc2luZyBkeW5hbWljIHJhbmdlOyBhbiBleHRyZW1lIFI8MSByYXRpbyBpcyBhIGdhdGUpLgogICAgICogU2VlIG5vdGVzLm1kICJFeHBhbmRlciAvIEdhdGUgZnJvbSB0aGUgU0FNRSBtYWNoaW5lcnkiLiBUaGUgY29tcHJlc3NvciBicmFuY2gKICAgICAqIChSID49IDEpIGFuZCB0aGUgZXhwYW5kZXIgYnJhbmNoIChSIDwgMSkgYXJlIG1pcnJvciBpbWFnZXMgYWJvdXQgVCwgc28gb25lCiAgICAgKiBmdW5jdGlvbiBwYXJhbWV0ZXJpemVkIGJ5IFQvUi9XIHNlcnZlcyBjb21wcmVzc29yLCBsaW1pdGVyLCBleHBhbmRlciBhbmQgZ2F0ZS4KICAgICAqLwogICAgZnVuY3Rpb24gY29tcHV0ZVN0YXRpY0dhaW4oeEcsIHRocmVzaG9sZCwgcmF0aW8sIGtuZWUpIHsKICAgICAgICBjb25zdCBzbG9wZSA9IHJhdGlvID09PSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkgPyAwIDogMSAvIHJhdGlvOyAvLyAxL1I7IGxpbWl0ZXIgLT4gMAogICAgICAgIGNvbnN0IGRlbHRhID0geEcgLSB0aHJlc2hvbGQ7CiAgICAgICAgY29uc3QgaXNFeHBhbmRlciA9IHJhdGlvIDwgMTsgLy8gUiA8IDEgPT4gZG93bndhcmQgZXhwYW5zaW9uIGJlbG93IFQKICAgICAgICBpZiAoaXNFeHBhbmRlcikgewogICAgICAgICAgICAvLyBEb3dud2FyZCBleHBhbmRlcjogc2xvcGUtYWx0ZXJlZCByZWdpb24gaXMgQkVMT1cgdGhyZXNob2xkIChlcXMgMi00CiAgICAgICAgICAgIC8vIG1pcnJvcmVkKS4gU29mdCBrbmVlIGNlbnRlcmVkIG9uIFQgKGVxLjQgZm9ybSwgbWlycm9yZWQgdG8gdGhlIGxvd2VyCiAgICAgICAgICAgIC8vIHNpZGUpOiBibGVuZCB0aGUgdW5pdHkgc2xvcGUgKGFib3ZlKSBpbnRvIHNsb3BlIDEvUiAoYmVsb3cpLgogICAgICAgICAgICBpZiAoa25lZSA+IDAgJiYgTWF0aC5hYnMoMiAqIGRlbHRhKSA8PSBrbmVlKSB7CiAgICAgICAgICAgICAgICAvLyBlcS40IHF1YWRyYXRpYyBpbnRlcnBvbGF0aW9uLCBtaXJyb3JlZCBhYm91dCBUIGZvciB0aGUgZXhwYW5kZXIuCiAgICAgICAgICAgICAgICAvLyBUaGUgZXhwYW5kZXIncyBzbG9wZS1hbHRlcmVkIHJlZ2lvbiBpcyBCRUxPVyBULCBzbyB0aGUga25lZSBtdXN0IGpvaW4KICAgICAgICAgICAgICAgIC8vIHRoZSBzbG9wZSBsaW5lIGF0IHRoZSBMT1dFUiBlZGdlIChkZWx0YSA9IC1XLzIpIGFuZCB1bml0eSBhdCB0aGUgVVBQRVIKICAgICAgICAgICAgICAgIC8vIGVkZ2UgKGRlbHRhID0gK1cvMikuIEFuY2hvciB0aGUgcGFyYWJvbGEgdG8gdGhlIHVwcGVyIGVkZ2UgKHQgPSAwIHRoZXJlKQogICAgICAgICAgICAgICAgLy8gYW5kIHN1YnRyYWN0IHRoZSBjdXJ2YXR1cmUgc28geV9HIG1lZXRzIHRoZSBzbG9wZSBsaW5lIGJlbG93OiB0aGlzCiAgICAgICAgICAgICAgICAvLyB5aWVsZHMgeV9HIDw9IHhfRyBldmVyeXdoZXJlIChuZXZlciBib29zdHMpIGFuZCBDMC9DMSBjb250aW51aXR5IGF0IGJvdGgKICAgICAgICAgICAgICAgIC8vIGVkZ2VzIChzbG9wZSAxL1IgYmVsb3csIHVuaXR5IGFib3ZlKS4gVGhlIE1JTlVTIHNpZ24gaXMgdGhlIG1pcnJvciBvZgogICAgICAgICAgICAgICAgLy8gdGhlIGNvbXByZXNzb3IgYnJhbmNoJ3MgUExVUyDigJQgc2FtZSBlcS40IGZvcm0sIG9wcG9zaXRlIGFsdGVyZWQgc2lkZS4KICAgICAgICAgICAgICAgIGNvbnN0IHQgPSBkZWx0YSAtIGtuZWUgLyAyOyAvLyAwIGF0IHRoZSB1cHBlciBlZGdlIChkZWx0YSA9ICtXLzIpCiAgICAgICAgICAgICAgICByZXR1cm4geEcgLSAoKHNsb3BlIC0gMSkgKiAodCAqIHQpKSAvICgyICoga25lZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKGRlbHRhID49IDApIHsKICAgICAgICAgICAgICAgIC8vIHhfRyA+PSBUIChhYm92ZSB0aHJlc2hvbGQpIOKAlCB1bml0eSBmb3IgYSBkb3dud2FyZCBleHBhbmRlci4KICAgICAgICAgICAgICAgIHJldHVybiB4RzsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyB4X0cgPCBUIChiZWxvdyB0aHJlc2hvbGQpIOKAlCBzbG9wZSAxL1IgPiAxIHB1c2hlcyB0aGUgbGV2ZWwgZG93bi4KICAgICAgICAgICAgcmV0dXJuIHRocmVzaG9sZCArIGRlbHRhICogc2xvcGU7CiAgICAgICAgfQogICAgICAgIC8vIENvbXByZXNzb3IgLyBsaW1pdGVyIChSID49IDEpOiBzbG9wZS1hbHRlcmVkIHJlZ2lvbiBpcyBBQk9WRSB0aHJlc2hvbGQuCiAgICAgICAgLy8gU29mdCBrbmVlIChlcS40IG1pZGRsZSBicmFuY2gpOiAyfHhfRyAtIFR8IDw9IFcuCiAgICAgICAgaWYgKGtuZWUgPiAwICYmIE1hdGguYWJzKDIgKiBkZWx0YSkgPD0ga25lZSkgewogICAgICAgICAgICAvLyBlcS40OiB5X0cgPSB4X0cgKyAoMS9SIC0gMSkoeF9HIC0gVCArIFcvMileMiAvICgyVykKICAgICAgICAgICAgY29uc3QgdCA9IGRlbHRhICsga25lZSAvIDI7CiAgICAgICAgICAgIHJldHVybiB4RyArICgoc2xvcGUgLSAxKSAqICh0ICogdCkpIC8gKDIgKiBrbmVlKTsKICAgICAgICB9CiAgICAgICAgLy8gSGFyZCBrbmVlIC8gb3V0c2lkZSBrbmVlIHJlZ2lvbiAoZXEuMyAvIGVxLjQgb3V0ZXIgYnJhbmNoZXMpLgogICAgICAgIGlmIChkZWx0YSA8PSAwKSB7CiAgICAgICAgICAgIC8vIHhfRyA8PSBUIChiZWxvdyB0aHJlc2hvbGQpIOKAlCB1bml0eS4gKGVxLjMgZmlyc3QgY2FzZS4pCiAgICAgICAgICAgIHJldHVybiB4RzsKICAgICAgICB9CiAgICAgICAgLy8geF9HID4gVCAoYWJvdmUgdGhyZXNob2xkKSDigJQgc2xvcGUgMS9SLiAoZXEuMyBzZWNvbmQgY2FzZTsgZXEuNCB1cHBlciBicmFuY2guKQogICAgICAgIHJldHVybiB0aHJlc2hvbGQgKyBkZWx0YSAqIHNsb3BlOwogICAgfQogICAgLyoqCiAgICAgKiBUaW1lLWNvbnN0YW50IC0+IG9uZS1wb2xlIGNvZWZmaWNpZW50IG1hcHBpbmcsIEdpYW5ub3VsaXMgMjAxMiBlcS43OgogICAgICogICBhbHBoYSA9IGV4cCgtMSAvICh0YXUgKiBmX3MpKQogICAgICogdGF1IGlzIHRoZSB0aW1lIHRvIHJlYWNoIDEgLSAxL2Ugb2YgdGhlIGZpbmFsIHZhbHVlLiBGb3IgdGF1ID4gMCwgZl9zID4gMAogICAgICogdGhlIHJlc3VsdCBsaWVzIGluICgwLCAxKTsgbGFyZ2VyIHRhdSAtPiBhbHBoYSBjbG9zZXIgdG8gMSAoc2xvd2VyKS4KICAgICAqLwogICAgZnVuY3Rpb24gdGltZUNvbnN0YW50VG9Db2VmZmljaWVudCh0YXUsIHNhbXBsZVJhdGUpIHsKICAgICAgICBpZiAodGF1IDw9IDApCiAgICAgICAgICAgIHJldHVybiAwOyAvLyBkZWdlbmVyYXRlOiBpbnN0YW50YW5lb3VzIHJlc3BvbnNlCiAgICAgICAgcmV0dXJuIE1hdGguZXhwKC0xIC8gKHRhdSAqIHNhbXBsZVJhdGUpKTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgZmVlZC1mb3J3YXJkIGR5bmFtaWNzIHByb2Nlc3Nvci4gT25lIGluc3RhbmNlIG93bnMgdGhlIGJhbGxpc3RpY3MKICAgICAqIHN0YXRlICh0aGUgc21vb3RoZWQgZ2Fpbi1yZWR1Y3Rpb24gZW52ZWxvcGUgeV9MIG9mIHRoZSBlcS4xNiBkZXRlY3RvcikgYW5kIGlzCiAgICAgKiByZXVzZWQgYmxvY2stdG8tYmxvY2sgc28gdGhlIElJUiBtZW1vcnkgaXMgcHJlc2VydmVkIGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMg4oCUCiAgICAgKiBjb25zdHJ1Y3RpbmcgYSBmcmVzaCBpbnN0YW5jZSBwZXIgYmxvY2sgd291bGQgcmVzZXQgdGhlIGVudmVsb3BlIGFuZCBwcm9kdWNlCiAgICAgKiBhdWRpYmxlIHRyYW5zaWVudHMgKHNhbWUgaGF6YXJkIGRvY3VtZW50ZWQgZm9yIHRoZSBiLWZvcm1hdCB1cG1peGVyKS4KICAgICAqLwogICAgY2xhc3MgRHluYW1pY3NQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgLyoqCiAgICAgICAgICogU21vb3RoZWQgZ2Fpbi1yZWR1Y3Rpb24gY29udHJvbCBlbnZlbG9wZSB5X0xbbi0xXSAodGhlIHN0YXRlIG9mIHRoZSBlcS4xNgogICAgICAgICAqIHNtb290aCBicmFuY2hpbmcgcGVhayBkZXRlY3RvcikuIE9wZXJhdGVzIG9uIHRoZSBnYWluLXJlZHVjdGlvbiBzaWduYWwKICAgICAgICAgKiB4X0wgPSB4X0cgLSB5X0cgKGVxLjIzKSwgd2hpY2ggaXMgMCAobm8gcmVkdWN0aW9uKSB3aGVuIG5vdCBjb21wcmVzc2luZywgc28KICAgICAgICAgKiB0aGUgZW52ZWxvcGUgcmVzdHMgYXQgMC4KICAgICAgICAgKi8KICAgICAgICBlbnZlbG9wZURiID0gMDsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgfQogICAgICAgIC8qKiBSZXNldCB0aGUgYmFsbGlzdGljcyBzdGF0ZSAoZS5nLiBvbiBhIGRpc2NvbnRpbnVpdHkpLiAqLwogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmVudmVsb3BlRGIgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIG9uZSBzYW1wbGUsIHJldHVybmluZyB0aGUgZ2Fpbi1hcHBsaWVkIG91dHB1dCBzYW1wbGUuCiAgICAgICAgICoKICAgICAgICAgKiBTaWduYWwgZmxvdyAoR2lhbm5vdWxpcyAyMDEyIHJlY29tbWVuZGVkIGZlZWQtZm9yd2FyZCwgbG9nLWRvbWFpbiBkZXRlY3RvcgogICAgICAgICAqIGFmdGVyIHRoZSBnYWluIGNvbXB1dGVyKToKICAgICAgICAgKiAgIDEuIHhfRyA9IDIwIGxvZzEwIHx4fCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZXEuMjMgbGluZSAxKQogICAgICAgICAqICAgMi4geV9HID0gZ2FpbiBjb21wdXRlcih4X0c7IFQsIFIsIFcpICAgICAgICAgICAgICAgICAgIChlcXMgMy00KQogICAgICAgICAqICAgMy4geF9MID0geF9HIC0geV9HICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChlcS4yMyBsaW5lIDIpCiAgICAgICAgICogICAgICDigJQgdGhlIGluc3RhbnRhbmVvdXMgZ2Fpbi1yZWR1Y3Rpb24gKD49IDAgZm9yIGNvbXByZXNzaW9uL2xpbWl0aW5nLAogICAgICAgICAqICAgICAgICBhbHNvID49IDAgZm9yIGRvd253YXJkIGV4cGFuc2lvbikuCiAgICAgICAgICogICA0LiBzbW9vdGggYnJhbmNoaW5nIHBlYWsgZGV0ZWN0b3Igb24geF9MICAgICAgICAgICAgICAgKGVxLjE2KQogICAgICAgICAqICAgNS4gY19kQiA9IC15X0wgKyBNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChlcS4yMyBsaW5lIDMsIGVxLjEpCiAgICAgICAgICogICA2LiB5W25dID0gMTBeKGNfZEIgLyAyMCkgKiB4W25dICAgICAgICAgICAgICAgICAgICAgICAgKGVxLjEgbGluZWFyIGZvcm0pCiAgICAgICAgICoKICAgICAgICAgKiBAcGFyYW0gYWxwaGFBdHRhY2sgIHByZWNvbXB1dGVkIGF0dGFjayBjb2VmZmljaWVudCBhbHBoYV9BIChlcS43KQogICAgICAgICAqIEBwYXJhbSBhbHBoYVJlbGVhc2UgcHJlY29tcHV0ZWQgcmVsZWFzZSBjb2VmZmljaWVudCBhbHBoYV9SIChlcS43KQogICAgICAgICAqLwogICAgICAgIHByb2Nlc3NTYW1wbGUoeCwgdGhyZXNob2xkLCByYXRpbywga25lZSwgYWxwaGFBdHRhY2ssIGFscGhhUmVsZWFzZSwgbWFrZXVwKSB7CiAgICAgICAgICAgIGNvbnN0IGxldmVsID0gTWF0aC5hYnMoeCk7CiAgICAgICAgICAgIC8vIGVxLjIzIGxpbmUgMTogeF9HID0gMjAgbG9nMTAgfHh8LiBGbG9vciBzaWxlbmNlIHRvIGF2b2lkIC1JbmYgZEIuCiAgICAgICAgICAgIGNvbnN0IHhHID0gbGV2ZWwgPCBMRVZFTF9GTE9PUiA/IDIwICogTWF0aC5sb2cxMChMRVZFTF9GTE9PUikgOiAyMCAqIE1hdGgubG9nMTAobGV2ZWwpOwogICAgICAgICAgICAvLyBlcXMgMy00OiBzdGF0aWMgY3VydmUuCiAgICAgICAgICAgIGNvbnN0IHlHID0gY29tcHV0ZVN0YXRpY0dhaW4oeEcsIHRocmVzaG9sZCwgcmF0aW8sIGtuZWUpOwogICAgICAgICAgICAvLyBlcS4yMyBsaW5lIDI6IGdhaW4tcmVkdWN0aW9uIGNvbnRyb2wgc2lnbmFsIChsb2cgZG9tYWluLCBhZnRlciB0aGUgZ2FpbgogICAgICAgICAgICAvLyBjb21wdXRlcikuIHhfTCA+PSAwIG1lYW5zICJ0aGlzIG11Y2ggcmVkdWN0aW9uIGlzIHdhbnRlZCByaWdodCBub3ciLgogICAgICAgICAgICBjb25zdCB4TCA9IHhHIC0geUc7CiAgICAgICAgICAgIC8vIGVxLjE2IHNtb290aCBicmFuY2hpbmcgcGVhayBkZXRlY3RvciDigJQgc21vb3RoIHRoZSBnYWluLXJlZHVjdGlvbiBzaWduYWwuCiAgICAgICAgICAgIC8vIEF0dGFjayBicmFuY2ggd2hlbiBtb3JlIHJlZHVjdGlvbiBpcyB3YW50ZWQgdGhhbiBjdXJyZW50bHkgYXBwbGllZDsKICAgICAgICAgICAgLy8gcmVsZWFzZSBicmFuY2ggb3RoZXJ3aXNlLiBUaGUgc21vb3RoICh2cyBwbGFpbikgYnJhbmNoaW5nIGZvcm0gbG93LXBhc3NlcwogICAgICAgICAgICAvLyBCT1RIIGJyYW5jaGVzIHNvIHRoZXJlIGlzIG5vIGRpc2NvbnRpbnVpdHkgYXQgdGhlIGF0dGFjazwtPnJlbGVhc2Ugc3dpdGNoLgogICAgICAgICAgICBpZiAoeEwgPiB0aGlzLmVudmVsb3BlRGIpIHsKICAgICAgICAgICAgICAgIC8vIGF0dGFjazogeF9MW25dID4geV9MW24tMV0KICAgICAgICAgICAgICAgIHRoaXMuZW52ZWxvcGVEYiA9IGFscGhhQXR0YWNrICogdGhpcy5lbnZlbG9wZURiICsgKDEgLSBhbHBoYUF0dGFjaykgKiB4TDsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIC8vIHJlbGVhc2U6IHhfTFtuXSA8PSB5X0xbbi0xXQogICAgICAgICAgICAgICAgdGhpcy5lbnZlbG9wZURiID0gYWxwaGFSZWxlYXNlICogdGhpcy5lbnZlbG9wZURiICsgKDEgLSBhbHBoYVJlbGVhc2UpICogeEw7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gZXEuMjMgbGluZSAzICsgZXEuMSBtYWtlLXVwOiBjb250cm9sIHZvbHRhZ2UgY19kQiA9IC15X0wgKyBNLgogICAgICAgICAgICBjb25zdCBjb250cm9sRGIgPSAtdGhpcy5lbnZlbG9wZURiICsgbWFrZXVwOwogICAgICAgICAgICAvLyBlcS4xIGxpbmVhciBmb3JtOiB5W25dID0gY1tuXSAqIHhbbl0sIHdpdGggY1tuXSA9IDEwXihjX2RCLzIwKS4KICAgICAgICAgICAgY29uc3QgY29udHJvbExpbmVhciA9IDEwICoqIChjb250cm9sRGIgLyAyMCk7CiAgICAgICAgICAgIHJldHVybiBjb250cm9sTGluZWFyICogeDsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhIGJsb2NrLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtYXkgYWxpYXMgKGluLXBsYWNlIGlzIGZpbmUpLiBUaGUKICAgICAgICAgKiBhdHRhY2svcmVsZWFzZSBjb2VmZmljaWVudHMgYXJlIGNvbXB1dGVkIG9uY2UgcGVyIGJsb2NrIChrLXJhdGUgcGFyYW1zKTsKICAgICAgICAgKiB0aGUgcGVyLXNhbXBsZSBzaWRlLWNoYWluIHJ1bnMgdGhlIGVxLjE2IGRldGVjdG9yIGFuZCBlcS4xIG11bHRpcGx5LgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIC8vIGVxLjc6IHRhdSAtPiBhbHBoYSwgb25jZSBwZXIgYmxvY2sgKGstcmF0ZSkuCiAgICAgICAgICAgIGNvbnN0IGFscGhhQXR0YWNrID0gdGltZUNvbnN0YW50VG9Db2VmZmljaWVudChwYXJhbXMuYXR0YWNrLCB0aGlzLnNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBhbHBoYVJlbGVhc2UgPSB0aW1lQ29uc3RhbnRUb0NvZWZmaWNpZW50KHBhcmFtcy5yZWxlYXNlLCB0aGlzLnNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBuID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IHRoaXMucHJvY2Vzc1NhbXBsZShpbnB1dFtpXSwgcGFyYW1zLnRocmVzaG9sZCwgcGFyYW1zLnJhdGlvLCBwYXJhbXMua25lZSwgYWxwaGFBdHRhY2ssIGFscGhhUmVsZWFzZSwgcGFyYW1zLm1ha2V1cCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgLyoKICAgICAqIER5bmFtaWNzIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMCiAgICAgKiBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIER5bmFtaWNzUHJvY2Vzc29yIGluIGR5bmFtaWNzLWNvcmUudHMuIE1pcnJvcnMKICAgICAqIHRoZSBkYXR0b3Jyby1yZXZlcmIudHMgLyBzdGVyZW8tdG8tYmZvcm1hdC50cyBjb3JlL3NoZWxsIHNwbGl0OiB0aGlzIGZpbGUKICAgICAqIG93bnMgb25seSB0aGUgd29ya2xldCBwbHVtYmluZyAocGFyYW1ldGVyRGVzY3JpcHRvcnMsIHByb2Nlc3MoKSwgdGhlCiAgICAgKiByZWdpc3RlclByb2Nlc3NvciBjYWxsKTsgdGhlIGFsZ29yaXRobSBsaXZlcyBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KICAgICAqCiAgICAgKiBBbGdvcml0aG06IGZlZWQtZm9yd2FyZCBkeW5hbWljIHJhbmdlIGNvbXByZXNzb3IvbGltaXRlci9leHBhbmRlci9nYXRlIHBlcgogICAgICogR2lhbm5vdWxpcywgTWFzc2JlcmcgJiBSZWlzcyAyMDEyIChzZWUgZHluYW1pY3MtY29yZS50cyBoZWFkZXIgZm9yIHRoZSBmdWxsCiAgICAgKiBjaXRhdGlvbiBhbmQgcGVyLWVxdWF0aW9uIGNvbW1lbnRzKS4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpkeW5hbWljc10iOwogICAgLyoqCiAgICAgKiBSYXRpbyBzZW50aW5lbDogdGhlIHdvcmtsZXQncyBBdWRpb1BhcmFtIGNhbm5vdCBjYXJyeSArSW5maW5pdHksIHNvIGEKICAgICAqIGxpbWl0ZXIgaXMgZXhwcmVzc2VkIGFzIGEgdmVyeSBsYXJnZSBmaW5pdGUgcmF0aW8gaGVyZSBhbmQgdHJlYXRlZCBhcwogICAgICogIi0+aW5maW5pdHkiIGJ5IHRoZSBnYWluIGNvbXB1dGVyIChzbG9wZSBlZmZlY3RpdmVseSAwIGFib3ZlIHRocmVzaG9sZCkuCiAgICAgKiBjcmVhdGVMaW1pdGVyKCkgaW4gY2Fjb3Bob255LnRzIHNldHMgdGhlIHJhdGlvIHRvIHRoaXMgdmFsdWUuCiAgICAgKi8KICAgIGNvbnN0IExJTUlURVJfUkFUSU8gPSAxMDAwOwogICAgY2xhc3MgRHluYW1pY3NXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBiYWxsaXN0aWNzCiAgICAgICAgLy8gZW52ZWxvcGUgKGVxLjE2IGRldGVjdG9yIHN0YXRlKSBhY3Jvc3MgcHJvY2VzcygpIGJsb2Nrcy4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBEWU5BTUlDU19ERUZBVUxUUyAoZHluYW1pY3MtY29yZS50cykg4oCUIHRoZSBzaW5nbGUKICAgICAgICAgICAgLy8gc291cmNlIG9mIHRydXRoIHNoYXJlZCB3aXRoIHRoZSBnYXRlIHJlZ3Jlc3Npb24gdGVzdHMuIFJhbmdlcyBsaXZlIGhlcmUKICAgICAgICAgICAgLy8gYW5kIGNvdmVyIGNvbXByZXNzb3IgKHJhdGlvID49IDEpLCBsaW1pdGVyIChyYXRpbyAtPiBsYXJnZSksIGFuZCBkb3dud2FyZAogICAgICAgICAgICAvLyBleHBhbmRlci9nYXRlIChyYXRpbyA8IDEpIGZyb20gb25lIHBhcmFtZXRlciBzZXQuCiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbInRocmVzaG9sZCIsIERZTkFNSUNTX0RFRkFVTFRTLnRocmVzaG9sZCwgLTEwMCwgMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJyYXRpbyIsIERZTkFNSUNTX0RFRkFVTFRTLnJhdGlvLCAwLjA1LCBMSU1JVEVSX1JBVElPLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImtuZWUiLCBEWU5BTUlDU19ERUZBVUxUUy5rbmVlLCAwLCA0MCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJhdHRhY2siLCBEWU5BTUlDU19ERUZBVUxUUy5hdHRhY2ssIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsicmVsZWFzZSIsIERZTkFNSUNTX0RFRkFVTFRTLnJlbGVhc2UsIDAsIDUsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWFrZXVwIiwgRFlOQU1JQ1NfREVGQVVMVFMubWFrZXVwLCAtMjQsIDI0LCAiay1yYXRlIl0sCiAgICAgICAgICAgIF0ubWFwKChbbmFtZSwgZGVmYXVsdFZhbHVlLCBtaW5WYWx1ZSwgbWF4VmFsdWUsIGF1dG9tYXRpb25SYXRlXSkgPT4gKHsKICAgICAgICAgICAgICAgIG5hbWU6IG5hbWUsCiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU6IGRlZmF1bHRWYWx1ZSwKICAgICAgICAgICAgICAgIG1pblZhbHVlOiBtaW5WYWx1ZSwKICAgICAgICAgICAgICAgIG1heFZhbHVlOiBtYXhWYWx1ZSwKICAgICAgICAgICAgICAgIGF1dG9tYXRpb25SYXRlOiBhdXRvbWF0aW9uUmF0ZSwKICAgICAgICAgICAgfSkpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgaWYgKCFpbnB1dCB8fCBpbnB1dC5sZW5ndGggPT09IDAgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoID09PSAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBrLXJhdGUgcGFyYW1zOiB0YWtlIHRoZSBmaXJzdCAoY29uc3RhbnQtb3Zlci1ibG9jaykgdmFsdWUuIEEgcmF0aW8gYXQgb3IKICAgICAgICAgICAgLy8gYWJvdmUgdGhlIGxpbWl0ZXIgc2VudGluZWwgaXMgbWFwcGVkIHRvICtJbmZpbml0eSBzbyB0aGUgY29yZSdzIGdhaW4KICAgICAgICAgICAgLy8gY29tcHV0ZXIgdXNlcyB0aGUgZXhhY3QgbGltaXRlciBzbG9wZSAoMCkgcmF0aGVyIHRoYW4gMS9MSU1JVEVSX1JBVElPLgogICAgICAgICAgICBjb25zdCByYXRpb1BhcmFtID0gcGFyYW1ldGVycy5yYXRpb1swXTsKICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgdGhyZXNob2xkOiBwYXJhbWV0ZXJzLnRocmVzaG9sZFswXSwKICAgICAgICAgICAgICAgIHJhdGlvOiByYXRpb1BhcmFtID49IExJTUlURVJfUkFUSU8gPyBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkgOiByYXRpb1BhcmFtLAogICAgICAgICAgICAgICAga25lZTogcGFyYW1ldGVycy5rbmVlWzBdLAogICAgICAgICAgICAgICAgYXR0YWNrOiBwYXJhbWV0ZXJzLmF0dGFja1swXSwKICAgICAgICAgICAgICAgIHJlbGVhc2U6IHBhcmFtZXRlcnMucmVsZWFzZVswXSwKICAgICAgICAgICAgICAgIG1ha2V1cDogcGFyYW1ldGVycy5tYWtldXBbMF0sCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGNoID0gMDsgY2ggPCBjaGFubmVsQ291bnQ7IGNoKyspIHsKICAgICAgICAgICAgICAgIGlmICghdGhpcy5jb3Jlc1tjaF0pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBEeW5hbWljc1Byb2Nlc3NvcihzYW1wbGVSYXRlKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJkeW5hbWljcyIsIER5bmFtaWNzV29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5EeW5hbWljc1dvcmtsZXRQcm9jZXNzb3IgPSBEeW5hbWljc1dvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWR5bmFtaWNzLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGZkblJldmVyYiA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qCiAgICAgKiBGRE4gcmV2ZXJiIHByb2Nlc3NvciBjb3JlIOKAlCBjb250ZXh0LWZyZWUgRFNQIG1hdGggZm9yIGEgRmVlZGJhY2sgRGVsYXkKICAgICAqIE5ldHdvcmsgKEZETikgcmV2ZXJiZXJhdG9yIHdpdGggYSBEZWxheSBGZWVkYmFjayBNYXRyaXggKHNjYXR0ZXJpbmcpIGFuZAogICAgICogcGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2lvbi4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBjYW5vbmljYWwgRkROIHJlY3Vyc2lvbiBhbmQgYSBsb3NzbGVzcyAocGFyYXVuaXRhcnkpIGZlZWRiYWNrCiAgICAgKiBtYXRyaXggZnJvbToKICAgICAqICAgUy4gSi4gU2NobGVjaHQsIEUuIEEuIFAuIEhhYmV0cywgIlNjYXR0ZXJpbmcgaW4gRmVlZGJhY2sgRGVsYXkgTmV0d29ya3MiLAogICAgICogICBJRUVFL0FDTSBUcmFucy4gQXVkaW8sIFNwZWVjaCwgTGFuZy4gUHJvY2Vzcy4sIFZvbC4gMjgsIDIwMjAgKGFyWGl2CiAgICAgKiAgIDE5MTIuMDg4ODgpLiBbaGVyZWFmdGVyICJTY2hsZWNodCAyMDIwIl0KICAgICAqCiAgICAgKiBhbmQgdGhlIG11bHRpcGxpY2F0aW9uLWZyZWUgcGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2lvbiBGSVIgZnJvbToKICAgICAqICAgSi4gRmFnZXJzdHLDtm0sIEIuIEFsYXJ5LCBTLiBKLiBTY2hsZWNodCwgVi4gVsOkbGltw6RraSwKICAgICAqICAgIlZlbHZldC1Ob2lzZSBGZWVkYmFjayBEZWxheSBOZXR3b3JrIiwgUHJvYy4gREFGeCAyMDIwLCBwcC4gMjE5LTIyNi4KICAgICAqICAgW2hlcmVhZnRlciAiRmFnZXJzdHLDtm0gMjAyMCJdCiAgICAgKgogICAgICogRnJlcXVlbmN5LWRlcGVuZGVudCBkZWNheSAoVDYwKSBpcyBzZXQgYnkgcGVyLWRlbGF5LWxpbmUgYWJzb3JwdGlvbiBmaWx0ZXJzLAogICAgICogTk9UIGJ5IHRoZSBmZWVkYmFjayBtYXRyaXgsIGZvbGxvd2luZyB0aGUgc3RhbmRhcmQgRkROIGRlc2lnbiByZWNpcGUgb2Y6CiAgICAgKiAgIEouLU0uIEpvdCwgQS4gQ2hhaWduZSwgIkRpZ2l0YWwgZGVsYXkgbmV0d29ya3MgZm9yIGRlc2lnbmluZyBhcnRpZmljaWFsCiAgICAgKiAgIHJldmVyYmVyYXRvcnMiLCBBRVMgQ29udmVudGlvbiA5MCwgMTk5MS4gW2hlcmVhZnRlciAiSm90IDE5OTEiXQogICAgICoKICAgICAqIOKUgOKUgCBTdHJ1Y3R1cmUgKFNjaGxlY2h0IDIwMjAgwqdJSTsgRmFnZXJzdHLDtm0gMjAyMCBlcXMuIDEtMykg4pSA4pSACiAgICAgKiBBbiBGRE4gb2YgTiBkZWxheSBsaW5lcyB6XnstbV9pfSBtaXhlZCB0aHJvdWdoIGFuIE7Dl04gZmVlZGJhY2sgbWF0cml4IEEoeiksCiAgICAgKiB3aXRoIGlucHV0IGdhaW5zIGIsIG91dHB1dCBnYWlucyBjLCBhbmQgZGlyZWN0IChkcnkpIGdhaW4gZC4gVGhlIHRyYW5zZmVyIGlzCiAgICAgKiAgIEgoeikgPSBj4bWAIChEX20oeileey0xfSDiiJIgQSh6KSleey0xfSBiICsgZCAsICBEX20oeikgPSBkaWFnKHpeey1tXzF94oCmel57LW1fTn0pCiAgICAgKiAoU2NobGVjaHQgMjAyMCDCp0lJOyBGYWdlcnN0csO2bSAyMDIwIGVxLiAzKS4gUGVyLXNhbXBsZSwgd2l0aCBxX2kobikgdGhlCiAgICAgKiBvdXRwdXQgb2YgZGVsYXkgbGluZSBpIChGYWdlcnN0csO2bSAyMDIwIGVxcy4gMS0yKToKICAgICAqICAgeShuKSAgICAgICAgICAgID0gzqNfaSBjX2kgcV9pKG4pICsgZMK3eChuKQogICAgICogICBzX2kobiArIG1faSkgICAgPSDOo19qIEFfe2lqfSBxX2oobikgKyBiX2nCt3gobikKICAgICAqCiAgICAgKiDilIDilIAgU2NhdHRlcmluZzogRGVsYXkgRmVlZGJhY2sgTWF0cml4LCBERk0gKFNjaGxlY2h0IDIwMjAgwqdJVi1BLCBlcS4gMTQpIOKUgOKUgAogICAgICogVGhlIGNsYXNzaWMgRkROIHVzZXMgYSBTQ0FMQVIgZmVlZGJhY2sgbWF0cml4IEEgKGRlZ3JlZS0wIHBhcmF1bml0YXJ5KS4gVGhhdAogICAgICogaXMgdGhlIGxvd2VzdC1lY2hvLWRlbnNpdHkgYmFzZWxpbmUgKFNjaGxlY2h0IDIwMjAgRmlnLiA0YSAiU2NhbGFyL0VCRk0iKS4gVG8KICAgICAqIHNjYXR0ZXIgZWNob2VzIGluc2lkZSB0aGUgZmVlZGJhY2sgcGF0aCBXSVRIT1VUIGFkZGluZyBkZWxheSBsaW5lcywgU2NobGVjaHQKICAgICAqIDIwMjAgZ2VuZXJhbGl6ZXMgQSB0byBhICpmaWx0ZXIgZmVlZGJhY2sgbWF0cml4KiBBKHopLiBUaGUgY2hlYXBlc3Qgc3VjaAogICAgICogY29uc3RydWN0aW9uIGlzIHRoZSAqKkRlbGF5IEZlZWRiYWNrIE1hdHJpeCoqIChTY2hsZWNodCAyMDIwIGVxLiAxNCk6CiAgICAgKiAgIEEoeikgPSBVIMK3IERfzrooeikgLCAgIERfzrooeikgPSBkaWFnKHpeey3Oul8xfSwg4oCmLCB6XnstzrpfTn0pCiAgICAgKiB3aGVyZSBVIGlzIGEgc2NhbGFyIG9ydGhvZ29uYWwvdW5pdGFyeSBtYXRyaXggKGhlcmUgYSBub3JtYWxpemVkIEhhZGFtYXJkKQogICAgICogYW5kIERfzrooeikgaXMgYSBkaWFnb25hbCBvZiAqcHVyZSBpbnRlZ2VyIGRlbGF5cyogzrpfaSBvbiB0aGUgZmVlZGJhY2sgcGF0aHMuCiAgICAgKiBJdCBpcyBwYXJhdW5pdGFyeSDigJQgYSB1bml0YXJ5IFUgdGltZXMgYSBkaWFnb25hbCBvZiBwdXJlIGRlbGF5cyBpcyBsb3NzbGVzcywKICAgICAqIMODKHopQSh6KSA9IERfzrooel57LTF9KSBV4bWAIFUgRF/Ouih6KSA9IEkgKFNjaGxlY2h0IDIwMjAgwqdJSUksIMKnSVYtQSkg4oCUIHNvIHRoZQogICAgICogcmVjdXJzaXZlIGNvcmUgc3RpbGwgaGFzIGFsbCBwb2xlcyBvbiB0aGUgdW5pdCBjaXJjbGUuIEVhY2ggcHJlLW1peCBkZWxheSDOul9pCiAgICAgKiBzcHJlYWRzIHRoZSBlY2hvIGVhY2ggZGVsYXktbGluZSBvdXRwdXQgcHJvZHVjZXMgYWNyb3NzIHRoZSBmZWVkYmFjayBwYXRocywKICAgICAqIHJhaXNpbmcgZWNobyBkZW5zaXR5IHBlciB0cmF2ZXJzYWwgKFNjaGxlY2h0IDIwMjAgwqdWSSwgZXEuIDM2OiBlY2hvIGdhaW4gaXMgYQogICAgICogUFJPRFVDVCBvdmVyIHRoZSBwYXRoIHNvIGVhY2ggZXh0cmEgaW50ZXJuYWwgZGVsYXkgbXVsdGlwbGllcyB0aGUgZWNobwogICAgICogY291bnQpLiBTZXR0aW5nIGFsbCDOul9pID0gMCByZWNvdmVycyB0aGUgc2NhbGFyIEhhZGFtYXJkIGJhc2VsaW5lLiBUaGlzIGNvcmUKICAgICAqIHVzZXMgc21hbGwsIG11dHVhbGx5LXNwcmVhZCDOul9pIHNvIHRoZSBERk0gZ2VudWluZWx5IHNjYXR0ZXJzLgogICAgICoKICAgICAqIOKUgOKUgCBQZXItbGluZSB2ZWx2ZXQgZGlmZnVzaW9uOiBWRkROICJzaW5nbGUiIChGYWdlcnN0csO2bSAyMDIwIMKnMywgRmlnLiA0KSDilIDilIAKICAgICAqIEZhZ2Vyc3Ryw7ZtIDIwMjAncyBWRkROIGluc2VydHMgdmVsdmV0LW5vaXNlIEZJUnMgaW50byB0aGUgRkROLiBUaGlzIGNvcmUKICAgICAqIGFwcGxpZXMgYSBESVNUSU5DVCBwZXItbGluZSBWTlMgdG8gdGhlIElOUFVUIGluamVjdGlvbiBvZiBlYWNoIGRlbGF5IGxpbmUg4oCUCiAgICAgKiB0aGUgc2luZ2xlIGlucHV0LXNldCBwbGFjZW1lbnQgKEZhZ2Vyc3Ryw7ZtIDIwMjAgZXEuIDExKTogZWFjaCBsaW5lIGlzCiAgICAgKiBkZWNvcnJlbGF0ZWQgYW5kIGVhcmx5IGVjaG8gZGVuc2l0eSByaXNlcyB0byB+TSBzcGFyc2UgZWNob2VzIHBlciBsaW5lLgogICAgICogTk9URSDigJQgaG9uZXN0IHNjb3BlOiB0aGlzIGNvcmUgZG9lcyBOT1QgcGxhY2UgVk5TIG9uIHRoZSBvdXRwdXQgYnJhbmNoIG9yCiAgICAgKiBpbnNpZGUgdGhlIGZlZWRiYWNrIGxvb3AsIHNvIHRoZSBkZW5zaXR5IGRvZXMgTk9UIGNvbXBvdW5kIHRvIHRoZSDiiYggTcKyIG9mIHRoZQogICAgICogZnVsbCBpbnB1dCtvdXRwdXQgY29uZmlndXJhdGlvbiAoZXEuIDEyKSDigJQgdGhhdCB3b3VsZCByZXF1aXJlIGFkZGl0aW9uYWwgVk5TCiAgICAgKiBzZXRzIG5vdCBpbXBsZW1lbnRlZCBoZXJlLiBBIFZOUyBoYXMgdGFwcyDiiIggeysxLCAwLCDiiJIxfSwgc28gdGhlIGNvbnZvbHV0aW9uIGlzCiAgICAgKiBhIHN1bSBvZiBzaWduZWQgZGVsYXllZCBzYW1wbGVzIOKAlCBtdWx0aXBsaWNhdGlvbi1mcmVlIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KS4KICAgICAqIERpc3RpbmN0IHBlci1saW5lIGZpbHRlcnMgKHRoZSAiVkZETiBzaW5nbGUiIGNvbmZpZ3VyYXRpb24pIGRlY29ycmVsYXRlIHRoZQogICAgICogbGluZXMsIGluc3RlYWQgb2Ygb25lIHNoYXJlZCBkaWZmdXNlciB3cml0aW5nIHRoZSBzYW1lIHZhbHVlIGludG8gZXZlcnkgbGluZS4gR3JpZCBzaXplCiAgICAgKiBUX2QgPSBmcy/PgV9kIChlcS4gNCksIE0gPSBMX3MvVF9kIGltcHVsc2VzIChlcS4gNSksIHRhcCBsb2NhdGlvbgogICAgICogayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoIobSkoVF9kIOKIkiAxKSkgKGVxLiA3KSwgc2lnbiBz4oKBKG0pID0gMsK3cm91bmQocuKCgSkg4oiSIDEKICAgICAqIOKIiCB7KzEs4oiSMX0gKGVxLiA2KS4gSml0dGVyZWQgbG9jYXRpb25zIGF2b2lkIGNvbWIgY29sb3JhdGlvbiAoRmFnZXJzdHLDtm0gMjAyMAogICAgICogwqcyLjMpLiBUaGUgcGVyLWxpbmUgZGlmZnVzaW9uIGlzIHBhcmFtZXRlcml6ZWQgYnkgYGRpZmZ1c2lvbmAgKDAgPSBieXBhc3MpLgogICAgICoKICAgICAqIOKUgOKUgCBEZWNheSAoSm90IDE5OTEsIHBlci1saW5lIGFic29ycHRpb24gZmlsdGVycykg4pSA4pSACiAgICAgKiBFYWNoIGRlbGF5IGxpbmUgaSBjYXJyaWVzIGEgcGVyLXNhbXBsZSBnYWluIGdfaSA9IDEwXnstMyBtX2kgLyAoVDYwwrdmcyl9IHNvCiAgICAgKiB0aGF0IGFmdGVyIFQ2MCBzZWNvbmRzIGEgc2lnbmFsIGNpcmN1bGF0aW5nIHRoYXQgbGluZSBoYXMgZGVjYXllZCBieSA2MCBkQgogICAgICogKEpvdCAxOTkxOiBnYWluIHBlciBkZWxheSA9IDEwXnstMyBtX2kgLyAoVDYwIGZzKX0g4oeSIOKIkjYwIGRCIG92ZXIgVDYwKS4KICAgICAqIEhpZ2gtZnJlcXVlbmN5IGRhbXBpbmcgaXMgYSBvbmUtcG9sZSBsb3ctcGFzcyBwZXIgbGluZSB0aGF0IHNob3J0ZW5zIHRoZSBIRgogICAgICogVDYwIHJlbGF0aXZlIHRvIHRoZSBicm9hZGJhbmQgVDYwIChKb3QgMTk5MSBmcmVxdWVuY3ktZGVwZW5kZW50IGFic29ycHRpb24pLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzIGFuZCBGbG9hdDMyQXJyYXkpLiBJdAogICAgICogaGFzIE5PIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMgc28gaXQgY2FuIGJlIHVuaXQtdGVzdGVkIGRpcmVjdGx5OwogICAgICogdGhlIHdvcmtsZXQgc2hlbGwgaW4gZmRuLXJldmVyYi50cyBkZWxlZ2F0ZXMgdG8gaXQuCiAgICAgKi8KICAgIC8qKiBTcGVlZCBvZiBzb3VuZCBpcyBpcnJlbGV2YW50IGhlcmU7IHRoaXMgaXMgdGhlIOKIkjYwIGRCIGNvbnN0YW50IDEwXigtMykuICovCiAgICAvKiogU21hbGxlc3QgVDYwIHdlIHdpbGwgaG9ub3IsIHRvIGtlZXAgdGhlIHBlci1saW5lIGdhaW4gZmluaXRlIGFuZCA8IDEuICovCiAgICBjb25zdCBNSU5fREVDQVlfVElNRSA9IDFlLTM7CiAgICAvKioKICAgICAqIERlZmF1bHQgZGVsYXktbGluZSBsZW5ndGhzIGluIHNhbXBsZXMgKE4gPSA4KSwgbXV0dWFsbHkgY29wcmltZSAvIHNwcmVhZCB0bwogICAgICogYXZvaWQgY29pbmNpZGVudCBlY2hvZXMgKFNjaGxlY2h0IDIwMjAgwqdJSSBkZXNpZ24gbG9yZTogImNob29zZSBOIGRlbGF5CiAgICAgKiBsZW5ndGhzIGNvcHJpbWUvc3ByZWFkIikuIFRoZXNlIHByaW1lcyBzcGFuIH4xM+KAkzU4IG1zIGF0IDQ4IGtIeiwgYSB0eXBpY2FsCiAgICAgKiBtZWRpdW0tcm9vbSBzZXQuIFNjYWxlZCB0byB0aGUgYWN0dWFsIHNhbXBsZSByYXRlIGF0IGNvbnN0cnVjdGlvbi4KICAgICAqLwogICAgY29uc3QgREVGQVVMVF9ERUxBWVNfQVRfNDhLID0gWzYxNywgNzY5LCA5MTksIDEwOTcsIDEyNTksIDE0MjksIDE2MDcsIDE3ODldOwogICAgLyoqCiAgICAgKiBEZWZhdWx0IERGTSBmZWVkYmFjay1wYXRoIGRlbGF5cyDOul9pIGluIHNhbXBsZXMgYXQgNDgga0h6IChTY2hsZWNodCAyMDIwIGVxLgogICAgICogMTQsIERfzrooeikgPSBkaWFnKHpeey3Oul9pfSkpLiBTbWFsbCwgbXV0dWFsbHktc3ByZWFkIGNvcHJpbWUgaW50ZWdlcnMgc28gZWFjaAogICAgICogZmVlZGJhY2sgcGF0aCBpcyBkZWxheWVkIGJ5IGEgZGlmZmVyZW50IHNob3J0IGFtb3VudCBiZWZvcmUgdGhlIEhhZGFtYXJkIG1peCwKICAgICAqIHNjYXR0ZXJpbmcgZWFjaCBlY2hvIGFjcm9zcyB0aGUgbGluZXMgKFNjaGxlY2h0IDIwMjAgwqdJVi1BLCDCp1ZJKS4gS2VwdCBzbWFsbAogICAgICogKHN1Yi1tcyB0byBhIGZldyBtcykgc28gdGhlIERGTSBzY2F0dGVycyBlYXJseSByZWZsZWN0aW9ucyB3aXRob3V0IHNtZWFyaW5nCiAgICAgKiB0aGUgYnJvYWRiYW5kIGRlY2F5LiBBbGwtemVybyB3b3VsZCBjb2xsYXBzZSB0aGUgREZNIHRvIHRoZSBzY2FsYXIgYmFzZWxpbmUuCiAgICAgKi8KICAgIGNvbnN0IERFRkFVTFRfRkVFREJBQ0tfREVMQVlTX0FUXzQ4SyA9IFs3LCAxMywgMjMsIDMxLCA0MSwgNTMsIDYxLCA3M107CiAgICAvKioKICAgICAqIEJ1aWxkIGEgbm9ybWFsaXplZCBIYWRhbWFyZCBtYXRyaXggSC/iiJpOIGZvciBOIGEgcG93ZXIgb2YgdHdvIHZpYSB0aGUKICAgICAqIFN5bHZlc3RlciBjb25zdHJ1Y3Rpb24uIEggaGFzIMKxMSBlbnRyaWVzIGFuZCBI4bWASCA9IE7Ct0ksIHNvIEgv4oiaTiBpcwogICAgICogb3J0aG9nb25hbCDigJQgdGhlIHNjYWxhciB1bml0YXJ5IFUgb2YgdGhlIERlbGF5IEZlZWRiYWNrIE1hdHJpeCBBKHopID0KICAgICAqIFXCt0RfzrooeikgKFNjaGxlY2h0IDIwMjAgZXEuIDE0KSwgYSBwYXJhdW5pdGFyeSBGRk0uIE9ydGhvZ29uYWxpdHkgb2YgVSBwbHVzIGEKICAgICAqIHB1cmUtZGVsYXkgZGlhZ29uYWwg4oeSIHRoZSBGRE4gZmVlZGJhY2sgY29yZSBpcyBsb3NzbGVzcyDih5IgcG9sZXMgb24gdGhlIHVuaXQKICAgICAqIGNpcmNsZSDih5Igc3RhYmxlLCB3aXRoIGRlY2F5IHN1cHBsaWVkIHNlcGFyYXRlbHkgYnkgdGhlIGFic29ycHRpb24gZmlsdGVycy4KICAgICAqCiAgICAgKiBAcGFyYW0gbiBOdW1iZXIgb2YgZGVsYXkgbGluZXMgKG11c3QgYmUgYSBwb3dlciBvZiB0d28pLgogICAgICogQHJldHVybnMgRmxhdCByb3ctbWFqb3IgTsOXTiBtYXRyaXgsIHNjYWxlZCBieSAxL+KImk4gKHNvIGl0IGlzIG9ydGhvbm9ybWFsKS4KICAgICAqLwogICAgZnVuY3Rpb24gYnVpbGRIYWRhbWFyZE1hdHJpeChuKSB7CiAgICAgICAgaWYgKG4gPCAxIHx8IChuICYgKG4gLSAxKSkgIT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBIYWRhbWFyZCBtYXRyaXggcmVxdWlyZXMgYSBwb3dlci1vZi10d28gc2l6ZSwgZ290ICR7bn1gKTsKICAgICAgICB9CiAgICAgICAgLy8gU3lsdmVzdGVyOiBIXzEgPSBbMV07IEhfezJrfSA9IFtbSF9rLCBIX2tdLCBbSF9rLCDiiJJIX2tdXS4KICAgICAgICBjb25zdCBoID0gbmV3IEZsb2F0MzJBcnJheShuICogbik7CiAgICAgICAgaFswXSA9IDE7CiAgICAgICAgZm9yIChsZXQgc2l6ZSA9IDE7IHNpemUgPCBuOyBzaXplIDw8PSAxKSB7CiAgICAgICAgICAgIGZvciAobGV0IHIgPSAwOyByIDwgc2l6ZTsgcisrKSB7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHNpemU7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHYgPSBoW3IgKiBuICsgY107CiAgICAgICAgICAgICAgICAgICAgaFtyICogbiArIChjICsgc2l6ZSldID0gdjsKICAgICAgICAgICAgICAgICAgICBoWyhyICsgc2l6ZSkgKiBuICsgY10gPSB2OwogICAgICAgICAgICAgICAgICAgIGhbKHIgKyBzaXplKSAqIG4gKyAoYyArIHNpemUpXSA9IC12OwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGNvbnN0IG5vcm0gPSAxIC8gTWF0aC5zcXJ0KG4pOwogICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbiAqIG47IGkrKykKICAgICAgICAgICAgaFtpXSAqPSBub3JtOwogICAgICAgIHJldHVybiBoOwogICAgfQogICAgLyoqCiAgICAgKiBDb25zdHJ1Y3QgYSB2ZWx2ZXQtbm9pc2Ugc2VxdWVuY2UgKEZhZ2Vyc3Ryw7ZtIDIwMjAgwqcyLjMsIGVxcy4gNC03KS4KICAgICAqCiAgICAgKiBHcmlkIHNpemUgVF9kID0gZnMvz4FfZCAoZXEuIDQpOyBudW1iZXIgb2YgaW1wdWxzZXMgTSA9IExfcy9UX2QgKGVxLiA1KTsgb25lCiAgICAgKiBqaXR0ZXJlZCBpbXB1bHNlIHBlciBncmlkIGNlbGwgYXQgayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoIobSkoVF9kIOKIkiAxKSkgKGVxLgogICAgICogNykgd2l0aCBzaWduIHPigoEobSkgPSAywrdyb3VuZChy4oKBKG0pKSDiiJIgMSDiiIggeysxLOKIkjF9IChlcS4gNikuIFRoZSBqaXR0ZXIgYXZvaWRzCiAgICAgKiB0aGUgcGVyaW9kaWMgY29tYiBjb2xvcmF0aW9uIGEgcmVndWxhciBwdWxzZSB0cmFpbiB3b3VsZCBjYXVzZSAoRmFnZXJzdHLDtm0KICAgICAqIDIwMjAgwqcyLjMgZGVzaWduIHJhdGlvbmFsZSkuIEFsbCBub24tdGFwIHBvc2l0aW9ucyBhcmUgaW1wbGljaXRseSAwLCBzbyB0aGUKICAgICAqIHNlcXVlbmNlIGhhcyBleGFjdGx5IE0gbm9uLXplcm8gwrExIGVudHJpZXMg4oCUIGV2ZXJ5IHZhbHVlIGlzICsxLCAwLCBvciDiiJIxLgogICAgICoKICAgICAqIEBwYXJhbSBsZW5ndGhTYW1wbGVzIExfcywgdG90YWwgbGVuZ3RoIG9mIHRoZSBzZXF1ZW5jZSBpbiBzYW1wbGVzLgogICAgICogQHBhcmFtIGRlbnNpdHkgz4FfZCwgcHVsc2UgZGVuc2l0eSBpbiBpbXB1bHNlcy9zZWNvbmQuCiAgICAgKiBAcGFyYW0gc2FtcGxlUmF0ZSBmcyBpbiBIei4KICAgICAqIEBwYXJhbSBybmcgQSBmdW5jdGlvbiByZXR1cm5pbmcgYSB1bmlmb3JtIHJhbmRvbSBudW1iZXIgaW4gWzAsMSkuIEluamVjdGVkIHNvCiAgICAgKiAgIHRlc3RzIGFyZSBkZXRlcm1pbmlzdGljLgogICAgICogQHJldHVybnMgVGhlIHNwYXJzZSB0YXAgbGlzdCAobGVuZ3RoIE0pLCBzb3J0ZWQgYnkgbG9jYXRpb24uCiAgICAgKi8KICAgIGZ1bmN0aW9uIGJ1aWxkVmVsdmV0Tm9pc2UobGVuZ3RoU2FtcGxlcywgZGVuc2l0eSwgc2FtcGxlUmF0ZSwgcm5nID0gTWF0aC5yYW5kb20pIHsKICAgICAgICBjb25zdCBncmlkU2l6ZSA9IHNhbXBsZVJhdGUgLyBkZW5zaXR5OyAvLyBUX2QgKGVxLiA0KQogICAgICAgIGNvbnN0IG0gPSBNYXRoLm1heCgwLCBNYXRoLmZsb29yKGxlbmd0aFNhbXBsZXMgLyBncmlkU2l6ZSkpOyAvLyBNIChlcS4gNSkKICAgICAgICBjb25zdCB0YXBzID0gW107CiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtOyBpKyspIHsKICAgICAgICAgICAgY29uc3QgcjEgPSBybmcoKTsKICAgICAgICAgICAgY29uc3QgcjIgPSBybmcoKTsKICAgICAgICAgICAgLy8gayhtKSA9IHJvdW5kKG3Ct1RfZCArIHLigoLCtyhUX2Qg4oiSIDEpKSAoZXEuIDcpLgogICAgICAgICAgICBjb25zdCBsb2NhdGlvbiA9IE1hdGgucm91bmQoaSAqIGdyaWRTaXplICsgcjIgKiAoZ3JpZFNpemUgLSAxKSk7CiAgICAgICAgICAgIGlmIChsb2NhdGlvbiA+PSBsZW5ndGhTYW1wbGVzKQogICAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICAgIC8vIHPigoEobSkgPSAywrdyb3VuZChy4oKBKSDiiJIgMSDiiIggeysxLOKIkjF9IChlcS4gNikuCiAgICAgICAgICAgIGNvbnN0IHNpZ24gPSAyICogTWF0aC5yb3VuZChyMSkgLSAxID49IDAgPyAxIDogLTE7CiAgICAgICAgICAgIHRhcHMucHVzaCh7IGxvY2F0aW9uLCBzaWduIH0pOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdGFwczsKICAgIH0KICAgIC8qKgogICAgICogQSBtdWx0aXBsaWNhdGlvbi1mcmVlIHZlbHZldC1ub2lzZSBGSVIgZmlsdGVyIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KS4gSG9sZHMKICAgICAqIGl0cyBvd24gc3BhcnNlIMKxMSB0YXAgbGlzdCBhbmQgYSBwb3dlci1vZi10d28gcmluZyBidWZmZXIgb2YgcmVjZW50IGlucHV0cyBzbwogICAgICogYHByb2Nlc3MoeClgIHJldHVybnMgzqNfbSBz4oKBKG0pwrd4KG4g4oiSIGsobSkpIOKAlCBhIHN1bSBvZiBzaWduZWQgZGVsYXllZCBzYW1wbGVzLAogICAgICogbm8gbXVsdGlwbGljYXRpb25zLiBPbmUgb2YgdGhlc2UgbGl2ZXMgb24gZWFjaCBGRE4gZGVsYXkgbGluZSBpbiB0aGUgVkZETgogICAgICogInNpbmdsZSIgY29uZmlndXJhdGlvbiAoaW5wdXQtc2V0IHBsYWNlbWVudCksIHNvIGVhY2ggbGluZSBpcyBkZWNvcnJlbGF0ZWQgYW5kCiAgICAgKiBlYXJseSBlY2hvIGRlbnNpdHkgcmlzZXMgKGlucHV0IGluamVjdGlvbiBvbmx5IOKAlCBubyBNwrIgbG9vcCBjb21wb3VuZGluZykuCiAgICAgKi8KICAgIGNsYXNzIFZlbHZldEZpbHRlciB7CiAgICAgICAgdGFwczsKICAgICAgICBidWZmZXI7CiAgICAgICAgbWFzazsKICAgICAgICB3cml0ZSA9IDA7CiAgICAgICAgbm9ybTsKICAgICAgICBjb25zdHJ1Y3Rvcih0YXBzLCBzcGFuU2FtcGxlcykgewogICAgICAgICAgICB0aGlzLnRhcHMgPSB0YXBzOwogICAgICAgICAgICBjb25zdCBwb3cyID0gMiAqKiBNYXRoLmNlaWwoTWF0aC5sb2cyKE1hdGgubWF4KDIsIHNwYW5TYW1wbGVzICsgMSkpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICAgICAgLy8gTm9ybWFsaXplIGJ5IOKImk0gc28gdGhlIGRpZmZ1c2VyIGlzIH51bml0LWVuZXJneSAoYSBWTlMgb2YgTSB1bml0IHRhcHMgaGFzCiAgICAgICAgICAgIC8vIGV4cGVjdGVkIGVuZXJneSBNKTsga2VlcHMgdGhlIHBlci1saW5lIGluamVjdGlvbiBsZXZlbCBjb21wYXJhYmxlIHRvIHRoZQogICAgICAgICAgICAvLyB1bmRpZmZ1c2VkIHBhdGggc28gYGRpZmZ1c2lvbmAgaXMgYSBjbGVhbiBkcnnihpR3ZXQgYmxlbmQuCiAgICAgICAgICAgIHRoaXMubm9ybSA9IHRhcHMubGVuZ3RoID4gMCA/IDEgLyBNYXRoLnNxcnQodGFwcy5sZW5ndGgpIDogMTsKICAgICAgICB9CiAgICAgICAgLyoqIE51bWJlciBvZiBub256ZXJvIHRhcHMgTSAoZWNoby1kZW5zaXR5IG11bHRpcGxpZXIgb2YgdGhpcyBmaWx0ZXIpLiAqLwogICAgICAgIGdldCB0YXBDb3VudCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMudGFwcy5sZW5ndGg7CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIENvbnZvbHZlIG9uZSBpbnB1dCBzYW1wbGUgd2l0aCB0aGUgVk5TIChGYWdlcnN0csO2bSAyMDIwIGVxLiA4KTogcHVzaCB4IGludG8KICAgICAgICAgKiB0aGUgcmluZywgdGhlbiBhY2N1bXVsYXRlIM6jX20gc+KCgShtKcK3eChuIOKIkiBrKG0pKSBhcyBzaWduZWQgYWRkcy4gUmV0dXJucyB0aGUKICAgICAgICAgKiDiiJpNLW5vcm1hbGl6ZWQgcmVzdWx0LiBNdWx0aXBsaWNhdGlvbi1mcmVlOiBlYWNoIHRlcm0gYWRkcyBvciBzdWJ0cmFjdHMgYQogICAgICAgICAqIHN0b3JlZCBzYW1wbGUuCiAgICAgICAgICovCiAgICAgICAgcHJvY2Vzcyh4KSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyW3RoaXMud3JpdGVdID0geDsKICAgICAgICAgICAgbGV0IGFjYyA9IDA7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy50YXBzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCB0YXAgPSB0aGlzLnRhcHNbaV07CiAgICAgICAgICAgICAgICBjb25zdCBpZHggPSAodGhpcy53cml0ZSAtIHRhcC5sb2NhdGlvbikgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgICAgICBhY2MgKz0gdGFwLnNpZ24gPT09IDEgPyB0aGlzLmJ1ZmZlcltpZHhdIDogLXRoaXMuYnVmZmVyW2lkeF07CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy53cml0ZSA9ICh0aGlzLndyaXRlICsgMSkgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgIHJldHVybiBhY2MgKiB0aGlzLm5vcm07CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlci5maWxsKDApOwogICAgICAgICAgICB0aGlzLndyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEEgc2luZ2xlIGRlbGF5IGxpbmUgd2l0aCBhbiBpbnRlZ3JhdGVkIG9uZS1wb2xlIEhGLWRhbXBpbmcgYWJzb3JwdGlvbiBmaWx0ZXIKICAgICAqIChKb3QgMTk5MSkuIFJlYWRzIHRhaWwsIGFwcGxpZXMgYnJvYWRiYW5kIGRlY2F5IGdhaW4gZyBhbmQgYW4gSEYgbG93LXBhc3MsCiAgICAgKiB0aGVuIGFjY2VwdHMgdGhlIGZlZWRiYWNrLW1peGVkIGlucHV0IHdyaXRlLiBCYWNrZWQgYnkgYSBwb3dlci1vZi10d28gcmluZwogICAgICogYnVmZmVyIGZvciBjaGVhcCBiaXRtYXNrIHdyYXBwaW5nIChtaXJyb3JzIGRhdHRvcnJvLXJldmVyYi50cykuCiAgICAgKi8KICAgIGNsYXNzIERhbXBlZERlbGF5TGluZSB7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgbHBTdGF0ZSA9IDA7IC8vIG9uZS1wb2xlIGxvdy1wYXNzIG1lbW9yeSAoSm90IEhGIGFic29ycHRpb24pCiAgICAgICAgbGVuZ3RoOwogICAgICAgIGNvbnN0cnVjdG9yKGxlbmd0aFNhbXBsZXMpIHsKICAgICAgICAgICAgdGhpcy5sZW5ndGggPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKGxlbmd0aFNhbXBsZXMpKTsKICAgICAgICAgICAgY29uc3QgcG93MiA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0aGlzLmxlbmd0aCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICB9CiAgICAgICAgLyoqIFJlYWQgdGhlIHNhbXBsZSBkZWxheWVkIGJ5IGBsZW5ndGhgICh0aGUgdGFpbCBvZiB0aGUgbGluZSkuICovCiAgICAgICAgcmVhZCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSB0aGlzLmxlbmd0aCkgJiB0aGlzLm1hc2tdOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBBcHBseSBicm9hZGJhbmQgZGVjYXkgZ2FpbiBgZ2AgYW5kIEhGIGRhbXBpbmcgY29lZmZpY2llbnQgYGRhbXBgIHRvIGEKICAgICAgICAgKiB0YWlsIHNhbXBsZS4geVtuXSA9IGfCtygoMeKIkmRhbXApwrd0YWlsICsgZGFtcMK3bHBTdGF0ZSk7IGxwU3RhdGUgdHJhY2tzIHRoZQogICAgICAgICAqIHNtb290aGVkIChsb3ctZnJlcXVlbmN5KSBjb21wb25lbnQsIHNvIGxhcmdlciBgZGFtcGAgcmVtb3ZlcyBtb3JlIEhGIGVuZXJneQogICAgICAgICAqIGVhY2ggcGFzcyDih5Igc2hvcnRlciBIRiBUNjAgKEpvdCAxOTkxIGZyZXF1ZW5jeS1kZXBlbmRlbnQgYWJzb3JwdGlvbikuCiAgICAgICAgICovCiAgICAgICAgYWJzb3JiKHRhaWwsIGcsIGRhbXApIHsKICAgICAgICAgICAgdGhpcy5scFN0YXRlID0gKDEgLSBkYW1wKSAqIHRhaWwgKyBkYW1wICogdGhpcy5scFN0YXRlOwogICAgICAgICAgICByZXR1cm4gZyAqIHRoaXMubHBTdGF0ZTsKICAgICAgICB9CiAgICAgICAgLyoqIFdyaXRlIHRoZSBmZWVkYmFjay1taXhlZCBpbnB1dCBhbmQgYWR2YW5jZSB0aGUgd3JpdGUgaGVhZCBvbmUgc2FtcGxlLiAqLwogICAgICAgIHdyaXRlKHZhbHVlKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyW3RoaXMud3JpdGVJbmRleF0gPSB2YWx1ZTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXIuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5scFN0YXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEEgcHVyZS1kZWxheSByaW5nIGJ1ZmZlcjogdGhlIGRpYWdvbmFsIHB1cmUtZGVsYXkgZWxlbWVudCB6Xnstzrp9IG9mIHRoZSBEZWxheQogICAgICogRmVlZGJhY2sgTWF0cml4IChTY2hsZWNodCAyMDIwIGVxLiAxNCwgRF/Ouih6KSA9IGRpYWcoel57Lc66X2l9KSkuIEFwcGxpZWQgdG8gYQogICAgICogZmVlZGJhY2sgcGF0aCBiZWZvcmUgdGhlIEhhZGFtYXJkIG1peC4gQSDOuiBvZiAwIGlzIGEgdHJhbnNwYXJlbnQgcGFzcy10aHJvdWdoCiAgICAgKiAocmVjb3ZlcnMgdGhlIHNjYWxhciBmZWVkYmFjayBtYXRyaXgpLiBObyBmaWx0ZXJpbmcsIG5vIGdhaW4g4oCUIHBhcmF1bml0YXJpdHkKICAgICAqIGlzIHByZXNlcnZlZCBiZWNhdXNlIGEgcHVyZSBpbnRlZ2VyIGRlbGF5IGlzIGFsbHBhc3MvbG9zc2xlc3MuCiAgICAgKi8KICAgIGNsYXNzIFB1cmVEZWxheSB7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGUgPSAwOwogICAgICAgIGRlbGF5OwogICAgICAgIGNvbnN0cnVjdG9yKGRlbGF5U2FtcGxlcykgewogICAgICAgICAgICB0aGlzLmRlbGF5ID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihkZWxheVNhbXBsZXMpKTsKICAgICAgICAgICAgY29uc3QgcG93MiA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMihNYXRoLm1heCgyLCB0aGlzLmRlbGF5ICsgMSkpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KHBvdzIpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBwb3cyIC0gMTsKICAgICAgICB9CiAgICAgICAgLyoqIFB1c2ggeCwgcmV0dXJuIHRoZSBzYW1wbGUgZGVsYXllZCBieSDOuiAozro9MCByZXR1cm5zIHgganVzdCB3cml0dGVuKS4gKi8KICAgICAgICBwcm9jZXNzKHgpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZV0gPSB4OwogICAgICAgICAgICBjb25zdCBvdXQgPSB0aGlzLmJ1ZmZlclsodGhpcy53cml0ZSAtIHRoaXMuZGVsYXkpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZSA9ICh0aGlzLndyaXRlICsgMSkgJiB0aGlzLm1hc2s7CiAgICAgICAgICAgIHJldHVybiBvdXQ7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlci5maWxsKDApOwogICAgICAgICAgICB0aGlzLndyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIEZlZWRiYWNrIERlbGF5IE5ldHdvcmsgcmV2ZXJiZXJhdG9yIGNvcmUuCiAgICAgKgogICAgICogTiBwYXJhbGxlbCB7QGxpbmsgRGFtcGVkRGVsYXlMaW5lfXMgbWl4ZWQgYnkgYSBsb3NzbGVzcyBEZWxheSBGZWVkYmFjayBNYXRyaXgKICAgICAqIEEoeikgPSBIwrdEX866KHopIChTY2hsZWNodCAyMDIwIGVxLiAxNDogSGFkYW1hcmQgVSBzY2F0dGVyZWQgYnkgcGVyLXBhdGggcHVyZQogICAgICogZGVsYXlzIERfzropLCB3aXRoIHBlci1saW5lIGFic29ycHRpb24gZmlsdGVycyBzZXR0aW5nIFQ2MCAoSm90IDE5OTEpIGFuZCBhCiAgICAgKiBwZXItZGVsYXktbGluZSB2ZWx2ZXQtbm9pc2UgZGlmZnVzZXIgKEZhZ2Vyc3Ryw7ZtIDIwMjAgVkZETiAic2luZ2xlIikuIFB1cmU6CiAgICAgKiBvcGVyYXRlcyBvbiBudW1iZXJzIC8gRmxvYXQzMkFycmF5LCBubyBnbG9iYWxzLgogICAgICovCiAgICBjbGFzcyBGZG5SZXZlcmJQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgbjsKICAgICAgICBsaW5lczsKICAgICAgICBtYXRyaXg7IC8vIHJvdy1tYWpvciBOw5dOLCBvcnRob25vcm1hbCAoVSkKICAgICAgICBmYkRlbGF5czsgLy8gRF/Ouih6KTogcGVyLXBhdGggcHVyZSBkZWxheXMgKERGTSkKICAgICAgICB2ZWx2ZXRzOyAvLyBwZXItbGluZSBWTlMgKEZhZ2Vyc3Ryw7ZtIFZGRE4gc2luZ2xlKQogICAgICAgIGZlZWRiYWNrOyAvLyBzY3JhdGNoOiBtYXRyaXjCt2RlbGF5ZWQtdGFpbAogICAgICAgIHNjYXR0ZXJlZDsgLy8gc2NyYXRjaDogRF/Ouih6KcK3dGFpbAogICAgICAgIHRhaWw7IC8vIHNjcmF0Y2g6IHBlci1saW5lIHRhaWwgcmVhZHMKICAgICAgICBsaW5lR2FpbnM7IC8vIHNjcmF0Y2g6IHBlci1saW5lIGRlY2F5IGdhaW5zCiAgICAgICAgLy8gUHJlLWRlbGF5IHJpbmcgYnVmZmVyIChTY2hsZWNodCAyMDIwIMKnSUkgaW5wdXQgcGF0aCkuCiAgICAgICAgcHJlRGVsYXlCdWZmZXI7CiAgICAgICAgcHJlRGVsYXlXcml0ZSA9IDA7CiAgICAgICAgLyoqCiAgICAgICAgICogQHBhcmFtIHNhbXBsZVJhdGUgZnMgaW4gSHouCiAgICAgICAgICogQHBhcmFtIG4gTnVtYmVyIG9mIGRlbGF5IGxpbmVzOyBtdXN0IGJlIGEgcG93ZXIgb2YgdHdvIGluIFs0LDhdLiBEZWZhdWx0IDguCiAgICAgICAgICogQHBhcmFtIHJuZyBJbmplY3RlZCBSTkcgZm9yIHRoZSB2ZWx2ZXQtbm9pc2Ugc2VxdWVuY2VzIChkZXRlcm1pbmlzdGljCiAgICAgICAgICogICB0ZXN0cykuIEVhY2ggZGVsYXkgbGluZSBkcmF3cyBhIERJU1RJTkNUIFZOUyBmcm9tIHRoaXMgc3RyZWFtLCBzbyB0aGUKICAgICAgICAgKiAgIGxpbmVzIGFyZSBkZWNvcnJlbGF0ZWQgKEZhZ2Vyc3Ryw7ZtIDIwMjAgVkZETiBzaW5nbGUpLgogICAgICAgICAqIEBwYXJhbSBkZWxheXNPdmVycmlkZSBPcHRpb25hbCBleHBsaWNpdCBkZWxheS1saW5lIGxlbmd0aHMgKHNhbXBsZXMpLCBmb3IKICAgICAgICAgKiAgIHRlc3RzOyBkZWZhdWx0cyB0byB0aGUgY29wcmltZSA0OCBrSHogc2V0IHNjYWxlZCB0byBgc2FtcGxlUmF0ZWAuCiAgICAgICAgICogQHBhcmFtIGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUgT3B0aW9uYWwgZXhwbGljaXQgREZNIGZlZWRiYWNrLXBhdGggZGVsYXlzIM66X2kKICAgICAgICAgKiAgIChzYW1wbGVzKSwgZm9yIHRlc3RzLiBBbGwtemVybyBjb2xsYXBzZXMgdGhlIERGTSB0byB0aGUgc2NhbGFyIEhhZGFtYXJkCiAgICAgICAgICogICBiYXNlbGluZSAoU2NobGVjaHQgMjAyMCBlcS4gMTQgd2l0aCBEX866ID0gSSkuIERlZmF1bHRzIHRvIGEgc21hbGwgc3ByZWFkLgogICAgICAgICAqLwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIG4gPSA4LCBybmcgPSBNYXRoLnJhbmRvbSwgZGVsYXlzT3ZlcnJpZGUsIGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUpIHsKICAgICAgICAgICAgaWYgKG4gIT09IDQgJiYgbiAhPT0gOCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGZG5SZXZlcmJQcm9jZXNzb3Igc3VwcG9ydHMgTiDiiIggezQsIDh9LCBnb3QgJHtufWApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMubiA9IG47CiAgICAgICAgICAgIGNvbnN0IGJhc2VEZWxheXMgPSBkZWxheXNPdmVycmlkZSA/PyBERUZBVUxUX0RFTEFZU19BVF80OEsuc2xpY2UoMCwgbik7CiAgICAgICAgICAgIGNvbnN0IHNjYWxlID0gZGVsYXlzT3ZlcnJpZGUgPyAxIDogc2FtcGxlUmF0ZSAvIDQ4MDAwOwogICAgICAgICAgICB0aGlzLmxpbmVzID0gYmFzZURlbGF5cy5zbGljZSgwLCBuKS5tYXAoKGQpID0+IG5ldyBEYW1wZWREZWxheUxpbmUoTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChkICogc2NhbGUpKSkpOwogICAgICAgICAgICB0aGlzLm1hdHJpeCA9IGJ1aWxkSGFkYW1hcmRNYXRyaXgobik7IC8vIHNjYWxhciB1bml0YXJ5IFUgb2YgQSh6KT1VwrdEX866KHopCiAgICAgICAgICAgIC8vIERGTSBmZWVkYmFjay1wYXRoIHB1cmUgZGVsYXlzIERfzrooeikgKFNjaGxlY2h0IDIwMjAgZXEuIDE0KS4gU2NhbGVkIHRvIGZzCiAgICAgICAgICAgIC8vIHVubGVzcyBhbiBleHBsaWNpdCBvdmVycmlkZSBpcyBnaXZlbiAodGVzdHMgbWF5IHBhc3MgYWxsLXplcm8gdG8gY29tcGFyZQogICAgICAgICAgICAvLyBhZ2FpbnN0IHRoZSBzY2FsYXItSGFkYW1hcmQgYmFzZWxpbmUpLgogICAgICAgICAgICBjb25zdCBiYXNlRmJEZWxheXMgPSBmZWVkYmFja0RlbGF5c092ZXJyaWRlID8/IERFRkFVTFRfRkVFREJBQ0tfREVMQVlTX0FUXzQ4Sy5zbGljZSgwLCBuKTsKICAgICAgICAgICAgY29uc3QgZmJTY2FsZSA9IGZlZWRiYWNrRGVsYXlzT3ZlcnJpZGUgPyAxIDogc2FtcGxlUmF0ZSAvIDQ4MDAwOwogICAgICAgICAgICB0aGlzLmZiRGVsYXlzID0gYmFzZUZiRGVsYXlzLnNsaWNlKDAsIG4pLm1hcCgoaykgPT4gbmV3IFB1cmVEZWxheShNYXRoLm1heCgwLCBNYXRoLnJvdW5kKGsgKiBmYlNjYWxlKSkpKTsKICAgICAgICAgICAgdGhpcy5mZWVkYmFjayA9IG5ldyBGbG9hdDMyQXJyYXkobik7CiAgICAgICAgICAgIHRoaXMuc2NhdHRlcmVkID0gbmV3IEZsb2F0MzJBcnJheShuKTsKICAgICAgICAgICAgdGhpcy50YWlsID0gbmV3IEZsb2F0MzJBcnJheShuKTsKICAgICAgICAgICAgdGhpcy5saW5lR2FpbnMgPSBuZXcgRmxvYXQzMkFycmF5KG4pOwogICAgICAgICAgICAvLyBQcmUtZGVsYXk6IHVwIHRvIDEgc2Vjb25kLgogICAgICAgICAgICBjb25zdCBwcmVMZW4gPSBNYXRoLm1heCgxLCBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5wcmVEZWxheUJ1ZmZlciA9IG5ldyBGbG9hdDMyQXJyYXkocHJlTGVuKTsKICAgICAgICAgICAgLy8gUGVyLWRlbGF5LWxpbmUgdmVsdmV0LW5vaXNlIGRpZmZ1c2VycyAoRmFnZXJzdHLDtm0gMjAyMCBWRkROICJzaW5nbGUiKS4KICAgICAgICAgICAgLy8gRWFjaCBsaW5lIGdldHMgaXRzIE9XTiBkaXN0aW5jdCBWTlMgKH4yMCBtcyBzcGFuLCDPgV9kID0gMTAwMCBwdWxzZXMvcyDih5IKICAgICAgICAgICAgLy8gfjIwIHRhcHMpLCBkcmF3biBmcm9tIHRoZSBzaGFyZWQgUk5HIHN0cmVhbSBzbyB0aGUgTiBmaWx0ZXJzIGRpZmZlci4gV2l0aAogICAgICAgICAgICAvLyBhIGRpc3RpbmN0IGZpbHRlciBwZXIgbGluZSwgZWFjaCBsaW5lJ3MgaW5wdXQgaW5qZWN0aW9uIGlzIGRlY29ycmVsYXRlZAogICAgICAgICAgICAvLyAoc2luZ2xlIGlucHV0LXNldCwgfk0gZWNob2VzL2xpbmU7IE5PVCB0aGUgTcKyIGlucHV0K291dHB1dCBjYXNlLCBlcS4gMTIpLgogICAgICAgICAgICBjb25zdCB2ZWx2ZXRMZW4gPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKDAuMDIgKiBzYW1wbGVSYXRlKSk7CiAgICAgICAgICAgIHRoaXMudmVsdmV0cyA9IFtdOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICAgICAgdGhpcy52ZWx2ZXRzLnB1c2gobmV3IFZlbHZldEZpbHRlcihidWlsZFZlbHZldE5vaXNlKHZlbHZldExlbiwgMTAwMCwgc2FtcGxlUmF0ZSwgcm5nKSwgdmVsdmV0TGVuKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIE51bWJlciBvZiBkZWxheSBsaW5lcy4gKi8KICAgICAgICBnZXQgc2l6ZSgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMubjsKICAgICAgICB9CiAgICAgICAgLyoqIFRoZSBzY2FsYXIgdW5pdGFyeSBVIChyb3ctbWFqb3IpIG9mIHRoZSBERk0gQSh6KT1VwrdEX866KHopLCBmb3IgdmVyaWZpY2F0aW9uLiAqLwogICAgICAgIGdldCBmZWVkYmFja01hdHJpeCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMubWF0cml4OwogICAgICAgIH0KICAgICAgICAvKiogVGhlIERGTSBmZWVkYmFjay1wYXRoIGRlbGF5cyDOul9pIChzYW1wbGVzKSwgZm9yIHZlcmlmaWNhdGlvbi4gKi8KICAgICAgICBnZXQgZmVlZGJhY2tEZWxheXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLmZiRGVsYXlzLm1hcCgoZCkgPT4gZC5kZWxheSk7CiAgICAgICAgfQogICAgICAgIC8qKiBQZXItbGluZSB2ZWx2ZXQgdGFwIGNvdW50cyBNX2kgKGVjaG8tZGVuc2l0eSBtdWx0aXBsaWVycyksIGZvciB2ZXJpZmljYXRpb24uICovCiAgICAgICAgZ2V0IHZlbHZldFRhcENvdW50cygpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMudmVsdmV0cy5tYXAoKHYpID0+IHYudGFwQ291bnQpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQZXItbGluZSBicm9hZGJhbmQgZGVjYXkgZ2FpbiBnX2kgPSAxMF57LTMgbV9pLyhUNjAgZnMpfSAoSm90IDE5OTEpOiB0aGUKICAgICAgICAgKiBnYWluIHRoYXQgeWllbGRzIOKIkjYwIGRCIGFmdGVyIFQ2MCBzZWNvbmRzIGZvciBhIHNpZ25hbCBjaXJjdWxhdGluZyBsaW5lIGkuCiAgICAgICAgICovCiAgICAgICAgbGluZUdhaW4obGluZUxlbmd0aCwgZGVjYXlUaW1lKSB7CiAgICAgICAgICAgIGNvbnN0IHQ2MCA9IE1hdGgubWF4KE1JTl9ERUNBWV9USU1FLCBkZWNheVRpbWUpOwogICAgICAgICAgICByZXR1cm4gMTAgKiogKCgtMyAqIGxpbmVMZW5ndGgpIC8gKHQ2MCAqIHRoaXMuc2FtcGxlUmF0ZSkpOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2sgb2YgbW9ubyBzYW1wbGVzLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtdXN0IGJlIHRoZSBzYW1lCiAgICAgICAgICogbGVuZ3RoOyBvdXRwdXQgbWF5IGFsaWFzIGlucHV0LiBTdGF0ZWZ1bCBhY3Jvc3MgY2FsbHMgKHJpbmcgYnVmZmVycyBhbmQKICAgICAgICAgKiBhYnNvcnB0aW9uIG1lbW9yeSBwZXJzaXN0KSwgc28gc3RyZWFtaW5nIGNhbGxlcnMgcmV1c2Ugb25lIGluc3RhbmNlLgogICAgICAgICAqCiAgICAgICAgICogUGVyIHNhbXBsZSAoRmFnZXJzdHLDtm0gMjAyMCBlcXMuIDEtMjsgU2NobGVjaHQgMjAyMCDCp0lJLCBlcS4gMTQpOgogICAgICAgICAqICAgMS4gcmVhZCBwcmUtZGVsYXllZCBkcnkgc2FtcGxlIOKGkiBzCiAgICAgICAgICogICAyLiB0YWlsX2kgID0gYWJzb3JiKHJlYWRfaSwgZ19pLCBkYW1wKSAgICAgICAgICAgKEpvdCBkZWNheSArIEhGIGRhbXBpbmcpCiAgICAgICAgICogICAzLiB3ZXQgICAgID0gKDEv4oiaTikgzqNfaSB0YWlsX2kgICAgICAgICAgICAgICAgICAgIChvdXRwdXQgZ2FpbiBjKQogICAgICAgICAqICAgNC4gc2NhdF9pICA9IHpeey3Oul9pfcK3dGFpbF9pICAgICAgICAgICAgICAgICAgICAgICAoREZNIGRlbGF5cyBEX866LCBTY2hsZWNodCkKICAgICAgICAgKiAgIDUuIGZiICAgICAgPSBIIMK3IHNjYXQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobG9zc2xlc3MgSGFkYW1hcmQgbWl4IFUpCiAgICAgICAgICogICA2LiBpbmpfaSAgID0gYiDCtyBWTlNfaShzKSAgICAgICAgICAgICAgICAgICAgICAgICAgKHBlci1saW5lIHZlbHZldCwgRmFnZXJzdHLDtm0pCiAgICAgICAgICogICA3LiB3cml0ZSBsaW5lIGkg4oaQIGZiX2kgKyBpbmpfaSAgICAgICAgICAgICAgICAgICAgIChpbnB1dCBpbmplY3Rpb24pCiAgICAgICAgICogICA4LiBvdXQgICAgID0gKDHiiJJtaXgpwrdkcnkgKyBtaXjCt3dldAogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IGxlbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGNvbnN0IGRhbXAgPSBjbGFtcDAxKHBhcmFtcy5kYW1waW5nKTsKICAgICAgICAgICAgY29uc3QgZGlmZnVzaW9uID0gY2xhbXAwMShwYXJhbXMuZGlmZnVzaW9uKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gY2xhbXAwMShwYXJhbXMubWl4KTsKICAgICAgICAgICAgY29uc3QgZGVjYXlUaW1lID0gcGFyYW1zLmRlY2F5VGltZTsKICAgICAgICAgICAgY29uc3QgcHJlRGVsYXlTYW1wbGVzID0gTWF0aC5taW4odGhpcy5wcmVEZWxheUJ1ZmZlci5sZW5ndGggLSAxLCBNYXRoLm1heCgwLCBNYXRoLnJvdW5kKHBhcmFtcy5wcmVEZWxheSAqIHRoaXMuc2FtcGxlUmF0ZSkpKTsKICAgICAgICAgICAgLy8gUHJlY29tcHV0ZSBwZXItbGluZSBkZWNheSBnYWlucyBvbmNlIHBlciBibG9jayAoay1yYXRlIGRlY2F5KS4KICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm47IGkrKykgewogICAgICAgICAgICAgICAgdGhpcy5saW5lR2FpbnNbaV0gPSB0aGlzLmxpbmVHYWluKHRoaXMubGluZXNbaV0ubGVuZ3RoLCBkZWNheVRpbWUpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IGxpbmVHYWlucyA9IHRoaXMubGluZUdhaW5zOwogICAgICAgICAgICBjb25zdCBvdXRHYWluID0gMSAvIE1hdGguc3FydCh0aGlzLm4pOyAvLyBvdXRwdXQgZ2FpbiBjIChlbmVyZ3ktbm9ybWFsaXplZCBzdW0pCiAgICAgICAgICAgIGNvbnN0IGluR2FpbiA9IDEgLyBNYXRoLnNxcnQodGhpcy5uKTsgLy8gaW5wdXQgZ2FpbiBiIChzcHJlYWQgZXF1YWxseSkKICAgICAgICAgICAgZm9yIChsZXQgcyA9IDA7IHMgPCBsZW47IHMrKykgewogICAgICAgICAgICAgICAgY29uc3QgZHJ5ID0gaW5wdXRbc107CiAgICAgICAgICAgICAgICAvLyBQcmUtZGVsYXkgKFNjaGxlY2h0IDIwMjAgwqdJSSBpbnB1dCBwYXRoKS4gV3JpdGUgdGhlIGN1cnJlbnQgc2FtcGxlLAogICAgICAgICAgICAgICAgLy8gdGhlbiByZWFkIGBwcmVEZWxheVNhbXBsZXNgIGJhY2sgZnJvbSBpdDsgcHJlRGVsYXk9MCByZXR1cm5zIHRoZQogICAgICAgICAgICAgICAgLy8ganVzdC13cml0dGVuIHNhbXBsZSAobm8gc3B1cmlvdXMgb25lLWJ1ZmZlci1sZW5ndGggbGF0ZW5jeSkuCiAgICAgICAgICAgICAgICBjb25zdCBwcmVMZW4gPSB0aGlzLnByZURlbGF5QnVmZmVyLmxlbmd0aDsKICAgICAgICAgICAgICAgIHRoaXMucHJlRGVsYXlCdWZmZXJbdGhpcy5wcmVEZWxheVdyaXRlXSA9IGRyeTsKICAgICAgICAgICAgICAgIGNvbnN0IHJlYWRJZHggPSAodGhpcy5wcmVEZWxheVdyaXRlIC0gcHJlRGVsYXlTYW1wbGVzICsgcHJlTGVuKSAlIHByZUxlbjsKICAgICAgICAgICAgICAgIGNvbnN0IHByZURlbGF5ZWQgPSB0aGlzLnByZURlbGF5QnVmZmVyW3JlYWRJZHhdOwogICAgICAgICAgICAgICAgdGhpcy5wcmVEZWxheVdyaXRlID0gKHRoaXMucHJlRGVsYXlXcml0ZSArIDEpICUgcHJlTGVuOwogICAgICAgICAgICAgICAgLy8gUmVhZCArIGFic29yYiBlYWNoIGRlbGF5LWxpbmUgdGFpbCAoSm90IDE5OTEgZGVjYXkvZGFtcGluZykuCiAgICAgICAgICAgICAgICBsZXQgd2V0ID0gMDsKICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uOyBpKyspIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCB0ID0gdGhpcy5saW5lc1tpXS5hYnNvcmIodGhpcy5saW5lc1tpXS5yZWFkKCksIGxpbmVHYWluc1tpXSwgZGFtcCk7CiAgICAgICAgICAgICAgICAgICAgdGhpcy50YWlsW2ldID0gdDsKICAgICAgICAgICAgICAgICAgICB3ZXQgKz0gdDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHdldCAqPSBvdXRHYWluOwogICAgICAgICAgICAgICAgLy8gREZNIHNjYXR0ZXJpbmc6IGFwcGx5IHRoZSBwZXItcGF0aCBwdXJlIGRlbGF5cyB6XnstzrpfaX0gQkVGT1JFIHRoZQogICAgICAgICAgICAgICAgLy8gbWl4IChTY2hsZWNodCAyMDIwIGVxLiAxNCwgRF/Ouih6KSkuIEVhY2ggZmVlZGJhY2sgcGF0aCBpcyBkZWxheWVkIGJ5IGEKICAgICAgICAgICAgICAgIC8vIGRpZmZlcmVudCDOul9pLCBzcHJlYWRpbmcgdGhlIGVjaG8gZWFjaCBsaW5lIHByb2R1Y2VzIGFjcm9zcyB0aGUgbGluZXMuCiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubjsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyZWRbaV0gPSB0aGlzLmZiRGVsYXlzW2ldLnByb2Nlc3ModGhpcy50YWlsW2ldKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIC8vIExvc3NsZXNzIEhhZGFtYXJkIG1peCBmYiA9IEjCt3NjYXQgKFNjaGxlY2h0IDIwMjAgZXEuIDE0LCB0aGUgdW5pdGFyeSBVKS4KICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uOyBpKyspIHsKICAgICAgICAgICAgICAgICAgICBsZXQgYWNjID0gMDsKICAgICAgICAgICAgICAgICAgICBjb25zdCByb3cgPSBpICogdGhpcy5uOwogICAgICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5uOyBqKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgYWNjICs9IHRoaXMubWF0cml4W3JvdyArIGpdICogdGhpcy5zY2F0dGVyZWRbal07CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHRoaXMuZmVlZGJhY2tbaV0gPSBhY2M7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAvLyBQZXItbGluZSB2ZWx2ZXQgZGlmZnVzaW9uIG9mIHRoZSBpbmplY3RlZCBpbnB1dCAoRmFnZXJzdHLDtm0gMjAyMCBWRkROCiAgICAgICAgICAgICAgICAvLyBzaW5nbGUsIGVxLiA4KS4gRWFjaCBsaW5lIGdldHMgaXRzIE9XTiBWTlMtZmlsdGVyZWQgY29weSBvZiB0aGUgaW5wdXQKICAgICAgICAgICAgICAgIC8vIChtdWx0aXBsaWNhdGlvbi1mcmVlKSwgYmxlbmRlZCBkcnnihpRkaWZmdXNlZCBieSBgZGlmZnVzaW9uYCwgdGhlbiBhZGRlZAogICAgICAgICAgICAgICAgLy8gdG8gdGhhdCBsaW5lJ3MgZmVlZGJhY2suIERpc3RpbmN0IHBlci1saW5lIGZpbHRlcnMgZGVjb3JyZWxhdGUgdGhlCiAgICAgICAgICAgICAgICAvLyBsaW5lcyBhbmQgcmFpc2UgZWFybHkgZWNobyBkZW5zaXR5IChpbnB1dC1zZXQgcGxhY2VtZW50OyBub3QgTcKyIGxvb3AgY29tcG91bmRpbmcpLgogICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm47IGkrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGRpZmZ1c2VkID0gdGhpcy52ZWx2ZXRzW2ldLnByb2Nlc3MocHJlRGVsYXllZCk7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5qZWN0ZWQgPSAoKDEgLSBkaWZmdXNpb24pICogcHJlRGVsYXllZCArIGRpZmZ1c2lvbiAqIGRpZmZ1c2VkKSAqIGluR2FpbjsKICAgICAgICAgICAgICAgICAgICB0aGlzLmxpbmVzW2ldLndyaXRlKHRoaXMuZmVlZGJhY2tbaV0gKyBpbmplY3RlZCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAvLyBXZXQvZHJ5IG1peC4KICAgICAgICAgICAgICAgIG91dHB1dFtzXSA9ICgxIC0gbWl4KSAqIGRyeSArIG1peCAqIHdldDsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAvKiogQ2xlYXIgYWxsIGludGVybmFsIHN0YXRlIChkZWxheSBsaW5lcywgcHJlLWRlbGF5LCBERk0gZGVsYXlzLCBkaWZmdXNlcnMpLiAqLwogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICBmb3IgKGNvbnN0IGxpbmUgb2YgdGhpcy5saW5lcykKICAgICAgICAgICAgICAgIGxpbmUucmVzZXQoKTsKICAgICAgICAgICAgZm9yIChjb25zdCBkIG9mIHRoaXMuZmJEZWxheXMpCiAgICAgICAgICAgICAgICBkLnJlc2V0KCk7CiAgICAgICAgICAgIGZvciAoY29uc3QgdiBvZiB0aGlzLnZlbHZldHMpCiAgICAgICAgICAgICAgICB2LnJlc2V0KCk7CiAgICAgICAgICAgIHRoaXMucHJlRGVsYXlCdWZmZXIuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy5wcmVEZWxheVdyaXRlID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKiogQ2xhbXAgeCB0byBbMCwxXS4gKi8KICAgIGZ1bmN0aW9uIGNsYW1wMDEoeCkgewogICAgICAgIGlmICh4IDwgMCkKICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgaWYgKHggPiAxKQogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICByZXR1cm4geDsKICAgIH0KCiAgICAvKgogICAgICogRkROIHJldmVyYiBBdWRpb1dvcmtsZXQgc2hlbGwg4oCUIGEgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMKICAgICAqIEFMTCBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIEZkblJldmVyYlByb2Nlc3NvciBpbiBmZG4tcmV2ZXJiLWNvcmUudHMuCiAgICAgKiBNaXJyb3JzIHRoZSBkYXR0b3Jyby1yZXZlcmIudHMgLyBkeW5hbWljcy50cyBjb3JlL3NoZWxsIHNwbGl0OiB0aGlzIGZpbGUgb3ducwogICAgICogb25seSB0aGUgd29ya2xldCBwbHVtYmluZyAocGFyYW1ldGVyRGVzY3JpcHRvcnMsIHByb2Nlc3MoKSwgdGhlCiAgICAgKiByZWdpc3RlclByb2Nlc3NvciBjYWxsKTsgdGhlIGFsZ29yaXRobSBsaXZlcyBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KICAgICAqCiAgICAgKiBBbGdvcml0aG06IGEgRmVlZGJhY2sgRGVsYXkgTmV0d29yayByZXZlcmJlcmF0b3Igd2l0aCBhIGxvc3NsZXNzIERlbGF5CiAgICAgKiBGZWVkYmFjayBNYXRyaXggQSh6KT1IwrdEX866KHopIOKAlCBhIEhhZGFtYXJkIG1hdHJpeCBzY2F0dGVyZWQgYnkgcGVyLWZlZWRiYWNrLQogICAgICogcGF0aCBwdXJlIGRlbGF5cyAoU2NobGVjaHQgJiBIYWJldHMgMjAyMCwgIlNjYXR0ZXJpbmcgaW4gRmVlZGJhY2sgRGVsYXkKICAgICAqIE5ldHdvcmtzIiwgwqdJVi1BIGVxLiAxNCkg4oCUIHBlci1kZWxheS1saW5lIGFic29ycHRpb24gZmlsdGVycyBzZXR0aW5nIFQ2MCAoSm90CiAgICAgKiAmIENoYWlnbmUgMTk5MSksIGFuZCBhIG11bHRpcGxpY2F0aW9uLWZyZWUgdmVsdmV0LW5vaXNlIGRpZmZ1c2VyIG9uIEVBQ0ggZGVsYXkKICAgICAqIGxpbmUsIHRoZSBWRkROICJzaW5nbGUiIGNvbmZpZ3VyYXRpb24gKEZhZ2Vyc3Ryw7ZtLCBBbGFyeSwgU2NobGVjaHQgJiBWw6RsaW3DpGtpCiAgICAgKiAyMDIwLCAiVmVsdmV0LU5vaXNlIEZlZWRiYWNrIERlbGF5IE5ldHdvcmsiLCDCpzMpLiBTZWUgZmRuLXJldmVyYi1jb3JlLnRzIGZvcgogICAgICogdGhlIGZ1bGwgY2l0YXRpb24gYW5kIHBlci1lcXVhdGlvbiBjb21tZW50cy4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpmZG4tcmV2ZXJiXSI7CiAgICBjbGFzcyBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBkZWxheS1saW5lIGFuZAogICAgICAgIC8vIGFic29ycHRpb24tZmlsdGVyIHN0YXRlIGFjcm9zcyBwcm9jZXNzKCkgYmxvY2tzLgogICAgICAgIGNvcmVzID0gW107CiAgICAgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKICAgICAgICAgICAgLy8gRGVmYXVsdHM6IGEgbW9kZXJhdGUsIG5hdHVyYWwgcm9vbS4gZGVjYXlUaW1lIGluIHNlY29uZHMgKFQ2MCk7IHByZURlbGF5CiAgICAgICAgICAgIC8vIGluIHNlY29uZHM7IGRhbXBpbmcvZGlmZnVzaW9uL21peCBub3JtYWxpemVkIHRvIFswLDFdLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJkZWNheVRpbWUiLCAxLjUsIDAuMDAxLCAyMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJwcmVEZWxheSIsIDAsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGFtcGluZyIsIDAuMywgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkaWZmdXNpb24iLCAwLjUsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWl4IiwgMC4zLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgIF0ubWFwKChbbmFtZSwgZGVmYXVsdFZhbHVlLCBtaW5WYWx1ZSwgbWF4VmFsdWUsIGF1dG9tYXRpb25SYXRlXSkgPT4gKHsKICAgICAgICAgICAgICAgIG5hbWU6IG5hbWUsCiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU6IGRlZmF1bHRWYWx1ZSwKICAgICAgICAgICAgICAgIG1pblZhbHVlOiBtaW5WYWx1ZSwKICAgICAgICAgICAgICAgIG1heFZhbHVlOiBtYXhWYWx1ZSwKICAgICAgICAgICAgICAgIGF1dG9tYXRpb25SYXRlOiBhdXRvbWF0aW9uUmF0ZSwKICAgICAgICAgICAgfSkpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgaWYgKCFpbnB1dCB8fCBpbnB1dC5sZW5ndGggPT09IDAgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoID09PSAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBrLXJhdGUgcGFyYW1zOiB0YWtlIHRoZSBmaXJzdCAoY29uc3RhbnQtb3Zlci1ibG9jaykgdmFsdWUuCiAgICAgICAgICAgIGNvbnN0IHBhcmFtcyA9IHsKICAgICAgICAgICAgICAgIGRlY2F5VGltZTogcGFyYW1ldGVycy5kZWNheVRpbWVbMF0sCiAgICAgICAgICAgICAgICBwcmVEZWxheTogcGFyYW1ldGVycy5wcmVEZWxheVswXSwKICAgICAgICAgICAgICAgIGRhbXBpbmc6IHBhcmFtZXRlcnMuZGFtcGluZ1swXSwKICAgICAgICAgICAgICAgIGRpZmZ1c2lvbjogcGFyYW1ldGVycy5kaWZmdXNpb25bMF0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0gPSBuZXcgRmRuUmV2ZXJiUHJvY2Vzc29yKHNhbXBsZVJhdGUpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoImZkbi1yZXZlcmIiLCBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yKTsKICAgICAgICBjb25zb2xlLmluZm8oYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBjb21wbGV0ZWApOwogICAgfQogICAgY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgY29uc29sZS5lcnJvcihgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGZhaWxlZGAsIGVycm9yKTsKICAgICAgICB0aHJvdyBlcnJvcjsKICAgIH0KCiAgICBleHBvcnRzLkZkblJldmVyYldvcmtsZXRQcm9jZXNzb3IgPSBGZG5SZXZlcmJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1mZG4tcmV2ZXJiLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGZyZXF1ZW5jeVNoaWZ0ZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIEZJUi1IaWxiZXJ0IHNpbmdsZS1zaWRlYmFuZCBmcmVxdWVuY3kgc2hpZnRlci4KICAgICAqCiAgICAgKiBTY290dCBXYXJkbGUsICJBIEhpbGJlcnQtVHJhbnNmb3JtZXIgRnJlcXVlbmN5IFNoaWZ0ZXIgZm9yIEF1ZGlvIiwKICAgICAqIERBRngtOTgsIGVxLiA1YzoKICAgICAqCiAgICAgKiAgIHkodCkgPSB4KHQpIGNvcyh3X2MgdCkgLSBIe3godCl9IHNpbih3X2MgdCkKICAgICAqCiAgICAgKiBUaGUgcmVhbCBwYXRoIGlzIGRlbGF5ZWQgYnkgdGhlIEZJUiBncm91cCBkZWxheSBzbyBpdCByZW1haW5zIGluIHF1YWRyYXR1cmUKICAgICAqIHdpdGggdGhlIEhpbGJlcnQgcGF0aC4gQSBzaWduZWQgY2FycmllciBmcmVxdWVuY3kgY2hvb3NlcyB1cHBlci9sb3dlcgogICAgICogc2lkZWJhbmQgdHJhbnNsYXRpb24gd2l0aG91dCBjaGFuZ2luZyB0aGUgYWxnb3JpdGhtLgogICAgICovCiAgICBjb25zdCBERUZBVUxUX1RBUFMgPSAxMjc7CiAgICAvKiogV2luZG93ZWQgaWRlYWwgb2RkLXN5bW1ldHJpYyBIaWxiZXJ0LXRyYW5zZm9ybSBGSVIuICovCiAgICBmdW5jdGlvbiBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgaWYgKHRhcENvdW50IDwgNyB8fCB0YXBDb3VudCAlIDIgPT09IDApIHsKICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIkhpbGJlcnQgRklSIHRhcCBjb3VudCBtdXN0IGJlIGFuIG9kZCBpbnRlZ2VyID49IDciKTsKICAgICAgICB9CiAgICAgICAgY29uc3QgY2VudGVyID0gKHRhcENvdW50IC0gMSkgLyAyOwogICAgICAgIGNvbnN0IGNvZWZmaWNpZW50cyA9IG5ldyBGbG9hdDY0QXJyYXkodGFwQ291bnQpOwogICAgICAgIGZvciAobGV0IG4gPSAwOyBuIDwgdGFwQ291bnQ7IG4rKykgewogICAgICAgICAgICBjb25zdCBtID0gbiAtIGNlbnRlcjsKICAgICAgICAgICAgaWYgKG0gIT09IDAgJiYgTWF0aC5hYnMobSkgJSAyID09PSAxKSB7CiAgICAgICAgICAgICAgICBjb25zdCB3aW5kb3cgPSAwLjQyIC0gMC41ICogTWF0aC5jb3MoKDIgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSkgKyAwLjA4ICogTWF0aC5jb3MoKDQgKiBNYXRoLlBJICogbikgLyAodGFwQ291bnQgLSAxKSk7CiAgICAgICAgICAgICAgICBjb2VmZmljaWVudHNbbl0gPSAoMiAvIChNYXRoLlBJICogbSkpICogd2luZG93OwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiBjb2VmZmljaWVudHM7CiAgICB9CiAgICAvKiogU3RyZWFtaW5nIEZJUiBIaWxiZXJ0IHRyYW5zZm9ybWVyIHdpdGggYW4gYWxpZ25lZCBkaXJlY3QgdGFwLiAqLwogICAgY2xhc3MgSGlsYmVydFRyYW5zZm9ybWVyIHsKICAgICAgICBsYXRlbmN5U2FtcGxlczsKICAgICAgICBjb2VmZmljaWVudHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGVJbmRleCA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodGFwQ291bnQgPSBERUZBVUxUX1RBUFMpIHsKICAgICAgICAgICAgdGhpcy5jb2VmZmljaWVudHMgPSBkZXNpZ25IaWxiZXJ0RmlyKHRhcENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5U2FtcGxlcyA9ICh0YXBDb3VudCAtIDEpIC8gMjsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IDIgKiogTWF0aC5jZWlsKE1hdGgubG9nMih0YXBDb3VudCArIDEpKTsKICAgICAgICAgICAgdGhpcy5idWZmZXIgPSBuZXcgRmxvYXQ2NEFycmF5KHNpemUpOwogICAgICAgICAgICB0aGlzLm1hc2sgPSBzaXplIC0gMTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICB0aGlzLmJ1ZmZlclt0aGlzLndyaXRlSW5kZXhdID0gaW5wdXQ7CiAgICAgICAgICAgIGxldCBxdWFkcmF0dXJlID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNvZWZmaWNpZW50cy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgcXVhZHJhdHVyZSArPSB0aGlzLmNvZWZmaWNpZW50c1tpXSAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlSW5kZXggLSBpKSAmIHRoaXMubWFza107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgZGlyZWN0ID0gdGhpcy5idWZmZXJbKHRoaXMud3JpdGVJbmRleCAtIHRoaXMubGF0ZW5jeVNhbXBsZXMpICYgdGhpcy5tYXNrXTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4geyBkaXJlY3QsIHF1YWRyYXR1cmUgfTsKICAgICAgICB9CiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgfQogICAgfQogICAgLyoqIE9uZS1jaGFubmVsIHN0cmVhbWluZyBTU0Igc2hpZnRlci4gQ3JlYXRlIG9uZSBpbnN0YW5jZSBwZXIgYXVkaW8gY2hhbm5lbC4gKi8KICAgIGNsYXNzIEZyZXF1ZW5jeVNoaWZ0ZXJDb3JlIHsKICAgICAgICBzYW1wbGVSYXRlOwogICAgICAgIGhpbGJlcnQ7CiAgICAgICAgcGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIHRhcENvdW50ID0gREVGQVVMVF9UQVBTKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMuaGlsYmVydCA9IG5ldyBIaWxiZXJ0VHJhbnNmb3JtZXIodGFwQ291bnQpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzU2FtcGxlKGlucHV0LCBwYXJhbXMpIHsKICAgICAgICAgICAgY29uc3QgcGFpciA9IHRoaXMuaGlsYmVydC5wcm9jZXNzKGlucHV0KTsKICAgICAgICAgICAgY29uc3Qgc2hpZnRlZCA9IHBhaXIuZGlyZWN0ICogTWF0aC5jb3ModGhpcy5waGFzZSkgLSBwYWlyLnF1YWRyYXR1cmUgKiBNYXRoLnNpbih0aGlzLnBoYXNlKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgcGFyYW1zLm1peCkpOwogICAgICAgICAgICBjb25zdCBmcmVxdWVuY3kgPSBNYXRoLm1heCgtdGhpcy5zYW1wbGVSYXRlIC8gMiwgTWF0aC5taW4odGhpcy5zYW1wbGVSYXRlIC8gMiwgcGFyYW1zLmZyZXF1ZW5jeSkpOwogICAgICAgICAgICB0aGlzLnBoYXNlICs9ICgyICogTWF0aC5QSSAqIGZyZXF1ZW5jeSkgLyB0aGlzLnNhbXBsZVJhdGU7CiAgICAgICAgICAgIGlmICh0aGlzLnBoYXNlID4gTWF0aC5QSSB8fCB0aGlzLnBoYXNlIDwgLU1hdGguUEkpCiAgICAgICAgICAgICAgICB0aGlzLnBoYXNlIC09IE1hdGgucm91bmQodGhpcy5waGFzZSAvICgyICogTWF0aC5QSSkpICogMiAqIE1hdGguUEk7CiAgICAgICAgICAgIHJldHVybiBwYWlyLmRpcmVjdCAqICgxIC0gbWl4KSArIHNoaWZ0ZWQgKiBtaXg7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5wdXQubGVuZ3RoOyBpKyspCiAgICAgICAgICAgICAgICBvdXRwdXRbaV0gPSB0aGlzLnByb2Nlc3NTYW1wbGUoaW5wdXRbaV0sIHBhcmFtcyk7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmhpbGJlcnQucmVzZXQoKTsKICAgICAgICAgICAgdGhpcy5waGFzZSA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIEZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJmcmVxdWVuY3kiLCBkZWZhdWx0VmFsdWU6IDEwMCwgbWluVmFsdWU6IC0yNGUzLCBtYXhWYWx1ZTogMjQwMDAsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICAgICAgeyBuYW1lOiAibWl4IiwgZGVmYXVsdFZhbHVlOiAxLCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDEsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICBdOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3QgZnJlcXVlbmN5ID0gcGFyYW1ldGVycy5mcmVxdWVuY3lbcGFyYW1ldGVycy5mcmVxdWVuY3kubGVuZ3RoIC0gMV07CiAgICAgICAgICAgIGNvbnN0IG1peCA9IHBhcmFtZXRlcnMubWl4W3BhcmFtZXRlcnMubWl4Lmxlbmd0aCAtIDFdOwogICAgICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dC5sZW5ndGg7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgY29uc3Qgc291cmNlID0gaW5wdXRbY2hhbm5lbF0gPz8gaW5wdXRbMF07CiAgICAgICAgICAgICAgICBpZiAoIXNvdXJjZSkgewogICAgICAgICAgICAgICAgICAgIG91dHB1dFtjaGFubmVsXS5maWxsKDApOwogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgY29yZSA9ICh0aGlzLmNvcmVzW2NoYW5uZWxdID8/PSBuZXcgRnJlcXVlbmN5U2hpZnRlckNvcmUoc2FtcGxlUmF0ZSkpOwogICAgICAgICAgICAgICAgY29yZS5wcm9jZXNzKHNvdXJjZSwgb3V0cHV0W2NoYW5uZWxdLCB7IGZyZXF1ZW5jeSwgbWl4IH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJmcmVxdWVuY3ktc2hpZnRlciIsIEZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yKTsKCiAgICBleHBvcnRzLkZyZXF1ZW5jeVNoaWZ0ZXJXb3JrbGV0UHJvY2Vzc29yID0gRnJlcXVlbmN5U2hpZnRlcldvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWZyZXF1ZW5jeS1zaGlmdGVyLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIGhhcm1vbml6ZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKCSd1c2Ugc3RyaWN0JzsKCglmdW5jdGlvbiBnZXREZWZhdWx0RXhwb3J0RnJvbUNqcyAoeCkgewoJCXJldHVybiB4ICYmIHguX19lc01vZHVsZSAmJiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ2RlZmF1bHQnKSA/IHhbJ2RlZmF1bHQnXSA6IHg7Cgl9CgoJdmFyIGZmdDsKCXZhciBoYXNSZXF1aXJlZEZmdDsKCglmdW5jdGlvbiByZXF1aXJlRmZ0ICgpIHsKCQlpZiAoaGFzUmVxdWlyZWRGZnQpIHJldHVybiBmZnQ7CgkJaGFzUmVxdWlyZWRGZnQgPSAxOwoKCQlmdW5jdGlvbiBGRlQoc2l6ZSkgewoJCSAgdGhpcy5zaXplID0gc2l6ZSB8IDA7CgkJICBpZiAodGhpcy5zaXplIDw9IDEgfHwgKHRoaXMuc2l6ZSAmICh0aGlzLnNpemUgLSAxKSkgIT09IDApCgkJICAgIHRocm93IG5ldyBFcnJvcignRkZUIHNpemUgbXVzdCBiZSBhIHBvd2VyIG9mIHR3byBhbmQgYmlnZ2VyIHRoYW4gMScpOwoKCQkgIHRoaXMuX2NzaXplID0gc2l6ZSA8PCAxOwoKCQkgIC8vIE5PVEU6IFVzZSBvZiBgdmFyYCBpcyBpbnRlbnRpb25hbCBmb3Igb2xkIFY4IHZlcnNpb25zCgkJICB2YXIgdGFibGUgPSBuZXcgQXJyYXkodGhpcy5zaXplICogMik7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHRhYmxlLmxlbmd0aDsgaSArPSAyKSB7CgkJICAgIGNvbnN0IGFuZ2xlID0gTWF0aC5QSSAqIGkgLyB0aGlzLnNpemU7CgkJICAgIHRhYmxlW2ldID0gTWF0aC5jb3MoYW5nbGUpOwoJCSAgICB0YWJsZVtpICsgMV0gPSAtTWF0aC5zaW4oYW5nbGUpOwoJCSAgfQoJCSAgdGhpcy50YWJsZSA9IHRhYmxlOwoKCQkgIC8vIEZpbmQgc2l6ZSdzIHBvd2VyIG9mIHR3bwoJCSAgdmFyIHBvd2VyID0gMDsKCQkgIGZvciAodmFyIHQgPSAxOyB0aGlzLnNpemUgPiB0OyB0IDw8PSAxKQoJCSAgICBwb3dlcisrOwoKCQkgIC8vIENhbGN1bGF0ZSBpbml0aWFsIHN0ZXAncyB3aWR0aDoKCQkgIC8vICAgKiBJZiB3ZSBhcmUgZnVsbCByYWRpeC00IC0gaXQgaXMgMnggc21hbGxlciB0byBnaXZlIGluaXRhbCBsZW49OAoJCSAgLy8gICAqIE90aGVyd2lzZSBpdCBpcyB0aGUgc2FtZSBhcyBgcG93ZXJgIHRvIGdpdmUgbGVuPTQKCQkgIHRoaXMuX3dpZHRoID0gcG93ZXIgJSAyID09PSAwID8gcG93ZXIgLSAxIDogcG93ZXI7CgoJCSAgLy8gUHJlLWNvbXB1dGUgYml0LXJldmVyc2FsIHBhdHRlcm5zCgkJICB0aGlzLl9iaXRyZXYgPSBuZXcgQXJyYXkoMSA8PCB0aGlzLl93aWR0aCk7CgkJICBmb3IgKHZhciBqID0gMDsgaiA8IHRoaXMuX2JpdHJldi5sZW5ndGg7IGorKykgewoJCSAgICB0aGlzLl9iaXRyZXZbal0gPSAwOwoJCSAgICBmb3IgKHZhciBzaGlmdCA9IDA7IHNoaWZ0IDwgdGhpcy5fd2lkdGg7IHNoaWZ0ICs9IDIpIHsKCQkgICAgICB2YXIgcmV2U2hpZnQgPSB0aGlzLl93aWR0aCAtIHNoaWZ0IC0gMjsKCQkgICAgICB0aGlzLl9iaXRyZXZbal0gfD0gKChqID4+PiBzaGlmdCkgJiAzKSA8PCByZXZTaGlmdDsKCQkgICAgfQoJCSAgfQoKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQkgIHRoaXMuX2ludiA9IDA7CgkJfQoJCWZmdCA9IEZGVDsKCgkJRkZULnByb3RvdHlwZS5mcm9tQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gZnJvbUNvbXBsZXhBcnJheShjb21wbGV4LCBzdG9yYWdlKSB7CgkJICB2YXIgcmVzID0gc3RvcmFnZSB8fCBuZXcgQXJyYXkoY29tcGxleC5sZW5ndGggPj4+IDEpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb21wbGV4Lmxlbmd0aDsgaSArPSAyKQoJCSAgICByZXNbaSA+Pj4gMV0gPSBjb21wbGV4W2ldOwoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmNyZWF0ZUNvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIGNyZWF0ZUNvbXBsZXhBcnJheSgpIHsKCQkgIGNvbnN0IHJlcyA9IG5ldyBBcnJheSh0aGlzLl9jc2l6ZSk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHJlcy5sZW5ndGg7IGkrKykKCQkgICAgcmVzW2ldID0gMDsKCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS50b0NvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIHRvQ29tcGxleEFycmF5KGlucHV0LCBzdG9yYWdlKSB7CgkJICB2YXIgcmVzID0gc3RvcmFnZSB8fCB0aGlzLmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXMubGVuZ3RoOyBpICs9IDIpIHsKCQkgICAgcmVzW2ldID0gaW5wdXRbaSA+Pj4gMV07CgkJICAgIHJlc1tpICsgMV0gPSAwOwoJCSAgfQoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmNvbXBsZXRlU3BlY3RydW0gPSBmdW5jdGlvbiBjb21wbGV0ZVNwZWN0cnVtKHNwZWN0cnVtKSB7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoJCSAgdmFyIGhhbGYgPSBzaXplID4+PiAxOwoJCSAgZm9yICh2YXIgaSA9IDI7IGkgPCBoYWxmOyBpICs9IDIpIHsKCQkgICAgc3BlY3RydW1bc2l6ZSAtIGldID0gc3BlY3RydW1baV07CgkJICAgIHNwZWN0cnVtW3NpemUgLSBpICsgMV0gPSAtc3BlY3RydW1baSArIDFdOwoJCSAgfQoJCX07CgoJCUZGVC5wcm90b3R5cGUudHJhbnNmb3JtID0gZnVuY3Rpb24gdHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDA7CgkJICB0aGlzLl90cmFuc2Zvcm00KCk7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5yZWFsVHJhbnNmb3JtID0gZnVuY3Rpb24gcmVhbFRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAwOwoJCSAgdGhpcy5fcmVhbFRyYW5zZm9ybTQoKTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQlGRlQucHJvdG90eXBlLmludmVyc2VUcmFuc2Zvcm0gPSBmdW5jdGlvbiBpbnZlcnNlVHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDE7CgkJICB0aGlzLl90cmFuc2Zvcm00KCk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IG91dC5sZW5ndGg7IGkrKykKCQkgICAgb3V0W2ldIC89IHRoaXMuc2l6ZTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQkvLyByYWRpeC00IGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBVc2VzIG9mIGB2YXJgIGFyZSBpbnRlbnRpb25hbCBmb3Igb2xkZXIgVjggdmVyc2lvbiB0aGF0IGRvIG5vdAoJCS8vIHN1cHBvcnQgYm90aCBgbGV0IGNvbXBvdW5kIGFzc2lnbm1lbnRzYCBhbmQgYGNvbnN0IHBoaWAKCQlGRlQucHJvdG90eXBlLl90cmFuc2Zvcm00ID0gZnVuY3Rpb24gX3RyYW5zZm9ybTQoKSB7CgkJICB2YXIgb3V0ID0gdGhpcy5fb3V0OwoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCgkJICAvLyBJbml0aWFsIHN0ZXAgKHBlcm11dGUgYW5kIHRyYW5zZm9ybSkKCQkgIHZhciB3aWR0aCA9IHRoaXMuX3dpZHRoOwoJCSAgdmFyIHN0ZXAgPSAxIDw8IHdpZHRoOwoJCSAgdmFyIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCgkJICB2YXIgb3V0T2ZmOwoJCSAgdmFyIHQ7CgkJICB2YXIgYml0cmV2ID0gdGhpcy5fYml0cmV2OwoJCSAgaWYgKGxlbiA9PT0gNCkgewoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlVHJhbnNmb3JtMihvdXRPZmYsIG9mZiwgc3RlcCk7CgkJICAgIH0KCQkgIH0gZWxzZSB7CgkJICAgIC8vIGxlbiA9PT0gOAoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlVHJhbnNmb3JtNChvdXRPZmYsIG9mZiwgc3RlcCk7CgkJICAgIH0KCQkgIH0KCgkJICAvLyBMb29wIHRocm91Z2ggc3RlcHMgaW4gZGVjcmVhc2luZyBvcmRlcgoJCSAgdmFyIGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIHZhciB0YWJsZSA9IHRoaXMudGFibGU7CgkJICBmb3IgKHN0ZXAgPj49IDI7IHN0ZXAgPj0gMjsgc3RlcCA+Pj0gMikgewoJCSAgICBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgkJICAgIHZhciBxdWFydGVyTGVuID0gbGVuID4+PiAyOwoKCQkgICAgLy8gTG9vcCB0aHJvdWdoIG9mZnNldHMgaW4gdGhlIGRhdGEKCQkgICAgZm9yIChvdXRPZmYgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuKSB7CgkJICAgICAgLy8gRnVsbCBjYXNlCgkJICAgICAgdmFyIGxpbWl0ID0gb3V0T2ZmICsgcXVhcnRlckxlbjsKCQkgICAgICBmb3IgKHZhciBpID0gb3V0T2ZmLCBrID0gMDsgaSA8IGxpbWl0OyBpICs9IDIsIGsgKz0gc3RlcCkgewoJCSAgICAgICAgY29uc3QgQSA9IGk7CgkJICAgICAgICBjb25zdCBCID0gQSArIHF1YXJ0ZXJMZW47CgkJICAgICAgICBjb25zdCBDID0gQiArIHF1YXJ0ZXJMZW47CgkJICAgICAgICBjb25zdCBEID0gQyArIHF1YXJ0ZXJMZW47CgoJCSAgICAgICAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBBciA9IG91dFtBXTsKCQkgICAgICAgIGNvbnN0IEFpID0gb3V0W0EgKyAxXTsKCQkgICAgICAgIGNvbnN0IEJyID0gb3V0W0JdOwoJCSAgICAgICAgY29uc3QgQmkgPSBvdXRbQiArIDFdOwoJCSAgICAgICAgY29uc3QgQ3IgPSBvdXRbQ107CgkJICAgICAgICBjb25zdCBDaSA9IG91dFtDICsgMV07CgkJICAgICAgICBjb25zdCBEciA9IG91dFtEXTsKCQkgICAgICAgIGNvbnN0IERpID0gb3V0W0QgKyAxXTsKCgkJICAgICAgICAvLyBNaWRkbGUgdmFsdWVzCgkJICAgICAgICBjb25zdCBNQXIgPSBBcjsKCQkgICAgICAgIGNvbnN0IE1BaSA9IEFpOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlQnIgPSB0YWJsZVtrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlQmkgPSBpbnYgKiB0YWJsZVtrICsgMV07CgkJICAgICAgICBjb25zdCBNQnIgPSBCciAqIHRhYmxlQnIgLSBCaSAqIHRhYmxlQmk7CgkJICAgICAgICBjb25zdCBNQmkgPSBCciAqIHRhYmxlQmkgKyBCaSAqIHRhYmxlQnI7CgoJCSAgICAgICAgY29uc3QgdGFibGVDciA9IHRhYmxlWzIgKiBrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlQ2kgPSBpbnYgKiB0YWJsZVsyICogayArIDFdOwoJCSAgICAgICAgY29uc3QgTUNyID0gQ3IgKiB0YWJsZUNyIC0gQ2kgKiB0YWJsZUNpOwoJCSAgICAgICAgY29uc3QgTUNpID0gQ3IgKiB0YWJsZUNpICsgQ2kgKiB0YWJsZUNyOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlRHIgPSB0YWJsZVszICoga107CgkJICAgICAgICBjb25zdCB0YWJsZURpID0gaW52ICogdGFibGVbMyAqIGsgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1EciA9IERyICogdGFibGVEciAtIERpICogdGFibGVEaTsKCQkgICAgICAgIGNvbnN0IE1EaSA9IERyICogdGFibGVEaSArIERpICogdGFibGVEcjsKCgkJICAgICAgICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBUMHIgPSBNQXIgKyBNQ3I7CgkJICAgICAgICBjb25zdCBUMGkgPSBNQWkgKyBNQ2k7CgkJICAgICAgICBjb25zdCBUMXIgPSBNQXIgLSBNQ3I7CgkJICAgICAgICBjb25zdCBUMWkgPSBNQWkgLSBNQ2k7CgkJICAgICAgICBjb25zdCBUMnIgPSBNQnIgKyBNRHI7CgkJICAgICAgICBjb25zdCBUMmkgPSBNQmkgKyBNRGk7CgkJICAgICAgICBjb25zdCBUM3IgPSBpbnYgKiAoTUJyIC0gTURyKTsKCQkgICAgICAgIGNvbnN0IFQzaSA9IGludiAqIChNQmkgLSBNRGkpOwoKCQkgICAgICAgIC8vIEZpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgRkFyID0gVDByICsgVDJyOwoJCSAgICAgICAgY29uc3QgRkFpID0gVDBpICsgVDJpOwoKCQkgICAgICAgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCQkgICAgICAgIGNvbnN0IEZDaSA9IFQwaSAtIFQyaTsKCgkJICAgICAgICBjb25zdCBGQnIgPSBUMXIgKyBUM2k7CgkJICAgICAgICBjb25zdCBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgICAgICAgY29uc3QgRkRyID0gVDFyIC0gVDNpOwoJCSAgICAgICAgY29uc3QgRkRpID0gVDFpICsgVDNyOwoKCQkgICAgICAgIG91dFtBXSA9IEZBcjsKCQkgICAgICAgIG91dFtBICsgMV0gPSBGQWk7CgkJICAgICAgICBvdXRbQl0gPSBGQnI7CgkJICAgICAgICBvdXRbQiArIDFdID0gRkJpOwoJCSAgICAgICAgb3V0W0NdID0gRkNyOwoJCSAgICAgICAgb3V0W0MgKyAxXSA9IEZDaTsKCQkgICAgICAgIG91dFtEXSA9IEZEcjsKCQkgICAgICAgIG91dFtEICsgMV0gPSBGRGk7CgkJICAgICAgfQoJCSAgICB9CgkJICB9CgkJfTsKCgkJLy8gcmFkaXgtMiBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj00CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlVHJhbnNmb3JtMiA9IGZ1bmN0aW9uIF9zaW5nbGVUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCgkJICBjb25zdCBldmVuUiA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IGV2ZW5JID0gZGF0YVtvZmYgKyAxXTsKCQkgIGNvbnN0IG9kZFIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3Qgb2RkSSA9IGRhdGFbb2ZmICsgc3RlcCArIDFdOwoKCQkgIGNvbnN0IGxlZnRSID0gZXZlblIgKyBvZGRSOwoJCSAgY29uc3QgbGVmdEkgPSBldmVuSSArIG9kZEk7CgkJICBjb25zdCByaWdodFIgPSBldmVuUiAtIG9kZFI7CgkJICBjb25zdCByaWdodEkgPSBldmVuSSAtIG9kZEk7CgoJCSAgb3V0W291dE9mZl0gPSBsZWZ0UjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IGxlZnRJOwoJCSAgb3V0W291dE9mZiArIDJdID0gcmlnaHRSOwoJCSAgb3V0W291dE9mZiArIDNdID0gcmlnaHRJOwoJCX07CgoJCS8vIHJhZGl4LTQKCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49OAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfc2luZ2xlVHJhbnNmb3JtNChvdXRPZmYsIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgkJICBjb25zdCBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICBjb25zdCBzdGVwMiA9IHN0ZXAgKiAyOwoJCSAgY29uc3Qgc3RlcDMgPSBzdGVwICogMzsKCgkJICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEFyID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgQWkgPSBkYXRhW29mZiArIDFdOwoJCSAgY29uc3QgQnIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3QgQmkgPSBkYXRhW29mZiArIHN0ZXAgKyAxXTsKCQkgIGNvbnN0IENyID0gZGF0YVtvZmYgKyBzdGVwMl07CgkJICBjb25zdCBDaSA9IGRhdGFbb2ZmICsgc3RlcDIgKyAxXTsKCQkgIGNvbnN0IERyID0gZGF0YVtvZmYgKyBzdGVwM107CgkJICBjb25zdCBEaSA9IGRhdGFbb2ZmICsgc3RlcDMgKyAxXTsKCgkJICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICBjb25zdCBUMHIgPSBBciArIENyOwoJCSAgY29uc3QgVDBpID0gQWkgKyBDaTsKCQkgIGNvbnN0IFQxciA9IEFyIC0gQ3I7CgkJICBjb25zdCBUMWkgPSBBaSAtIENpOwoJCSAgY29uc3QgVDJyID0gQnIgKyBEcjsKCQkgIGNvbnN0IFQyaSA9IEJpICsgRGk7CgkJICBjb25zdCBUM3IgPSBpbnYgKiAoQnIgLSBEcik7CgkJICBjb25zdCBUM2kgPSBpbnYgKiAoQmkgLSBEaSk7CgoJCSAgLy8gRmluYWwgdmFsdWVzCgkJICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgkJICBjb25zdCBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgY29uc3QgRkJyID0gVDFyICsgVDNpOwoJCSAgY29uc3QgRkJpID0gVDFpIC0gVDNyOwoKCQkgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCQkgIGNvbnN0IEZDaSA9IFQwaSAtIFQyaTsKCgkJICBjb25zdCBGRHIgPSBUMXIgLSBUM2k7CgkJICBjb25zdCBGRGkgPSBUMWkgKyBUM3I7CgoJCSAgb3V0W291dE9mZl0gPSBGQXI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSBGQWk7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSBGQnI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSBGQmk7CgkJICBvdXRbb3V0T2ZmICsgNF0gPSBGQ3I7CgkJICBvdXRbb3V0T2ZmICsgNV0gPSBGQ2k7CgkJICBvdXRbb3V0T2ZmICsgNl0gPSBGRHI7CgkJICBvdXRbb3V0T2ZmICsgN10gPSBGRGk7CgkJfTsKCgkJLy8gUmVhbCBpbnB1dCByYWRpeC00IGltcGxlbWVudGF0aW9uCgkJRkZULnByb3RvdHlwZS5fcmVhbFRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfcmVhbFRyYW5zZm9ybTQoKSB7CgkJICB2YXIgb3V0ID0gdGhpcy5fb3V0OwoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCgkJICAvLyBJbml0aWFsIHN0ZXAgKHBlcm11dGUgYW5kIHRyYW5zZm9ybSkKCQkgIHZhciB3aWR0aCA9IHRoaXMuX3dpZHRoOwoJCSAgdmFyIHN0ZXAgPSAxIDw8IHdpZHRoOwoJCSAgdmFyIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCgkJICB2YXIgb3V0T2ZmOwoJCSAgdmFyIHQ7CgkJICB2YXIgYml0cmV2ID0gdGhpcy5fYml0cmV2OwoJCSAgaWYgKGxlbiA9PT0gNCkgewoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlUmVhbFRyYW5zZm9ybTIob3V0T2ZmLCBvZmYgPj4+IDEsIHN0ZXAgPj4+IDEpOwoJCSAgICB9CgkJICB9IGVsc2UgewoJCSAgICAvLyBsZW4gPT09IDgKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVJlYWxUcmFuc2Zvcm00KG91dE9mZiwgb2ZmID4+PiAxLCBzdGVwID4+PiAxKTsKCQkgICAgfQoJCSAgfQoKCQkgIC8vIExvb3AgdGhyb3VnaCBzdGVwcyBpbiBkZWNyZWFzaW5nIG9yZGVyCgkJICB2YXIgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgdmFyIHRhYmxlID0gdGhpcy50YWJsZTsKCQkgIGZvciAoc3RlcCA+Pj0gMjsgc3RlcCA+PSAyOyBzdGVwID4+PSAyKSB7CgkJICAgIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCQkgICAgdmFyIGhhbGZMZW4gPSBsZW4gPj4+IDE7CgkJICAgIHZhciBxdWFydGVyTGVuID0gaGFsZkxlbiA+Pj4gMTsKCQkgICAgdmFyIGhxdWFydGVyTGVuID0gcXVhcnRlckxlbiA+Pj4gMTsKCgkJICAgIC8vIExvb3AgdGhyb3VnaCBvZmZzZXRzIGluIHRoZSBkYXRhCgkJICAgIGZvciAob3V0T2ZmID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbikgewoJCSAgICAgIGZvciAodmFyIGkgPSAwLCBrID0gMDsgaSA8PSBocXVhcnRlckxlbjsgaSArPSAyLCBrICs9IHN0ZXApIHsKCQkgICAgICAgIHZhciBBID0gb3V0T2ZmICsgaTsKCQkgICAgICAgIHZhciBCID0gQSArIHF1YXJ0ZXJMZW47CgkJICAgICAgICB2YXIgQyA9IEIgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgdmFyIEQgPSBDICsgcXVhcnRlckxlbjsKCgkJICAgICAgICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBBciA9IG91dFtBXTsKCQkgICAgICAgIHZhciBBaSA9IG91dFtBICsgMV07CgkJICAgICAgICB2YXIgQnIgPSBvdXRbQl07CgkJICAgICAgICB2YXIgQmkgPSBvdXRbQiArIDFdOwoJCSAgICAgICAgdmFyIENyID0gb3V0W0NdOwoJCSAgICAgICAgdmFyIENpID0gb3V0W0MgKyAxXTsKCQkgICAgICAgIHZhciBEciA9IG91dFtEXTsKCQkgICAgICAgIHZhciBEaSA9IG91dFtEICsgMV07CgoJCSAgICAgICAgLy8gTWlkZGxlIHZhbHVlcwoJCSAgICAgICAgdmFyIE1BciA9IEFyOwoJCSAgICAgICAgdmFyIE1BaSA9IEFpOwoKCQkgICAgICAgIHZhciB0YWJsZUJyID0gdGFibGVba107CgkJICAgICAgICB2YXIgdGFibGVCaSA9IGludiAqIHRhYmxlW2sgKyAxXTsKCQkgICAgICAgIHZhciBNQnIgPSBCciAqIHRhYmxlQnIgLSBCaSAqIHRhYmxlQmk7CgkJICAgICAgICB2YXIgTUJpID0gQnIgKiB0YWJsZUJpICsgQmkgKiB0YWJsZUJyOwoKCQkgICAgICAgIHZhciB0YWJsZUNyID0gdGFibGVbMiAqIGtdOwoJCSAgICAgICAgdmFyIHRhYmxlQ2kgPSBpbnYgKiB0YWJsZVsyICogayArIDFdOwoJCSAgICAgICAgdmFyIE1DciA9IENyICogdGFibGVDciAtIENpICogdGFibGVDaTsKCQkgICAgICAgIHZhciBNQ2kgPSBDciAqIHRhYmxlQ2kgKyBDaSAqIHRhYmxlQ3I7CgoJCSAgICAgICAgdmFyIHRhYmxlRHIgPSB0YWJsZVszICoga107CgkJICAgICAgICB2YXIgdGFibGVEaSA9IGludiAqIHRhYmxlWzMgKiBrICsgMV07CgkJICAgICAgICB2YXIgTURyID0gRHIgKiB0YWJsZURyIC0gRGkgKiB0YWJsZURpOwoJCSAgICAgICAgdmFyIE1EaSA9IERyICogdGFibGVEaSArIERpICogdGFibGVEcjsKCgkJICAgICAgICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICAgICAgICB2YXIgVDByID0gTUFyICsgTUNyOwoJCSAgICAgICAgdmFyIFQwaSA9IE1BaSArIE1DaTsKCQkgICAgICAgIHZhciBUMXIgPSBNQXIgLSBNQ3I7CgkJICAgICAgICB2YXIgVDFpID0gTUFpIC0gTUNpOwoJCSAgICAgICAgdmFyIFQyciA9IE1CciArIE1EcjsKCQkgICAgICAgIHZhciBUMmkgPSBNQmkgKyBNRGk7CgkJICAgICAgICB2YXIgVDNyID0gaW52ICogKE1CciAtIE1Ecik7CgkJICAgICAgICB2YXIgVDNpID0gaW52ICogKE1CaSAtIE1EaSk7CgoJCSAgICAgICAgLy8gRmluYWwgdmFsdWVzCgkJICAgICAgICB2YXIgRkFyID0gVDByICsgVDJyOwoJCSAgICAgICAgdmFyIEZBaSA9IFQwaSArIFQyaTsKCgkJICAgICAgICB2YXIgRkJyID0gVDFyICsgVDNpOwoJCSAgICAgICAgdmFyIEZCaSA9IFQxaSAtIFQzcjsKCgkJICAgICAgICBvdXRbQV0gPSBGQXI7CgkJICAgICAgICBvdXRbQSArIDFdID0gRkFpOwoJCSAgICAgICAgb3V0W0JdID0gRkJyOwoJCSAgICAgICAgb3V0W0IgKyAxXSA9IEZCaTsKCgkJICAgICAgICAvLyBPdXRwdXQgZmluYWwgbWlkZGxlIHBvaW50CgkJICAgICAgICBpZiAoaSA9PT0gMCkgewoJCSAgICAgICAgICB2YXIgRkNyID0gVDByIC0gVDJyOwoJCSAgICAgICAgICB2YXIgRkNpID0gVDBpIC0gVDJpOwoJCSAgICAgICAgICBvdXRbQ10gPSBGQ3I7CgkJICAgICAgICAgIG91dFtDICsgMV0gPSBGQ2k7CgkJICAgICAgICAgIGNvbnRpbnVlOwoJCSAgICAgICAgfQoKCQkgICAgICAgIC8vIERvIG5vdCBvdmVyd3JpdGUgb3Vyc2VsdmVzCgkJICAgICAgICBpZiAoaSA9PT0gaHF1YXJ0ZXJMZW4pCgkJICAgICAgICAgIGNvbnRpbnVlOwoKCQkgICAgICAgIC8vIEluIHRoZSBmbGlwcGVkIGNhc2U6CgkJICAgICAgICAvLyBNQWkgPSAtTUFpCgkJICAgICAgICAvLyBNQnI9LU1CaSwgTUJpPS1NQnIKCQkgICAgICAgIC8vIE1Dcj0tTUNyCgkJICAgICAgICAvLyBNRHI9TURpLCBNRGk9TURyCgkJICAgICAgICB2YXIgU1QwciA9IFQxcjsKCQkgICAgICAgIHZhciBTVDBpID0gLVQxaTsKCQkgICAgICAgIHZhciBTVDFyID0gVDByOwoJCSAgICAgICAgdmFyIFNUMWkgPSAtVDBpOwoJCSAgICAgICAgdmFyIFNUMnIgPSAtaW52ICogVDNpOwoJCSAgICAgICAgdmFyIFNUMmkgPSAtaW52ICogVDNyOwoJCSAgICAgICAgdmFyIFNUM3IgPSAtaW52ICogVDJpOwoJCSAgICAgICAgdmFyIFNUM2kgPSAtaW52ICogVDJyOwoKCQkgICAgICAgIHZhciBTRkFyID0gU1QwciArIFNUMnI7CgkJICAgICAgICB2YXIgU0ZBaSA9IFNUMGkgKyBTVDJpOwoKCQkgICAgICAgIHZhciBTRkJyID0gU1QxciArIFNUM2k7CgkJICAgICAgICB2YXIgU0ZCaSA9IFNUMWkgLSBTVDNyOwoKCQkgICAgICAgIHZhciBTQSA9IG91dE9mZiArIHF1YXJ0ZXJMZW4gLSBpOwoJCSAgICAgICAgdmFyIFNCID0gb3V0T2ZmICsgaGFsZkxlbiAtIGk7CgoJCSAgICAgICAgb3V0W1NBXSA9IFNGQXI7CgkJICAgICAgICBvdXRbU0EgKyAxXSA9IFNGQWk7CgkJICAgICAgICBvdXRbU0JdID0gU0ZCcjsKCQkgICAgICAgIG91dFtTQiArIDFdID0gU0ZCaTsKCQkgICAgICB9CgkJICAgIH0KCQkgIH0KCQl9OwoKCQkvLyByYWRpeC0yIGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTQKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVSZWFsVHJhbnNmb3JtMiA9IGZ1bmN0aW9uIF9zaW5nbGVSZWFsVHJhbnNmb3JtMihvdXRPZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoKCQkgIGNvbnN0IGV2ZW5SID0gZGF0YVtvZmZdOwoJCSAgY29uc3Qgb2RkUiA9IGRhdGFbb2ZmICsgc3RlcF07CgoJCSAgY29uc3QgbGVmdFIgPSBldmVuUiArIG9kZFI7CgkJICBjb25zdCByaWdodFIgPSBldmVuUiAtIG9kZFI7CgoJCSAgb3V0W291dE9mZl0gPSBsZWZ0UjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSByaWdodFI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSAwOwoJCX07CgoJCS8vIHJhZGl4LTQKCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49OAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVJlYWxUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3NpbmdsZVJlYWxUcmFuc2Zvcm00KG91dE9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgkJICBjb25zdCBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICBjb25zdCBzdGVwMiA9IHN0ZXAgKiAyOwoJCSAgY29uc3Qgc3RlcDMgPSBzdGVwICogMzsKCgkJICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEFyID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgQnIgPSBkYXRhW29mZiArIHN0ZXBdOwoJCSAgY29uc3QgQ3IgPSBkYXRhW29mZiArIHN0ZXAyXTsKCQkgIGNvbnN0IERyID0gZGF0YVtvZmYgKyBzdGVwM107CgoJCSAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgY29uc3QgVDByID0gQXIgKyBDcjsKCQkgIGNvbnN0IFQxciA9IEFyIC0gQ3I7CgkJICBjb25zdCBUMnIgPSBCciArIERyOwoJCSAgY29uc3QgVDNyID0gaW52ICogKEJyIC0gRHIpOwoKCQkgIC8vIEZpbmFsIHZhbHVlcwoJCSAgY29uc3QgRkFyID0gVDByICsgVDJyOwoKCQkgIGNvbnN0IEZCciA9IFQxcjsKCQkgIGNvbnN0IEZCaSA9IC1UM3I7CgoJCSAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoKCQkgIGNvbnN0IEZEciA9IFQxcjsKCQkgIGNvbnN0IEZEaSA9IFQzcjsKCgkJICBvdXRbb3V0T2ZmXSA9IEZBcjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSBGQnI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSBGQmk7CgkJICBvdXRbb3V0T2ZmICsgNF0gPSBGQ3I7CgkJICBvdXRbb3V0T2ZmICsgNV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDZdID0gRkRyOwoJCSAgb3V0W291dE9mZiArIDddID0gRkRpOwoJCX07CgkJcmV0dXJuIGZmdDsKCX0KCgl2YXIgZmZ0RXhwb3J0cyA9IHJlcXVpcmVGZnQoKTsKCXZhciBGRlQgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoZmZ0RXhwb3J0cyk7CgoJY29uc3QgV0VCQVVESU9fQkxPQ0tfU0laRSA9IDEyODsKCWNvbnN0IERFRkFVTFRfQkxPQ0tfU0laRSA9IDEwMjQ7IC8vIERlZmF1bHQgYmxvY2sgc2l6ZSBpZiBub3QgcHJvdmlkZWQgaW4gb3B0aW9ucwoJLyoqIE92ZXJsYXAtQWRkIE5vZGUgKi8KCWNsYXNzIE9MQVByb2Nlc3NvciBleHRlbmRzIEF1ZGlvV29ya2xldFByb2Nlc3NvciB7CgkgICAgbmJJbnB1dHM7CgkgICAgbmJPdXRwdXRzOwoJICAgIGJsb2NrU2l6ZTsKCSAgICBob3BTaXplOwoJICAgIG5iT3ZlcmxhcHM7CgkgICAgaW5wdXRCdWZmZXJzID0gW107CgkgICAgaW5wdXRCdWZmZXJzSGVhZCA9IFtdOwoJICAgIGlucHV0QnVmZmVyc1RvU2VuZCA9IFtdOwoJICAgIG91dHB1dEJ1ZmZlcnMgPSBbXTsKCSAgICBvdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSA9IFtdOwoJICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKCSAgICAgICAgc3VwZXIob3B0aW9ucyk7CgkgICAgICAgIHRoaXMubmJJbnB1dHMgPSBvcHRpb25zPy5udW1iZXJPZklucHV0cyB8fCAxOwoJICAgICAgICB0aGlzLm5iT3V0cHV0cyA9IG9wdGlvbnM/Lm51bWJlck9mT3V0cHV0cyB8fCAxOwoJICAgICAgICAvLyBwcm9jZXNzb3JPcHRpb25zIGlzIHR5cGVkIGB1bmtub3duYCBhdCB0aGUgYW1iaWVudCBib3VuZGFyeTsgbmFycm93IGhlcmUuCgkgICAgICAgIGNvbnN0IHByb2NPcHRzID0gKG9wdGlvbnM/LnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwoJICAgICAgICB0aGlzLmJsb2NrU2l6ZSA9IHByb2NPcHRzLmJsb2NrU2l6ZSB8fCBERUZBVUxUX0JMT0NLX1NJWkU7CgkgICAgICAgIHRoaXMuaG9wU2l6ZSA9IFdFQkFVRElPX0JMT0NLX1NJWkU7CgkgICAgICAgIHRoaXMubmJPdmVybGFwcyA9IE1hdGguZmxvb3IodGhpcy5ibG9ja1NpemUgLyB0aGlzLmhvcFNpemUpOwoJICAgICAgICB0aGlzLmluaXRpYWxpemVCdWZmZXJzKCk7CgkgICAgfQoJICAgIGluaXRpYWxpemVCdWZmZXJzKCkgewoJICAgICAgICB0aGlzLmlucHV0QnVmZmVycyA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZCA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzID0gbmV3IEFycmF5KHRoaXMubmJPdXRwdXRzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSA9IG5ldyBBcnJheSh0aGlzLm5iT3V0cHV0cyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmFsbG9jYXRlSW5wdXRDaGFubmVscyhpLCAxKTsKCSAgICAgICAgfQoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVPdXRwdXRDaGFubmVscyhpLCAxKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICBhbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaW5wdXRJbmRleCwgbmJDaGFubmVscykgewoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYkNoYW5uZWxzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSArIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0uZmlsbCgwKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpbnB1dEluZGV4XVtpXSA9IHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldLnN1YmFycmF5KDAsIHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2lucHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgYWxsb2NhdGVPdXRwdXRDaGFubmVscyhvdXRwdXRJbmRleCwgbmJDaGFubmVscykgewoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW291dHB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYkNoYW5uZWxzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF1baV0uZmlsbCgwKTsKCSAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbb3V0cHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgcmVhbGxvY2F0ZUNoYW5uZWxzSWZOZWVkZWQoaW5wdXRzLCBvdXRwdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBuYkNoYW5uZWxzID0gaW5wdXRzW2ldLmxlbmd0aDsKCSAgICAgICAgICAgIGlmIChuYkNoYW5uZWxzICE9PSB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGgpIHsKCSAgICAgICAgICAgICAgICB0aGlzLmFsbG9jYXRlSW5wdXRDaGFubmVscyhpLCBuYkNoYW5uZWxzKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IG5iQ2hhbm5lbHMgPSBvdXRwdXRzW2ldLmxlbmd0aDsKCSAgICAgICAgICAgIGlmIChuYkNoYW5uZWxzICE9PSB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZU91dHB1dENoYW5uZWxzKGksIG5iQ2hhbm5lbHMpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHJlYWRJbnB1dHMoaW5wdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgY29uc3Qgd2ViQXVkaW9CbG9jayA9IGlucHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpXVtqXS5zZXQod2ViQXVkaW9CbG9jaywgdGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHdyaXRlT3V0cHV0cyhvdXRwdXRzKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCB3ZWJBdWRpb0Jsb2NrID0gb3V0cHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICB3ZWJBdWRpb0Jsb2NrLnNldCh0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uc3ViYXJyYXkoMCwgV0VCQVVESU9fQkxPQ0tfU0laRSkpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHNoaWZ0SW5wdXRCdWZmZXJzKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2ldW2pdLmNvcHlXaXRoaW4oMCwgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgc2hpZnRPdXRwdXRCdWZmZXJzKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLmNvcHlXaXRoaW4oMCwgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLmZpbGwoMCwgdGhpcy5ibG9ja1NpemUgLSBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBwcmVwYXJlSW5wdXRCdWZmZXJzVG9TZW5kKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2ldW2pdLnNldCh0aGlzLmlucHV0QnVmZmVyc0hlYWRbaV1bal0pOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIGhhbmRsZU91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlKCkgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCB0aGlzLmJsb2NrU2l6ZTsgaysrKSB7CgkgICAgICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXVtrXSArPSB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW2ldW2pdW2tdIC8gdGhpcy5uYk92ZXJsYXBzOwoJICAgICAgICAgICAgICAgIH0KCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICB0aGlzLnJlYWxsb2NhdGVDaGFubmVsc0lmTmVlZGVkKGlucHV0cywgb3V0cHV0cyk7CgkgICAgICAgIHRoaXMucmVhZElucHV0cyhpbnB1dHMpOwoJICAgICAgICB0aGlzLnNoaWZ0SW5wdXRCdWZmZXJzKCk7CgkgICAgICAgIHRoaXMucHJlcGFyZUlucHV0QnVmZmVyc1RvU2VuZCgpOwoJICAgICAgICB0aGlzLnByb2Nlc3NPTEEodGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmQsIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmUsIHBhcmFtZXRlcnMpOwoJICAgICAgICB0aGlzLmhhbmRsZU91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlKCk7CgkgICAgICAgIHRoaXMud3JpdGVPdXRwdXRzKG91dHB1dHMpOwoJICAgICAgICB0aGlzLnNoaWZ0T3V0cHV0QnVmZmVycygpOwoJICAgICAgICByZXR1cm4gdHJ1ZTsKCSAgICB9Cgl9CgoJLyoKCSAqIFBoYXNlLXZvY29kZXIgY29yZSDigJQgY29udGV4dC1mcmVlLCB1bml0LXRlc3RhYmxlIERTUCBmb3IgcGVhay1iYXNlZAoJICogcGl0Y2gtc2hpZnRpbmcgd2l0aCBJZGVudGl0eSBQaGFzZS1Mb2NraW5nLgoJICoKCSAqIEFsZ29yaXRobTogSmVhbiBMYXJvY2hlICYgTWFyayBEb2xzb24sICJOZXcgUGhhc2UtVm9jb2RlciBUZWNobmlxdWVzIGZvcgoJICogUGl0Y2gtU2hpZnRpbmcsIEhhcm1vbml6aW5nIGFuZCBPdGhlciBFeG90aWMgRWZmZWN0cyIsIFByb2MuIDE5OTkgSUVFRQoJICogV0FTUEFBLCBOZXcgUGFsdHosIE5ZLiBUaGUgcGl0Y2gtc2hpZnQgZGV0ZWN0cyBzcGVjdHJhbCBwZWFrcywgZGl2aWRlcyB0aGUKCSAqIGZyZXF1ZW5jeSBheGlzIGludG8gcGVyLXBlYWsgInJlZ2lvbnMgb2YgaW5mbHVlbmNlIiwgYW5kIHJpZ2lkbHkgdHJhbnNsYXRlcwoJICogZWFjaCByZWdpb24gdG8gdGhlIHBlYWsncyBzaGlmdGVkIGZyZXF1ZW5jeSAoU2VjdGlvbiAzLjEtMy40KS4KCSAqCgkgKiBUaGUgcGhhc2UgaGFuZGxpbmcgaXMgdGhlICJJZGVudGl0eSBQaGFzZS1Mb2NraW5nIiBydWxlIG9mIExhcm9jaGUtRG9sc29uCgkgKiAxOTk5IFNlY3Rpb24gMy41OiB0byBtYWludGFpbiBmcmFtZS10by1mcmFtZSAoaG9yaXpvbnRhbCkgcGhhc2UgY29oZXJlbmNlCgkgKiBhZnRlciBzaGlmdGluZyBhIHBlYWsgYnkgRGVsdGEtb21lZ2EsIEVWRVJZIGZyZXF1ZW5jeSBiaW4gaW4gdGhhdCBwZWFrJ3MKCSAqIHJlZ2lvbiBvZiBpbmZsdWVuY2UgaXMgbXVsdGlwbGllZCBieSBhIFNJTkdMRSBjb21wbGV4IG51bWJlcgoJICoKCSAqICAgICBaX3UgPSBleHAoaiAqIERlbHRhLW9tZWdhICogUikgICAgICAgICAgICAoTGFyb2NoZS1Eb2xzb24gMTk5OSwgZXEuIHAuMykKCSAqCgkgKiAoUiA9IGhvcCBzaXplKSwgYW5kIHRoZSByb3RhdGlvbiBpcyBjdW11bGF0ZWQgZnJhbWUgdG8gZnJhbWUKCSAqCgkgKiAgICAgWl97dSsxfSA9IFpfdSAqIGV4cChqICogRGVsdGEtb21lZ2Ffe3UrMX0gKiBSKS4KCSAqCgkgKiBCZWNhdXNlIGFsbCBiaW5zIGluIGEgcmVnaW9uIGFyZSByb3RhdGVkIGJ5IHRoZSBTQU1FIGFuZ2xlLCB0aGUgcGhhc2UKCSAqIHJlbGF0aW9uc2hpcHMgQkVUV0VFTiBiaW5zIGFyb3VuZCBhIHBlYWsgKHRoZSB2ZXJ0aWNhbCAvIGludHJhLXBlYWsKCSAqIGNvaGVyZW5jZSB0aGF0IGlkZW50aWZpZXMgdGhlIHNpbnVzb2lkKSBhcmUgUFJFU0VSVkVEIGFjcm9zcyB0aGUgbW92ZSDigJQgdGhpcwoJICogaXMgZXhhY3RseSB3aGF0IHJlbW92ZXMgdGhlICJwaGFzaW5lc3MiIGFydGlmYWN0IG9mIHRoZSBuYWl2ZSBiaW4taW5kZXBlbmRlbnQKCSAqIHBoYXNlIHZvY29kZXIuIE5vIGtub3dsZWRnZSBvZiB0aGUgdHJ1ZSBwZWFrIGZyZXF1ZW5jeSBvbWVnYSBpcyBuZWVkZWQsIHNvIG5vCgkgKiBhcmN0YW5nZW50IGFuZCBubyBwaGFzZS11bndyYXBwaW5nIChMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy41KS4KCSAqCgkgKiBUaGlzIG1vZHVsZSBtaXJyb3JzIHRoZSBwcm9qZWN0J3MgY29yZS9zaGVsbCBzcGxpdCAoY2YuIHdhdmVzaGFwZXItY29yZS50cywKCSAqIGR5bmFtaWNzLWNvcmUudHMpOiB0aGUgRkZUIGZyYW1pbmcgYW5kIHdvcmtsZXQgcGx1bWJpbmcgbGl2ZSBpbiB0aGUKCSAqIEF1ZGlvV29ya2xldFByb2Nlc3NvciBzaGVsbCAocGhhc2Utdm9jb2Rlci50cyk7IHRoZSBzcGVjdHJ1bSBtYW5pcHVsYXRpb24KCSAqIHRoYXQgY2FycmllcyB0aGUgdGVzdGFibGUgaW52YXJpYW50cyBsaXZlcyBoZXJlLgoJICovCgkvKioKCSAqIFNxdWFyZWQtbWFnbml0dWRlIHNwZWN0cnVtIGZyb20gYW4gaW50ZXJsZWF2ZWQgW3JlLCBpbSwgcmUsIGltLCAuLi5dIGNvbXBsZXgKCSAqIGJ1ZmZlci4gYG1hZ25pdHVkZXNbaV1gIGNvcnJlc3BvbmRzIHRvIGJpbiBgaWAuIFdyaXRlcyBpbnRvIGBvdXRgIChsZW5ndGgKCSAqIGZmdFNpemUvMiArIDEpIHRvIGF2b2lkIHBlci1jYWxsIGFsbG9jYXRpb24gaW4gdGhlIHdvcmtsZXQuCgkgKi8KCWZ1bmN0aW9uIGNvbXB1dGVNYWduaXR1ZGVzKGNvbXBsZXgsIG91dCkgewoJICAgIGZvciAobGV0IGkgPSAwLCBqID0gMDsgaSA8IG91dC5sZW5ndGg7IGkrKywgaiArPSAyKSB7CgkgICAgICAgIGNvbnN0IHJlYWwgPSBjb21wbGV4W2pdOwoJICAgICAgICBjb25zdCBpbWFnID0gY29tcGxleFtqICsgMV07CgkgICAgICAgIG91dFtpXSA9IHJlYWwgKiogMiArIGltYWcgKiogMjsKCSAgICB9Cgl9CgkvKioKCSAqIFBlYWsgZGV0ZWN0aW9uIHBlciBMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy4yOiBhIGJpbiBpcyBhIHBlYWsgaWZmIGl0cwoJICogbWFnbml0dWRlIHN0cmljdGx5IGV4Y2VlZHMgaXRzIHR3byBuZWFyZXN0IG5laWdoYm91cnMgb24gZWFjaCBzaWRlLiBXcml0ZXMKCSAqIHRoZSBwZWFrIGJpbiBpbmRpY2VzIGludG8gYHBlYWtJbmRleGVzYCBhbmQgcmV0dXJucyB0aGUgY291bnQuCgkgKi8KCWZ1bmN0aW9uIGZpbmRQZWFrcyhtYWduaXR1ZGVzLCBwZWFrSW5kZXhlcykgewoJICAgIGxldCBuYlBlYWtzID0gMDsKCSAgICBmb3IgKGxldCBpID0gMiwgZW5kID0gbWFnbml0dWRlcy5sZW5ndGggLSAyOyBpIDwgZW5kOyBpKyspIHsKCSAgICAgICAgY29uc3QgbWFnID0gbWFnbml0dWRlc1tpXTsKCSAgICAgICAgaWYgKG1hZ25pdHVkZXNbaSAtIDFdID49IG1hZyB8fCBtYWduaXR1ZGVzW2kgLSAyXSA+PSBtYWcgfHwgbWFnbml0dWRlc1tpICsgMV0gPj0gbWFnIHx8IG1hZ25pdHVkZXNbaSArIDJdID49IG1hZykgewoJICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgIH0KCSAgICAgICAgcGVha0luZGV4ZXNbbmJQZWFrcysrXSA9IGk7CgkgICAgfQoJICAgIHJldHVybiBuYlBlYWtzOwoJfQoJLyoqCgkgKiBUaGUgcGVyLWZyYW1lIHBoYXNlIGluY3JlbWVudCBleHAoaiAqIERlbHRhLW9tZWdhICogUikgZm9yIE9ORSBwZWFrIHNoaWZ0ZWQKCSAqIGJ5IERlbHRhLW9tZWdhIG92ZXIgYSBob3Agb2YgUiBzeW50aGVzaXMgc2FtcGxlcy4gTGFyb2NoZS1Eb2xzb24gMTk5OSBlcS4KCSAqIHAuMyAoU2VjdGlvbiAzLjUpLCB0aGUgc2luZ2xlIGNvbXBsZXggbnVtYmVyIGFwcGxpZWQgdW5pZm9ybWx5IHRvIHRoZSBwZWFrJ3MKCSAqIHdob2xlIHJlZ2lvbiBvZiBpbmZsdWVuY2UuCgkgKgoJICogRGVsdGEtb21lZ2EgKHJhZC9zYW1wbGUpIGlzIHRoZSBmcmVxdWVuY3kgc2hpZnQgdGhlIHBlYWsgdW5kZXJnb2VzOgoJICogICBEZWx0YS1vbWVnYSA9IDIqcGkgKiAocGVha0luZGV4U2hpZnRlZCAtIHBlYWtJbmRleCkgLyBmZnRTaXplLgoJICoKCSAqIFRoaXMgaXMgdGhlIHBlci1mcmFtZSBmYWN0b3IsIE5PVCB0aGUgY3VtdWxhdGl2ZSByb3RhdG9yLiBDcm9zcy1mcmFtZQoJICogY3VtdWxhdGlvbiBaX3t1KzF9ID0gWl91ICogZXhwKGoqRGVsdGEtb21lZ2Ffe3UrMX0qUikgaXMgdGhlIGpvYiBvZgoJICoge0BsaW5rIFBlYWtSb3RhdG9yU3RhdGV9OyB0aGlzIGZ1bmN0aW9uIHByb2R1Y2VzIHRoZSBgZXhwKC4uLilgIGZhY3RvciB0aGF0CgkgKiBzdGF0ZSBtdWx0aXBsaWVzIGluIGVhY2ggZnJhbWUuCgkgKi8KCWZ1bmN0aW9uIGZyYW1lUm90YXRpb24ocGVha0luZGV4LCBwZWFrSW5kZXhTaGlmdGVkLCBmZnRTaXplLCBob3ApIHsKCSAgICBjb25zdCBvbWVnYURlbHRhID0gKDIgKiBNYXRoLlBJICogKHBlYWtJbmRleFNoaWZ0ZWQgLSBwZWFrSW5kZXgpKSAvIGZmdFNpemU7CgkgICAgY29uc3QgYW5nbGUgPSBvbWVnYURlbHRhICogaG9wOwoJICAgIHJldHVybiB7IHJlOiBNYXRoLmNvcyhhbmdsZSksIGltOiBNYXRoLnNpbihhbmdsZSkgfTsKCX0KCS8qKgoJICogQ3Jvc3MtZnJhbWUgY3VtdWxhdGl2ZSBwaGFzZS1sb2NrIHN0YXRlIChMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy41KS4KCSAqCgkgKiBUaGUgcGFwZXIgcmVxdWlyZXMgdGhlIHBlci1wZWFrIHJvdGF0aW9uIGJlIEFDQ1VNVUxBVEVEIGZyYW1lIHRvIGZyYW1lOgoJICoKCSAqICAgICBaX3t1KzF9ID0gWl91ICogZXhwKGogKiBEZWx0YS1vbWVnYV97dSsxfSAqIFIpCgkgKgoJICogd2l0aCBEZWx0YS1vbWVnYSBhbGxvd2VkIHRvIHZhcnkgcGVyIGZyYW1lIChhdXRvbWF0ZWQgLyB0aW1lLXZhcnlpbmcgcGl0Y2gpLgoJICogQSBuYWl2ZSBgb21lZ2FEZWx0YSAqIGVsYXBzZWRUaW1lYCByb3RhdG9yIGlzIHdyb25nOiB3aGVuIHRoZSBzaGlmdCBjaGFuZ2VzLAoJICogaXQgcmV0cm9hY3RpdmVseSByZS1waGFzZXMgZXZlcnkgcHJpb3IgZnJhbWUgYW5kIHByb2R1Y2VzIGEgZGlzY29udGludWl0eS4KCSAqCgkgKiBUaGlzIHN0YXRlIGtlZXBzIG9uZSBjdW11bGF0aXZlIHJvdGF0b3IgWl91IFBFUiBQRUFLLCBrZXllZCBieSB0aGUgcGVhaydzCgkgKiBzb3VyY2UgYmluIGluZGV4LiB7QGxpbmsgUGVha1JvdGF0b3JTdGF0ZS5hZHZhbmNlfSBtdWx0aXBsaWVzIGVhY2ggcGVhaydzIFoKCSAqIGJ5IHRoaXMgZnJhbWUncyBleHAoaipEZWx0YS1vbWVnYSpSKSAoc28gaGlzdG9yeSBpcyBwcmVzZXJ2ZWQgYWNyb3NzIHBpdGNoCgkgKiBjaGFuZ2VzKSwgYW5kIHtAbGluayBQZWFrUm90YXRvclN0YXRlLmdldH0gcmV0dXJucyB0aGUgY3VycmVudCBjdW11bGF0aXZlCgkgKiByb3RhdG9yIHRvIGFwcGx5IHRvIHRoYXQgcGVhaydzIHJlZ2lvbiBvZiBpbmZsdWVuY2UuCgkgKi8KCWNsYXNzIFBlYWtSb3RhdG9yU3RhdGUgewoJICAgIC8qKiBwZWFrIHNvdXJjZS1iaW4gaW5kZXggLT4gY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSAodW5pdCBtb2R1bHVzKS4gKi8KCSAgICByb3RhdG9ycyA9IG5ldyBNYXAoKTsKCSAgICAvKiogc2NyYXRjaCBzZXQgb2YgYmlucyBzZWVuIHRoaXMgZnJhbWUsIGZvciBwcnVuaW5nIHZhbmlzaGVkIHBlYWtzLiAqLwoJICAgIHNlZW4gPSBuZXcgU2V0KCk7CgkgICAgLyoqCgkgICAgICogQWR2YW5jZSBldmVyeSBjdXJyZW50bHktZGV0ZWN0ZWQgcGVhaydzIGN1bXVsYXRpdmUgcm90YXRvciBieSB0aGlzIGZyYW1lJ3MKCSAgICAgKiBleHAoaipEZWx0YS1vbWVnYSpSKS4gTmV3IHBlYWtzIHN0YXJ0IGF0IFogPSAxIChubyByb3RhdGlvbikgdGhlbiB0YWtlIHRoaXMKCSAgICAgKiBmcmFtZSdzIGluY3JlbWVudDsgcGVha3Mgbm90IHByZXNlbnQgdGhpcyBmcmFtZSBhcmUgZHJvcHBlZCBzbyB0aGVpciBzdGFsZQoJICAgICAqIHBoYXNlIGRvZXMgbm90IGxlYWsgaW50byBhIGxhdGVyIHJlLWRldGVjdGlvbi4KCSAgICAgKgoJICAgICAqIGBwaXRjaEZhY3RvcmAgYW5kIGBob3BgIGRlZmluZSB0aGlzIGZyYW1lJ3MgcGVyLXBlYWsgRGVsdGEtb21lZ2EgdmlhIHRoZQoJICAgICAqIHNoaWZ0ZWQgYmluIGByb3VuZChwZWFrSW5kZXggKiBwaXRjaEZhY3RvcilgLgoJICAgICAqLwoJICAgIGFkdmFuY2UocGVha0luZGV4ZXMsIG5iUGVha3MsIGZmdFNpemUsIHBpdGNoRmFjdG9yLCBob3ApIHsKCSAgICAgICAgdGhpcy5zZWVuLmNsZWFyKCk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJQZWFrczsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBwZWFrSW5kZXggPSBwZWFrSW5kZXhlc1tpXTsKCSAgICAgICAgICAgIHRoaXMuc2Vlbi5hZGQocGVha0luZGV4KTsKCSAgICAgICAgICAgIGNvbnN0IHBlYWtJbmRleFNoaWZ0ZWQgPSBNYXRoLnJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKTsKCSAgICAgICAgICAgIGNvbnN0IGluYyA9IGZyYW1lUm90YXRpb24ocGVha0luZGV4LCBwZWFrSW5kZXhTaGlmdGVkLCBmZnRTaXplLCBob3ApOwoJICAgICAgICAgICAgY29uc3QgcHJldiA9IHRoaXMucm90YXRvcnMuZ2V0KHBlYWtJbmRleCkgPz8geyByZTogMSwgaW06IDAgfTsKCSAgICAgICAgICAgIC8vIFpfe3UrMX0gPSBaX3UgKiBpbmMgIChjb21wbGV4IG11bHRpcGx5KQoJICAgICAgICAgICAgdGhpcy5yb3RhdG9ycy5zZXQocGVha0luZGV4LCB7CgkgICAgICAgICAgICAgICAgcmU6IHByZXYucmUgKiBpbmMucmUgLSBwcmV2LmltICogaW5jLmltLAoJICAgICAgICAgICAgICAgIGltOiBwcmV2LnJlICogaW5jLmltICsgcHJldi5pbSAqIGluYy5yZSwKCSAgICAgICAgICAgIH0pOwoJICAgICAgICB9CgkgICAgICAgIC8vIFBydW5lIHBlYWtzIHRoYXQgZGlzYXBwZWFyZWQgdGhpcyBmcmFtZS4KCSAgICAgICAgZm9yIChjb25zdCBrZXkgb2YgdGhpcy5yb3RhdG9ycy5rZXlzKCkpIHsKCSAgICAgICAgICAgIGlmICghdGhpcy5zZWVuLmhhcyhrZXkpKQoJICAgICAgICAgICAgICAgIHRoaXMucm90YXRvcnMuZGVsZXRlKGtleSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgLyoqIFRoZSBjdXJyZW50IGN1bXVsYXRpdmUgcm90YXRvciBaX3UgZm9yIGEgcGVhayAoaWRlbnRpdHkgaWYgdW5zZWVuKS4gKi8KCSAgICBnZXQocGVha0luZGV4KSB7CgkgICAgICAgIHJldHVybiB0aGlzLnJvdGF0b3JzLmdldChwZWFrSW5kZXgpID8/IHsgcmU6IDEsIGltOiAwIH07CgkgICAgfQoJICAgIC8qKiBSZXNldCBhbGwgYWNjdW11bGF0ZWQgcGhhc2UgKGUuZy4gb24gc3RvcCAvIHJlLXNlZWspLiAqLwoJICAgIHJlc2V0KCkgewoJICAgICAgICB0aGlzLnJvdGF0b3JzLmNsZWFyKCk7CgkgICAgICAgIHRoaXMuc2Vlbi5jbGVhcigpOwoJICAgIH0KCSAgICAvKiogTnVtYmVyIG9mIHBlYWtzIGN1cnJlbnRseSB0cmFja2VkICh0ZXN0L2ludHJvc3BlY3Rpb24gaGVscGVyKS4gKi8KCSAgICBnZXQgc2l6ZSgpIHsKCSAgICAgICAgcmV0dXJuIHRoaXMucm90YXRvcnMuc2l6ZTsKCSAgICB9Cgl9CgkvKioKCSAqIEhhbGYtd2F5IHJlZ2lvbi1vZi1pbmZsdWVuY2UgYm91bmRhcmllcyBmb3IgcGVhayBgaWAgKExhcm9jaGUtRG9sc29uIDE5OTkKCSAqIFNlY3Rpb24gMy4yIOKAlCBib3VuZGFyeSBzZXQgbWlkd2F5IGJldHdlZW4gYWRqYWNlbnQgcGVha3MpLiBSZXR1cm5zIHRoZQoJICogW3N0YXJ0SW5kZXgsIGVuZEluZGV4KSBiaW4gcmFuZ2Ugb3duZWQgYnkgdGhpcyBwZWFrLgoJICoKCSAqIFRoZSBMQVNUIHBlYWsncyByZWdpb24gbXVzdCBlbmQgYXQgdGhlIG5vbi1yZWR1bmRhbnQgaGFsZi1zcGVjdHJ1bSBsZW5ndGgKCSAqIGBtYWduaXR1ZGVzTGVuZ3RoYCAoPSBmZnRTaXplLzIgKyAxKSwgTk9UIGF0IGBmZnRTaXplYC4gVGhlIGFuYWx5c2VkIHNwZWN0cnVtCgkgKiBvbmx5IGNhcnJpZXMgYmlucyBbMCwgZmZ0U2l6ZS8yXTsgdGhlIHVwcGVyIGhhbGYgaXMgdGhlIGNvbmp1Z2F0ZS1zeW1tZXRyaWMKCSAqIG1pcnJvciBmaWxsZWQgbGF0ZXIgYnkgYGNvbXBsZXRlU3BlY3RydW1gLiBSZWFkaW5nIHNvdXJjZSBiaW5zIHBhc3QgTnlxdWlzdAoJICogZm9sZHMgc3RhbGUgLyBub3QteWV0LXBvcHVsYXRlZCBuZWdhdGl2ZS1mcmVxdWVuY3kgZGF0YSBpbnRvIHRoZSBvdXRwdXQsIHNvCgkgKiB0aGUgcmVnaW9uIGlzIGNsYW1wZWQgdG8gdGhlIGFuYWx5c2VkIHBvc2l0aXZlIHNwZWN0cnVtLgoJICovCglmdW5jdGlvbiByZWdpb25PZkluZmx1ZW5jZShwZWFrSW5kZXhlcywgaSwgbmJQZWFrcywgbWFnbml0dWRlc0xlbmd0aCkgewoJICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgIGNvbnN0IHN0YXJ0SW5kZXggPSBpID4gMCA/IHBlYWtJbmRleCAtIE1hdGguZmxvb3IoKHBlYWtJbmRleCAtIHBlYWtJbmRleGVzW2kgLSAxXSkgLyAyKSA6IDA7CgkgICAgY29uc3QgZW5kSW5kZXggPSBpIDwgbmJQZWFrcyAtIDEgPyBwZWFrSW5kZXggKyBNYXRoLmNlaWwoKHBlYWtJbmRleGVzW2kgKyAxXSAtIHBlYWtJbmRleCkgLyAyKSA6IG1hZ25pdHVkZXNMZW5ndGg7CgkgICAgcmV0dXJuIHsgc3RhcnRJbmRleCwgZW5kSW5kZXggfTsKCX0KCS8qKgoJICogUGVhay1zaGlmdCB3aXRoIElkZW50aXR5IFBoYXNlLUxvY2tpbmcgKExhcm9jaGUtRG9sc29uIDE5OTksIFNlY3Rpb25zIDMuNCAmCgkgKiAzLjUpLiBGb3IgZWFjaCBkZXRlY3RlZCBwZWFrOgoJICogIC0gY29tcHV0ZSB0aGUgc2hpZnRlZCBwZWFrIGJpbiAocGVha0luZGV4ICogcGl0Y2hGYWN0b3IsIHJvdW5kZWQg4oCUIHRoZQoJICogICAgaW50ZWdlci1iaW4gY2FzZSBvZiBTZWN0aW9uIDMuNCwgYSBsb3NzbGVzcyByZWdpb24gY29weSksCgkgKiAgLSBjb21wdXRlIE9ORSByb3RhdG9yIFpfdSA9IGV4cChqKkRlbHRhLW9tZWdhKnRpbWVDdXJzb3IpIGZvciB0aGUgcGVhawoJICogICAgKGBwZWFrUm90YXRvcmApLAoJICogIC0gcmlnaWRseSB0cmFuc2xhdGUgdGhlIHBlYWsncyByZWdpb24gb2YgaW5mbHVlbmNlIHRvIHRoZSBzaGlmdGVkIGxvY2F0aW9uLAoJICogICAgbXVsdGlwbHlpbmcgRVZFUlkgYmluIGluIHRoZSByZWdpb24gYnkgdGhhdCBTQU1FIFpfdS4KCSAqIE92ZXJsYXBwaW5nIHNoaWZ0ZWQgcmVnaW9ucyBhcmUgc3VtbWVkIChTZWN0aW9uIDMuNCkuCgkgKgoJICogYGNvbXBsZXhgIGlzIHRoZSBhbmFseXNpcyBzcGVjdHJ1bSAoaW50ZXJsZWF2ZWQgcmUvaW0pLiBgc2hpZnRlZGAgaXMgdGhlCgkgKiBvdXRwdXQgc3BlY3RydW0gKGludGVybGVhdmVkIHJlL2ltKSBhbmQgaXMgemVyby1maWxsZWQgaGVyZSBiZWZvcmUKCSAqIGFjY3VtdWxhdGlvbi4gYG1hZ25pdHVkZXNMZW5ndGhgID0gZmZ0U2l6ZS8yICsgMSAodGhlIG5vbi1yZWR1bmRhbnQgYmluCgkgKiBjb3VudCk7IGJpbnMgYmV5b25kIGl0IGFyZSBza2lwcGVkIC8gdGVybWluYXRlIHRoZSBwZWFrIGxvb3AsIG1hdGNoaW5nIHRoZQoJICogd29ya2xldCdzIHByZS1gY29tcGxldGVTcGVjdHJ1bWAgaGFsZi1zcGVjdHJ1bS4KCSAqLwoJZnVuY3Rpb24gc2hpZnRQZWFrcyhjb21wbGV4LCBzaGlmdGVkLCBwZWFrSW5kZXhlcywgbmJQZWFrcywgZmZ0U2l6ZSwgbWFnbml0dWRlc0xlbmd0aCwgcGl0Y2hGYWN0b3IsIHJvdGF0b3JzKSB7CgkgICAgc2hpZnRlZC5maWxsKDApOwoJICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJQZWFrczsgaSsrKSB7CgkgICAgICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgICAgICBjb25zdCBwZWFrSW5kZXhTaGlmdGVkID0gTWF0aC5yb3VuZChwZWFrSW5kZXggKiBwaXRjaEZhY3Rvcik7CgkgICAgICAgIGNvbnN0IHsgc3RhcnRJbmRleCwgZW5kSW5kZXggfSA9IHJlZ2lvbk9mSW5mbHVlbmNlKHBlYWtJbmRleGVzLCBpLCBuYlBlYWtzLCBtYWduaXR1ZGVzTGVuZ3RoKTsKCSAgICAgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBJZGVudGl0eSBQaGFzZS1Mb2NraW5nOiBPTkUgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdQoJICAgICAgICAvLyBwZXIgcGVhaywgYXBwbGllZCB1bmlmb3JtbHkgdG8gdGhlIHdob2xlIHJlZ2lvbiBvZiBpbmZsdWVuY2UgKG9uZSBjb21wbGV4CgkgICAgICAgIC8vIG11bHRpcGx5IHBlciBiaW4pLCBwcmVzZXJ2aW5nIHRoZSBpbnRyYS1yZWdpb24gcGhhc2UgcmVsYXRpb25zaGlwcy4gWl91CgkgICAgICAgIC8vIGlzIGFjY3VtdWxhdGVkIGZyYW1lLXRvLWZyYW1lIGJ5IFBlYWtSb3RhdG9yU3RhdGUg4oCUIG5vdCByZWNvbXB1dGVkIGZyb20KCSAgICAgICAgLy8gYWJzb2x1dGUgdGltZSDigJQgc28gYSBjaGFuZ2luZyBwaXRjaEZhY3RvciBkb2VzIG5vdCByZXRyb2FjdGl2ZWx5IHJlcGhhc2UuCgkgICAgICAgIGNvbnN0IHJvdCA9IHJvdGF0b3JzLmdldChwZWFrSW5kZXgpOwoJICAgICAgICBmb3IgKGxldCBqID0gc3RhcnRJbmRleCAtIHBlYWtJbmRleDsgaiA8IGVuZEluZGV4IC0gcGVha0luZGV4OyBqKyspIHsKCSAgICAgICAgICAgIGNvbnN0IGJpbkluZGV4ID0gcGVha0luZGV4ICsgajsKCSAgICAgICAgICAgIGNvbnN0IGJpbkluZGV4U2hpZnRlZCA9IHBlYWtJbmRleFNoaWZ0ZWQgKyBqOwoJICAgICAgICAgICAgaWYgKGJpbkluZGV4U2hpZnRlZCA+PSBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgYnJlYWs7CgkgICAgICAgICAgICB9CgkgICAgICAgICAgICAvLyBTb3VyY2UgYmlucyBhcmUgb25seSB2YWxpZCBpbnNpZGUgdGhlIGFuYWx5c2VkIHBvc2l0aXZlIHNwZWN0cnVtLgoJICAgICAgICAgICAgaWYgKGJpbkluZGV4IDwgMCB8fCBiaW5JbmRleCA+PSBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgICAgICB9CgkgICAgICAgICAgICBjb25zdCBpbmRleFJlYWwgPSBiaW5JbmRleCAqIDI7CgkgICAgICAgICAgICBjb25zdCBpbmRleEltYWcgPSBpbmRleFJlYWwgKyAxOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVSZWFsID0gY29tcGxleFtpbmRleFJlYWxdOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVJbWFnID0gY29tcGxleFtpbmRleEltYWddOwoJICAgICAgICAgICAgLy8gQ29tcGxleCBtdWx0aXBseSBieSB0aGUgc2luZ2xlIHBlci1wZWFrIGN1bXVsYXRpdmUgcm90YXRvciBaX3UuCgkgICAgICAgICAgICBjb25zdCB2YWx1ZVNoaWZ0ZWRSZWFsID0gdmFsdWVSZWFsICogcm90LnJlIC0gdmFsdWVJbWFnICogcm90LmltOwoJICAgICAgICAgICAgY29uc3QgdmFsdWVTaGlmdGVkSW1hZyA9IHZhbHVlUmVhbCAqIHJvdC5pbSArIHZhbHVlSW1hZyAqIHJvdC5yZTsKCSAgICAgICAgICAgIGlmIChiaW5JbmRleFNoaWZ0ZWQgPCAwKSB7CgkgICAgICAgICAgICAgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNDogYSByZWdpb24gb2YgaW5mbHVlbmNlIHNwaWxsaW5nIG9udG8KCSAgICAgICAgICAgICAgICAvLyB0aGUgTkVHQVRJVkUtZnJlcXVlbmN5IGF4aXMgaXMgcmVmbGVjdGVkIGJhY2sgaW50byB0aGUgcG9zaXRpdmUKCSAgICAgICAgICAgICAgICAvLyBmcmVxdWVuY2llcyB3aXRoIENPTVBMRVggQ09OSlVHQVRJT04sIGJlY2F1c2UgdGhlIG9yaWdpbmFsIHNpZ25hbCBpcwoJICAgICAgICAgICAgICAgIC8vIHJlYWwgKEhlcm1pdGlhbiBzeW1tZXRyeSBYKC13KSA9IGNvbmooWCh3KSkpLiBXaXRob3V0IHRoaXMgdGhlIGVuZXJneQoJICAgICAgICAgICAgICAgIC8vIGJlbG93IERDIGlzIGRyb3BwZWQgYW5kIGRvd253YXJkIHNoaWZ0cyBsb3NlIHRoZWlyIGxvdyBlbmQuCgkgICAgICAgICAgICAgICAgY29uc3QgcmVmbGVjdGVkID0gLWJpbkluZGV4U2hpZnRlZDsKCSAgICAgICAgICAgICAgICBpZiAocmVmbGVjdGVkID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICAgICAgY29udGludWU7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgICAgIGNvbnN0IHJlZmxSZWFsID0gcmVmbGVjdGVkICogMjsKCSAgICAgICAgICAgICAgICBjb25zdCByZWZsSW1hZyA9IHJlZmxSZWFsICsgMTsKCSAgICAgICAgICAgICAgICBzaGlmdGVkW3JlZmxSZWFsXSArPSB2YWx1ZVNoaWZ0ZWRSZWFsOwoJICAgICAgICAgICAgICAgIHNoaWZ0ZWRbcmVmbEltYWddICs9IC12YWx1ZVNoaWZ0ZWRJbWFnOyAvLyBjb25qdWdhdGUKCSAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIGNvbnN0IGluZGV4U2hpZnRlZFJlYWwgPSBiaW5JbmRleFNoaWZ0ZWQgKiAyOwoJICAgICAgICAgICAgY29uc3QgaW5kZXhTaGlmdGVkSW1hZyA9IGluZGV4U2hpZnRlZFJlYWwgKyAxOwoJICAgICAgICAgICAgc2hpZnRlZFtpbmRleFNoaWZ0ZWRSZWFsXSArPSB2YWx1ZVNoaWZ0ZWRSZWFsOwoJICAgICAgICAgICAgc2hpZnRlZFtpbmRleFNoaWZ0ZWRJbWFnXSArPSB2YWx1ZVNoaWZ0ZWRJbWFnOwoJICAgICAgICB9CgkgICAgfQoJfQoKCWNvbnN0IEJMT0NLX1NJWkUgPSAyMDQ4OwoJZnVuY3Rpb24gaGFubihsZW5ndGgpIHsKCSAgICBjb25zdCBvdXQgPSBuZXcgRmxvYXQzMkFycmF5KGxlbmd0aCk7CgkgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykKCSAgICAgICAgb3V0W2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIGxlbmd0aCkpOwoJICAgIHJldHVybiBvdXQ7Cgl9CgkvKiogVHdvIGFkZGVkIHZvaWNlcyBpbiBvbmUgTGFyb2NoZS1Eb2xzb24gU1RGVCBhbmFseXNpcyBwYXNzLiAqLwoJY2xhc3MgSGFybW9uaXplcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBPTEFQcm9jZXNzb3IgewoJICAgIGZmdDsKCSAgICB3aW5kb3c7CgkgICAgc291cmNlU3BlY3RydW07CgkgICAgdm9pY2VBU3BlY3RydW07CgkgICAgdm9pY2VCU3BlY3RydW07CgkgICAgb3V0cHV0U3BlY3RydW07CgkgICAgdGltZUNvbXBsZXg7CgkgICAgbWFnbml0dWRlczsKCSAgICBwZWFrczsKCSAgICB2b2ljZUFTdGF0ZXMgPSBbXTsKCSAgICB2b2ljZUJTdGF0ZXMgPSBbXTsKCSAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewoJICAgICAgICByZXR1cm4gWwoJICAgICAgICAgICAgeyBuYW1lOiAic2VtaXRvbmVzQSIsIGRlZmF1bHRWYWx1ZTogNywgbWluVmFsdWU6IC0zNiwgbWF4VmFsdWU6IDM2LCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogInNlbWl0b25lc0IiLCBkZWZhdWx0VmFsdWU6IDEyLCBtaW5WYWx1ZTogLTM2LCBtYXhWYWx1ZTogMzYsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICAgICAgeyBuYW1lOiAiZ2FpbkEiLCBkZWZhdWx0VmFsdWU6IDAuNiwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAyLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogImdhaW5CIiwgZGVmYXVsdFZhbHVlOiAwLjQ1LCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICAgICAgeyBuYW1lOiAiZHJ5IiwgZGVmYXVsdFZhbHVlOiAxLCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDIsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAoJICAgICAgICBdOwoJICAgIH0KCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIGNvbnN0IHdvcmtsZXRPcHRpb25zID0gb3B0aW9ucyA/PyB7fTsKCSAgICAgICAgd29ya2xldE9wdGlvbnMucHJvY2Vzc29yT3B0aW9ucyA9IHsgYmxvY2tTaXplOiBCTE9DS19TSVpFLCAuLi4od29ya2xldE9wdGlvbnMucHJvY2Vzc29yT3B0aW9ucyA/PyB7fSkgfTsKCSAgICAgICAgc3VwZXIod29ya2xldE9wdGlvbnMpOwoJICAgICAgICB0aGlzLmZmdCA9IG5ldyBGRlQodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB0aGlzLndpbmRvdyA9IGhhbm4odGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB0aGlzLnNvdXJjZVNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudm9pY2VBU3BlY3RydW0gPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy52b2ljZUJTcGVjdHJ1bSA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudGltZUNvbXBsZXggPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy5tYWduaXR1ZGVzID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSAvIDIgKyAxKTsKCSAgICAgICAgdGhpcy5wZWFrcyA9IG5ldyBJbnQzMkFycmF5KHRoaXMubWFnbml0dWRlcy5sZW5ndGgpOwoJICAgIH0KCSAgICBwcm9jZXNzT0xBKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICBjb25zdCBmYWN0b3JBID0gMiAqKiAocGFyYW1ldGVycy5zZW1pdG9uZXNBW3BhcmFtZXRlcnMuc2VtaXRvbmVzQS5sZW5ndGggLSAxXSAvIDEyKTsKCSAgICAgICAgY29uc3QgZmFjdG9yQiA9IDIgKiogKHBhcmFtZXRlcnMuc2VtaXRvbmVzQltwYXJhbWV0ZXJzLnNlbWl0b25lc0IubGVuZ3RoIC0gMV0gLyAxMik7CgkgICAgICAgIGNvbnN0IGdhaW5BID0gcGFyYW1ldGVycy5nYWluQVtwYXJhbWV0ZXJzLmdhaW5BLmxlbmd0aCAtIDFdOwoJICAgICAgICBjb25zdCBnYWluQiA9IHBhcmFtZXRlcnMuZ2FpbkJbcGFyYW1ldGVycy5nYWluQi5sZW5ndGggLSAxXTsKCSAgICAgICAgY29uc3QgZHJ5ID0gcGFyYW1ldGVycy5kcnlbcGFyYW1ldGVycy5kcnkubGVuZ3RoIC0gMV07CgkgICAgICAgIGNvbnN0IG5vcm0gPSAxIC8gTWF0aC5tYXgoMSwgZHJ5ICsgZ2FpbkEgKyBnYWluQik7CgkgICAgICAgIGZvciAobGV0IGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgaW5wdXRzWzBdLmxlbmd0aDsgY2hhbm5lbCsrKSB7CgkgICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXVtjaGFubmVsXTsKCSAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF1bY2hhbm5lbF07CgkgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlucHV0Lmxlbmd0aDsgaSsrKQoJICAgICAgICAgICAgICAgIGlucHV0W2ldICo9IHRoaXMud2luZG93W2ldOwoJICAgICAgICAgICAgdGhpcy5mZnQucmVhbFRyYW5zZm9ybSh0aGlzLnNvdXJjZVNwZWN0cnVtLCBpbnB1dCk7CgkgICAgICAgICAgICBjb21wdXRlTWFnbml0dWRlcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLm1hZ25pdHVkZXMpOwoJICAgICAgICAgICAgY29uc3QgcGVha0NvdW50ID0gZmluZFBlYWtzKHRoaXMubWFnbml0dWRlcywgdGhpcy5wZWFrcyk7CgkgICAgICAgICAgICBjb25zdCBzdGF0ZUEgPSAodGhpcy52b2ljZUFTdGF0ZXNbY2hhbm5lbF0gPz89IG5ldyBQZWFrUm90YXRvclN0YXRlKCkpOwoJICAgICAgICAgICAgY29uc3Qgc3RhdGVCID0gKHRoaXMudm9pY2VCU3RhdGVzW2NoYW5uZWxdID8/PSBuZXcgUGVha1JvdGF0b3JTdGF0ZSgpKTsKCSAgICAgICAgICAgIHN0YXRlQS5hZHZhbmNlKHRoaXMucGVha3MsIHBlYWtDb3VudCwgdGhpcy5ibG9ja1NpemUsIGZhY3RvckEsIHRoaXMuaG9wU2l6ZSk7CgkgICAgICAgICAgICBzdGF0ZUIuYWR2YW5jZSh0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCBmYWN0b3JCLCB0aGlzLmhvcFNpemUpOwoJICAgICAgICAgICAgc2hpZnRQZWFrcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLnZvaWNlQVNwZWN0cnVtLCB0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCB0aGlzLm1hZ25pdHVkZXMubGVuZ3RoLCBmYWN0b3JBLCBzdGF0ZUEpOwoJICAgICAgICAgICAgc2hpZnRQZWFrcyh0aGlzLnNvdXJjZVNwZWN0cnVtLCB0aGlzLnZvaWNlQlNwZWN0cnVtLCB0aGlzLnBlYWtzLCBwZWFrQ291bnQsIHRoaXMuYmxvY2tTaXplLCB0aGlzLm1hZ25pdHVkZXMubGVuZ3RoLCBmYWN0b3JCLCBzdGF0ZUIpOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRTcGVjdHJ1bS5maWxsKDApOwoJICAgICAgICAgICAgZm9yIChsZXQgYmluID0gMDsgYmluIDwgdGhpcy5tYWduaXR1ZGVzLmxlbmd0aDsgYmluKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtW2ldID0KCSAgICAgICAgICAgICAgICAgICAgKGRyeSAqIHRoaXMuc291cmNlU3BlY3RydW1baV0gKyBnYWluQSAqIHRoaXMudm9pY2VBU3BlY3RydW1baV0gKyBnYWluQiAqIHRoaXMudm9pY2VCU3BlY3RydW1baV0pICogbm9ybTsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtW2kgKyAxXSA9CgkgICAgICAgICAgICAgICAgICAgIChkcnkgKiB0aGlzLnNvdXJjZVNwZWN0cnVtW2kgKyAxXSArIGdhaW5BICogdGhpcy52b2ljZUFTcGVjdHJ1bVtpICsgMV0gKyBnYWluQiAqIHRoaXMudm9pY2VCU3BlY3RydW1baSArIDFdKSAqCgkgICAgICAgICAgICAgICAgICAgICAgICBub3JtOwoJICAgICAgICAgICAgfQoJICAgICAgICAgICAgdGhpcy5mZnQuY29tcGxldGVTcGVjdHJ1bSh0aGlzLm91dHB1dFNwZWN0cnVtKTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LmludmVyc2VUcmFuc2Zvcm0odGhpcy50aW1lQ29tcGxleCwgdGhpcy5vdXRwdXRTcGVjdHJ1bSk7CgkgICAgICAgICAgICB0aGlzLmZmdC5mcm9tQ29tcGxleEFycmF5KHRoaXMudGltZUNvbXBsZXgsIG91dHB1dCk7CgkgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dC5sZW5ndGg7IGkrKykKCSAgICAgICAgICAgICAgICBvdXRwdXRbaV0gKj0gdGhpcy53aW5kb3dbaV07CgkgICAgICAgIH0KCSAgICB9Cgl9CglyZWdpc3RlclByb2Nlc3NvcigiaGFybW9uaXplciIsIEhhcm1vbml6ZXJXb3JrbGV0UHJvY2Vzc29yKTsKCglleHBvcnRzLkhhcm1vbml6ZXJXb3JrbGV0UHJvY2Vzc29yID0gSGFybW9uaXplcldvcmtsZXRQcm9jZXNzb3I7CgoJcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWhhcm1vbml6ZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,KGZ1bmN0aW9uICgpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKioKICAgICAqIElUVS1SIEJTLjE3NzAtNSBsb3VkbmVzcyBtZXRlcmluZyDigJQgY29udGV4dC1mcmVlIERTUCBjb3JlLgogICAgICoKICAgICAqIEltcGxlbWVudHMgdGhlIGxvdWRuZXNzIG1lYXN1cmVtZW50IHBpcGVsaW5lIG9mIFJlY29tbWVuZGF0aW9uCiAgICAgKiBJVFUtUiBCUy4xNzcwLTUgKDExLzIwMjMpLCAiQWxnb3JpdGhtcyB0byBtZWFzdXJlIGF1ZGlvIHByb2dyYW1tZSBsb3VkbmVzcwogICAgICogYW5kIHRydWUtcGVhayBhdWRpbyBsZXZlbCIsIEFubmV4IDE6CiAgICAgKgogICAgICogICAxLiAiSyIgZnJlcXVlbmN5IHdlaWdodGluZyDigJQgYSB0d28tc3RhZ2UgcHJlLWZpbHRlciBwZXIgY2hhbm5lbAogICAgICogICAgICAoQW5uZXggMSwgwqcyIC8gRmlncyAyLTQ7IGNvZWZmaWNpZW50cyBUYWJsZXMgMSAmIDIsIHAuNC01KS4KICAgICAqICAgMi4gTWVhbi1zcXVhcmUgcGVyIGNoYW5uZWwgel9pIChBbm5leCAxLCBlcS4xLCBwLjUpLgogICAgICogICAzLiBDaGFubmVsLXdlaWdodGVkIHN1bW1hdGlvbiB3aXRoIHdlaWdodHMgR19pIChBbm5leCAxLCBUYWJsZSAzLCBwLjc7CiAgICAgKiAgICAgIEwvUi9DID0gMS4wLCBMcy9ScyA9IDEuNDEsIExGRSBleGNsdWRlZCkuCiAgICAgKiAgIDQuIExvdWRuZXNzICBMX0sgPSAtMC42OTEgKyAxMMK3bG9nMTAozqMgR19pwrd6X2kpICAoQW5uZXggMSwgZXEuMiwgcC42KSwKICAgICAqICAgICAgYW5kIGdhdGVkIGludGVncmF0ZWQgbG91ZG5lc3MgdmlhIHRoZSB0d28tc3RhZ2UgZ2F0ZQogICAgICogICAgICAozpNfYSA9IC03MCBMS0ZTIGFic29sdXRlLCDOk19yID0gLTEwIExVIHJlbGF0aXZlOyBBbm5leCAxLCBlcS4zLTcsIHAuNi03KS4KICAgICAqCiAgICAgKiBNb21lbnRhcnkgKDQwMCBtcykgYW5kIHNob3J0LXRlcm0gKDMgcykgd2luZG93cyBhcmUgdGhlIEVCVSBSMTI4IC8gVGVjaCAzMzQxCiAgICAgKiB1bmdhdGVkIGRlcml2YXRpdmVzIGJ1aWx0IGRpcmVjdGx5IG9uIHRoZSBzYW1lIEstd2VpZ2h0ZWQgbWVhbi1zcXVhcmU7IExSQQogICAgICogKGxvdWRuZXNzIHJhbmdlKSBmb2xsb3dzIEVCVSBUZWNoIDMzNDIgKDk1dGgg4oiSIDEwdGggcGVyY2VudGlsZSBvZiBzaG9ydC10ZXJtCiAgICAgKiBsb3VkbmVzcyBhYm92ZSBhIHJlbGF0aXZlIGdhdGUpLgogICAgICoKICAgICAqIFRoaXMgbW9kdWxlIGlzIFBVUkU6IGl0IG9wZXJhdGVzIG9uIGBGbG9hdDMyQXJyYXlgIGNoYW5uZWwgZGF0YSBhbmQgaGFzIG5vCiAgICAgKiBXZWIgQXVkaW8gLyB3b3JrbGV0IC8gZ2xvYmFsIGRlcGVuZGVuY2llcywgc28gdGhlIG1ldGVyaW5nIE1BVEggY2FuIGJlIHVuaXQKICAgICAqIHRlc3RlZCBkaXJlY3RseSAodGhlIHN0YW5kYXJkaXplZC1hdWRpby1jb250ZXh0IG1vY2sgY2FycmllcyBubyBzaWduYWwg4oCUIGl0cwogICAgICogQW5hbHlzZXJOb2RlIGlzIGFuIGVtcHR5IHN0dWIpLiBNaXJyb3JzIHRoZSBjb250ZXh0LWZyZWUtY29yZSB0ZXN0IHBhdHRlcm4gb2YKICAgICAqIGBzcmMvcHJvY2Vzc29ycy9zdGVyZW8tdG8tYmZvcm1hdC1jb3JlLnRzYC4KICAgICAqCiAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lml0dS5pbnQvcmVjL1ItUkVDLUJTLjE3NzAtNS0yMDIzMTEtSS9lbgogICAgICovCiAgICAvKioKICAgICAqIFBlci1jaGFubmVsIHdlaWdodGluZyBHX2kg4oCUIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBUYWJsZSAzIChwLjcpLgogICAgICogRnJvbnQvY2VudHJlIGNoYW5uZWxzIGFyZSB1bml0eTsgdGhlIHR3byBzdXJyb3VuZCBjaGFubmVscyBhcmUgYm9vc3RlZCBieQogICAgICogKzEuNSBkQiAoRyA9IDEuNDEpOyB0aGUgTEZFIGNoYW5uZWwgaXMgbm90IG1lYXN1cmVkICh3ZWlnaHQgMCAvIGV4Y2x1ZGVkKS4KICAgICAqLwogICAgY29uc3QgQ0hBTk5FTF9XRUlHSFRTID0gewogICAgICAgIEw6IDEuMCwKICAgICAgICBSOiAxLjAsCiAgICAgICAgQzogMS4wLAogICAgICAgIExzOiAxLjQxLAogICAgICAgIFJzOiAxLjQxLAogICAgICAgIExGRTogMCwgLy8gZXhjbHVkZWQg4oCUIHNlZSBUYWJsZSAzLCBwLjcKICAgIH07CiAgICAvKioKICAgICAqIFRoZSBLLXdlaWdodGluZyBjYWxpYnJhdGlvbiBjb25zdGFudCBvZiBJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSwgZXEuMiAocC42KS4KICAgICAqIENob3NlbiBzbyB0aGUgSy13ZWlnaHRpbmcgZmlsdGVyJ3MgZ2FpbiBhdCB0aGUgOTk3IEh6IHJlZmVyZW5jZSBmcmVxdWVuY3kgaXMKICAgICAqIGNhbmNlbGxlZCwgbWFraW5nIGEgMCBkQkZTIDk5NyBIeiBzaW5lIG9uIEwvQy9SIHJlYWQgZXhhY3RseSAtMy4wMSBMS0ZTIChwLjcpLgogICAgICovCiAgICBjb25zdCBMT1VETkVTU19PRkZTRVQgPSAtMC42OTE7CiAgICAvKiogQWJzb2x1dGUgZ2F0aW5nIHRocmVzaG9sZCDOk19hLCBJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSwgZXEuNiAocC42KS4gKi8KICAgIGNvbnN0IEFCU09MVVRFX0dBVEVfTEtGUyA9IC03MDsKICAgIC8qKiBSZWxhdGl2ZSBnYXRlIG9mZnNldCAoTFUgYmVsb3cgdGhlIGFic29sdXRlLWdhdGVkIG1lYW4pLCBBbm5leCAxIGVxLjYgKHAuNikuICovCiAgICBjb25zdCBSRUxBVElWRV9HQVRFX09GRlNFVF9MVSA9IC0xMDsKICAgIC8qKiBHYXRpbmcgYmxvY2sgbGVuZ3RoIFRfZywgSVRVLVIgQlMuMTc3MC01IEFubmV4IDEgKHAuNik6IDQwMCBtcy4gKi8KICAgIGNvbnN0IEdBVElOR19CTE9DS19TRUNPTkRTID0gMC40OwogICAgLyoqIEdhdGluZyBibG9jayBvdmVybGFwLCBBbm5leCAxIChwLjYpOiA3NSUg4oaSIHN0ZXAgPSAwLjI1IOKGkiAxMDAgbXMgaG9wLiAqLwogICAgY29uc3QgR0FUSU5HX09WRVJMQVAgPSAwLjc1OwogICAgLyoqIE1vbWVudGFyeSB3aW5kb3cgKEVCVSBUZWNoIDMzNDEpOiA0MDAgbXMsIHVuZ2F0ZWQuICovCiAgICBjb25zdCBNT01FTlRBUllfU0VDT05EUyA9IDAuNDsKICAgIC8qKiBTaG9ydC10ZXJtIHdpbmRvdyAoRUJVIFRlY2ggMzM0MSk6IDMgcywgdW5nYXRlZC4gKi8KICAgIGNvbnN0IFNIT1JUX1RFUk1fU0VDT05EUyA9IDMuMDsKICAgIC8qKgogICAgICogU3RhZ2UgMSBvZiB0aGUgSy13ZWlnaHRpbmcgZmlsdGVyIOKAlCBzZWNvbmQtb3JkZXIgaGlnaC1zaGVsZiBtb2RlbGxpbmcgdGhlCiAgICAgKiBhY291c3RpYyBlZmZlY3Qgb2YgdGhlIGhlYWQgYXMgYSByaWdpZCBzcGhlcmUuIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLAogICAgICogVGFibGUgMSAocC40KS4gVkVSQkFUSU0gY29lZmZpY2llbnRzLCBzcGVjaWZpZWQgYXQgYSA0OCBrSHogc2FtcGxlIHJhdGUuCiAgICAgKi8KICAgIGNvbnN0IEtfV0VJR0hUSU5HX1NUQUdFMV80OEsgPSB7CiAgICAgICAgYjA6IDEuNTM1MTI0ODU5NTg2OTcsCiAgICAgICAgYjE6IC0yLjY5MTY5NjE4OTQwNjM4LAogICAgICAgIGIyOiAxLjE5ODM5MjgxMDg1Mjg1LAogICAgICAgIGExOiAtMS42OTA2NTkyOTMxODI0MSwKICAgICAgICBhMjogMC43MzI0ODA3NzQyMTU4NSwKICAgIH07CiAgICAvKioKICAgICAqIFN0YWdlIDIgb2YgdGhlIEstd2VpZ2h0aW5nIGZpbHRlciDigJQgc2Vjb25kLW9yZGVyIFJMQiBoaWdoLXBhc3MuIElUVS1SCiAgICAgKiBCUy4xNzcwLTUgQW5uZXggMSwgVGFibGUgMiAocC41KS4gVkVSQkFUSU0gY29lZmZpY2llbnRzLCBhdCA0OCBrSHouCiAgICAgKi8KICAgIGNvbnN0IEtfV0VJR0hUSU5HX1NUQUdFMl80OEsgPSB7CiAgICAgICAgYjA6IDEuMCwKICAgICAgICBiMTogLTIsCiAgICAgICAgYjI6IDEuMCwKICAgICAgICBhMTogLTEuOTkwMDQ3NDU0ODMzOTgsCiAgICAgICAgYTI6IDAuOTkwMDcyMjUwMzY2MjEsCiAgICB9OwogICAgLyoqIFNhbXBsZSByYXRlIHRoZSB2ZXJiYXRpbSBUYWJsZSAxLzIgY29lZmZpY2llbnRzIGFyZSBzcGVjaWZpZWQgYXQgKHAuNSkuICovCiAgICBjb25zdCBSRUZFUkVOQ0VfU0FNUExFX1JBVEUgPSA0OF8wMDA7CiAgICAvKioKICAgICAqIERpcmVjdC1Gb3JtLUlJIHRyYW5zcG9zZWQgYmlxdWFkLiBUaGUgSy13ZWlnaHRpbmcgc3RhZ2VzIGFyZSBhcHBsaWVkIGFzIGEKICAgICAqIGNhc2NhZGUgb2YgdHdvIG9mIHRoZXNlIChBbm5leCAxLCBGaWdzIDMtNCkuIFN0YXRlZnVsIGFjcm9zcyBgcHJvY2Vzc2AgY2FsbHMKICAgICAqIHNvIGEgc3RyZWFtaW5nIGNhbGxlciBjYW4gZmVlZCBzdWNjZXNzaXZlIGJsb2NrcyB3aXRob3V0IHJlc2V0dGluZyBJSVIgbWVtb3J5LgogICAgICovCiAgICBjbGFzcyBCaXF1YWQgewogICAgICAgIGIwOwogICAgICAgIGIxOwogICAgICAgIGIyOwogICAgICAgIGExOwogICAgICAgIGEyOwogICAgICAgIC8vIFRyYW5zcG9zZWQgRGlyZWN0IEZvcm0gSUkgc3RhdGUuCiAgICAgICAgejEgPSAwOwogICAgICAgIHoyID0gMDsKICAgICAgICBjb25zdHJ1Y3Rvcihjb2VmZnMpIHsKICAgICAgICAgICAgdGhpcy5iMCA9IGNvZWZmcy5iMDsKICAgICAgICAgICAgdGhpcy5iMSA9IGNvZWZmcy5iMTsKICAgICAgICAgICAgdGhpcy5iMiA9IGNvZWZmcy5iMjsKICAgICAgICAgICAgdGhpcy5hMSA9IGNvZWZmcy5hMTsKICAgICAgICAgICAgdGhpcy5hMiA9IGNvZWZmcy5hMjsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSB0aGlzLmIwICogaW5wdXQgKyB0aGlzLnoxOwogICAgICAgICAgICB0aGlzLnoxID0gdGhpcy5iMSAqIGlucHV0IC0gdGhpcy5hMSAqIG91dHB1dCArIHRoaXMuejI7CiAgICAgICAgICAgIHRoaXMuejIgPSB0aGlzLmIyICogaW5wdXQgLSB0aGlzLmEyICogb3V0cHV0OwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy56MSA9IDA7CiAgICAgICAgICAgIHRoaXMuejIgPSAwOwogICAgICAgIH0KICAgIH0KICAgIC8qKgogICAgICogUmUtZGVyaXZlcyB0aGUgSy13ZWlnaHRpbmcgY29lZmZpY2llbnRzIGZvciBhIHNhbXBsZSByYXRlIG90aGVyIHRoYW4gdGhlCiAgICAgKiA0OCBrSHogdGhlIFRhYmxlIDEvMiB2YWx1ZXMgYXJlIHF1b3RlZCBhdCwgc28gdGhlIGRpZ2l0YWwgZmlsdGVyIGtlZXBzIHRoZQogICAgICogU0FNRSBmcmVxdWVuY3kgcmVzcG9uc2UgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBwLjU6ICJPdGhlciBzYW1wbGUgcmF0ZXMKICAgICAqIHJlcXVpcmUgcmVjb21wdXRlZCBjb2VmZmljaWVudHMgZ2l2aW5nIHRoZSBzYW1lIGZyZXF1ZW5jeSByZXNwb25zZSIpLgogICAgICoKICAgICAqIE1ldGhvZDogcmVhZCB0aGUgYW5hbG9nIHByb3RvdHlwZSBwb2xlcy96ZXJvcyBiYWNrIG91dCBvZiB0aGUgNDgga0h6IGRpZ2l0YWwKICAgICAqIGNvZWZmaWNpZW50cyB3aXRoIHRoZSBJTlZFUlNFIGJpbGluZWFyIHRyYW5zZm9ybSAobWF0Y2hlZCBhdCB0aGUgcmVmZXJlbmNlCiAgICAgKiByYXRlKSwgdGhlbiByZS1hcHBseSB0aGUgYmlsaW5lYXIgdHJhbnNmb3JtIGF0IHRoZSB0YXJnZXQgcmF0ZS4gQXQgNDgga0h6CiAgICAgKiB0aGlzIGlzIHRoZSBpZGVudGl0eSBhbmQgcmV0dXJucyB0aGUgdmVyYmF0aW0gY29lZmZpY2llbnRzIHVuY2hhbmdlZC4KICAgICAqLwogICAgZnVuY3Rpb24ga1dlaWdodGluZ0NvZWZmaWNpZW50cyhzYW1wbGVSYXRlLCByZWZlcmVuY2UpIHsKICAgICAgICBpZiAoc2FtcGxlUmF0ZSA9PT0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFKSB7CiAgICAgICAgICAgIHJldHVybiB7IC4uLnJlZmVyZW5jZSB9OwogICAgICAgIH0KICAgICAgICAvLyBCaWxpbmVhciB0cmFuc2Zvcm06IHMgPSBLwrcoMSAtIHpeLTEpLygxICsgel4tMSksIHdpdGggSyA9IDLCt2ZzICh0aGUgZmFjdG9yCiAgICAgICAgLy8gaXMgYWJzb3JiZWQgYnkgdGhlIGFuYWxvZyBjb2VmZmljaWVudHMsIHNvIGFueSBjb25zaXN0ZW50IEsgd29ya3MgYXMgbG9uZwogICAgICAgIC8vIGFzIHRoZSBzYW1lIEsgaXMgdXNlZCBmb3IgdGhlIGludmVyc2UgYW5kIGZvcndhcmQgdHJhbnNmb3JtKS4gUmVjb3ZlciB0aGUKICAgICAgICAvLyBjb250aW51b3VzLXRpbWUgYmlxdWFkIChhbmFsb2cgYi9hIGluIHBvd2VycyBvZiBzKSBmcm9tIHRoZSByZWZlcmVuY2UKICAgICAgICAvLyBkaWdpdGFsIGNvZWZmaWNpZW50cyBhdCBmc19yZWYsIHRoZW4gcmUtZGlzY3JldGlzZSBhdCB0aGUgdGFyZ2V0IGZzLgogICAgICAgIGNvbnN0IGZzUmVmID0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFOwogICAgICAgIGNvbnN0IGtSZWYgPSAyICogZnNSZWY7CiAgICAgICAgLy8gRGlnaXRhbCDihpIgYW5hbG9nIChpbnZlcnNlIGJpbGluZWFyKS4gRm9yCiAgICAgICAgLy8gICBIKHopID0gKGIwICsgYjEgel4tMSArIGIyIHpeLTIpLygxICsgYTEgel4tMSArIGEyIHpeLTIpCiAgICAgICAgLy8gc3Vic3RpdHV0ZSB6Xi0xID0gKHAgLSBzKS8ocCArIHMpIChwID0ga1JlZiA9IDLCt2ZzX3JlZikgYW5kIG11bHRpcGx5CiAgICAgICAgLy8gbnVtZXJhdG9yIGFuZCBkZW5vbWluYXRvciBieSAocCArIHMpXjIsIGNvbGxlY3RpbmcgcG93ZXJzIG9mIHMuIFRoZQogICAgICAgIC8vIHJlc3VsdGluZyBhbmFsb2cgbnVtL2Rlbm9tIGNvZWZmaWNpZW50cyAoQjIgc14yICsgQjEgcyArIEIwKSAvCiAgICAgICAgLy8gKEEyIHNeMiArIEExIHMgKyBBMCkga2VlcCBDT05TSVNURU5UIHJlbGF0aXZlIHNjYWxpbmcgYmV0d2VlbiBudW0gJiBkZW5vbSwKICAgICAgICAvLyB3aGljaCB0aGUgZm9yd2FyZCB0cmFuc2Zvcm0gYmVsb3cgdGhlbiByZS1kaXNjcmV0aXNlcyBhdCB0aGUgdGFyZ2V0IHJhdGUuCiAgICAgICAgY29uc3QgcCA9IGtSZWY7CiAgICAgICAgY29uc3QgeyBiMCwgYjEsIGIyLCBhMSwgYTIgfSA9IHJlZmVyZW5jZTsKICAgICAgICAvLyBOdW1lcmF0b3IgYW5hbG9nIGNvZWZmcyAoQjIgc14yICsgQjEgcyArIEIwKToKICAgICAgICBjb25zdCBCMiA9IGIwIC0gYjEgKyBiMjsKICAgICAgICBjb25zdCBCMSA9IDIgKiAoYjAgLSBiMikgKiBwOwogICAgICAgIGNvbnN0IEIwID0gKGIwICsgYjEgKyBiMikgKiBwICogcDsKICAgICAgICAvLyBEZW5vbWluYXRvciBhbmFsb2cgY29lZmZzIChBMiBzXjIgKyBBMSBzICsgQTApOgogICAgICAgIGNvbnN0IEEyID0gMSAtIGExICsgYTI7CiAgICAgICAgY29uc3QgQTEgPSAyICogKDEgLSBhMikgKiBwOwogICAgICAgIGNvbnN0IEEwID0gKDEgKyBhMSArIGEyKSAqIHAgKiBwOwogICAgICAgIC8vIEFuYWxvZyDihpIgZGlnaXRhbCAoZm9yd2FyZCBiaWxpbmVhcikgYXQgdGhlIHRhcmdldCByYXRlLiBzID0ga1TCtygxLXpeLTEpLygxK3peLTEpLgogICAgICAgIGNvbnN0IGtUID0gMiAqIHNhbXBsZVJhdGU7CiAgICAgICAgY29uc3Qga1QyID0ga1QgKiBrVDsKICAgICAgICAvLyBFdmFsdWF0ZSBudW1lcmF0b3IvZGVub21pbmF0b3IgcG9seW5vbWlhbHMgYXQgcyA9IGtUwrcoMS16Xi0xKS8oMSt6Xi0xKSBhbmQKICAgICAgICAvLyBtdWx0aXBseSB0aHJvdWdoIGJ5ICgxK3peLTEpXjI7IGNvbGxlY3QgZGlnaXRhbCBjb2VmZmljaWVudHMuCiAgICAgICAgY29uc3QgbmQwID0gQjIgKiBrVDIgKyBCMSAqIGtUICsgQjA7CiAgICAgICAgY29uc3QgbmQxID0gMiAqIChCMCAtIEIyICoga1QyKTsKICAgICAgICBjb25zdCBuZDIgPSBCMiAqIGtUMiAtIEIxICoga1QgKyBCMDsKICAgICAgICBjb25zdCBkZDAgPSBBMiAqIGtUMiArIEExICoga1QgKyBBMDsKICAgICAgICBjb25zdCBkZDEgPSAyICogKEEwIC0gQTIgKiBrVDIpOwogICAgICAgIGNvbnN0IGRkMiA9IEEyICoga1QyIC0gQTEgKiBrVCArIEEwOwogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgIGIwOiBuZDAgLyBkZDAsCiAgICAgICAgICAgIGIxOiBuZDEgLyBkZDAsCiAgICAgICAgICAgIGIyOiBuZDIgLyBkZDAsCiAgICAgICAgICAgIGExOiBkZDEgLyBkZDAsCiAgICAgICAgICAgIGEyOiBkZDIgLyBkZDAsCiAgICAgICAgfTsKICAgIH0KICAgIC8qKgogICAgICogVGhlIHR3by1zdGFnZSBLLXdlaWdodGluZyBwcmUtZmlsdGVyIGZvciBPTkUgY2hhbm5lbCAoQW5uZXggMSwgwqcyKS4gSG9sZHMgdGhlCiAgICAgKiBjYXNjYWRlIG9mIHRoZSBUYWJsZSAxIGhpZ2gtc2hlbGYgZm9sbG93ZWQgYnkgdGhlIFRhYmxlIDIgUkxCIGhpZ2gtcGFzcyBhbmQKICAgICAqIHByZXNlcnZlcyBJSVIgc3RhdGUgYWNyb3NzIGJsb2Nrcy4KICAgICAqLwogICAgY2xhc3MgS1dlaWdodGluZ0ZpbHRlciB7CiAgICAgICAgc3RhZ2UxOwogICAgICAgIHN0YWdlMjsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlID0gUkVGRVJFTkNFX1NBTVBMRV9SQVRFKSB7CiAgICAgICAgICAgIHRoaXMuc3RhZ2UxID0gbmV3IEJpcXVhZChrV2VpZ2h0aW5nQ29lZmZpY2llbnRzKHNhbXBsZVJhdGUsIEtfV0VJR0hUSU5HX1NUQUdFMV80OEspKTsKICAgICAgICAgICAgdGhpcy5zdGFnZTIgPSBuZXcgQmlxdWFkKGtXZWlnaHRpbmdDb2VmZmljaWVudHMoc2FtcGxlUmF0ZSwgS19XRUlHSFRJTkdfU1RBR0UyXzQ4SykpOwogICAgICAgIH0KICAgICAgICAvKiogQXBwbHkgYm90aCBLLXdlaWdodGluZyBzdGFnZXMgdG8gb25lIHNhbXBsZSAoQW5uZXggMSwgRmlncyAzLTQpLiAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RhZ2UyLnByb2Nlc3ModGhpcy5zdGFnZTEucHJvY2VzcyhpbnB1dCkpOwogICAgICAgIH0KICAgICAgICByZXNldCgpIHsKICAgICAgICAgICAgdGhpcy5zdGFnZTEucmVzZXQoKTsKICAgICAgICAgICAgdGhpcy5zdGFnZTIucmVzZXQoKTsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIENvbnZlcnRzIGEgY2hhbm5lbC13ZWlnaHRlZCBtZWFuLXNxdWFyZSBwb3dlciBzdW0gzqMgR19pwrd6X2kgdG8gbG91ZG5lc3MgaW4KICAgICAqIExLRlMgdmlhIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxLCBlcS4yIChwLjYpOiBMX0sgPSAtMC42OTEgKyAxMMK3bG9nMTAozqMpLgogICAgICogUmV0dXJucyAtSW5maW5pdHkgZm9yIGEgbm9uLXBvc2l0aXZlIHN1bSAoZGlnaXRhbCBzaWxlbmNlKS4KICAgICAqLwogICAgZnVuY3Rpb24gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkUG93ZXJTdW0pIHsKICAgICAgICBpZiAod2VpZ2h0ZWRQb3dlclN1bSA8PSAwKSB7CiAgICAgICAgICAgIHJldHVybiAtSW5maW5pdHk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBMT1VETkVTU19PRkZTRVQgKyAxMCAqIE1hdGgubG9nMTAod2VpZ2h0ZWRQb3dlclN1bSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBJVFUtUiBCUy4xNzcwLTUgdHJ1ZS1wZWFrIG1ldGVyaW5nIOKAlCBjb250ZXh0LWZyZWUgRFNQIGNvcmUuCiAgICAgKgogICAgICogSW1wbGVtZW50cyB0aGUgaW50ZXItc2FtcGxlICh0cnVlLXBlYWspIGxldmVsIGVzdGltYXRvciBvZiBSZWNvbW1lbmRhdGlvbgogICAgICogSVRVLVIgQlMuMTc3MC01ICgxMS8yMDIzKSwgQW5uZXggMiAocC4xOC0xOSkuIFRoZSB0cnVlIHBlYWsgb2YgYSBzaWduYWwgaXMKICAgICAqIHRoZSBtYXhpbXVtIGFic29sdXRlIHZhbHVlIG9mIHRoZSByZWNvbnN0cnVjdGVkIENPTlRJTlVPVVMtdGltZSB3YXZlZm9ybSwKICAgICAqIHdoaWNoIGNhbiBleGNlZWQgdGhlIGxhcmdlc3QgZGlzY3JldGUgc2FtcGxlIOKAlCBhIHBlYWstc2FtcGxlIG1ldGVyIG1pc3NlcyBpdC4KICAgICAqCiAgICAgKiBQZXItY2hhbm5lbCBwcm9jZXNzaW5nIHN0YWdlcyAoQW5uZXggMiwgRmlnLiBwLjE4KToKICAgICAqICAgMS4gQXR0ZW51YXRlIGJ5IDEyLjA0IGRCIChhIDItYml0IHJpZ2h0IHNoaWZ0OyBpbnRlZ2VyIGhlYWRyb29tKS4gU0tJUFBFRAogICAgICogICAgICBoZXJlIOKAlCB0aGlzIGlzIGEgZmxvYXRpbmctcG9pbnQgaW1wbGVtZW50YXRpb24sIGFuZCB0aGUgcmVjb21tZW5kYXRpb24KICAgICAqICAgICAgc3RhdGVzIHRoZSBhdHRlbnVhdGUvcmVzdG9yZSBwYWlyIGlzIGZvciBpbnRlZ2VyIGFyaXRobWV0aWMgb25seSAocC4xOCkuCiAgICAgKiAgIDIuIOKJpTTDlyBvdmVyLXNhbXBsZSAoNDgga0h6IOKGkiAxOTIga0h6KS4gQSBjb21wbGlhbnQgbWV0ZXIgbXVzdCBvdmVyc2FtcGxlIHRvCiAgICAgKiAgICAgIGF0IGxlYXN0IDE5MiBrSHogKHAuMTgtMTkpLiBUaGlzIGNvcmUgZG9lcyA0w5cgdmlhIHRoZSBwb2x5cGhhc2UgRklSLgogICAgICogICAzLiBMb3ctcGFzcyBpbnRlcnBvbGF0aW9uIGZpbHRlciDigJQgdGhlIDQ4LXRhcCwgNC1waGFzZSBwb2x5cGhhc2UgRklSIHdob3NlCiAgICAgKiAgICAgIGNvZWZmaWNpZW50cyBhcmUgZ2l2ZW4gdmVyYmF0aW0gYmVsb3cgKEFubmV4IDIsIHAuMTgtMTkpLgogICAgICogICA0LiBUYWtlIHRoZSBhYnNvbHV0ZSB2YWx1ZSAocmVjdGlmeSkuCiAgICAgKiAgIDUuIENvbnZlcnQgdG8gZEIgVFA6IDIwwrdsb2cxMCh2YWx1ZSkgW3RoZW4gKzEyLjA0IGRCIHRvIHVuZG8gc3RhZ2UgMSwgd2hpY2gKICAgICAqICAgICAgaXMgYSBuby1vcCBoZXJlIHNpbmNlIHN0YWdlIDEgaXMgc2tpcHBlZF0uCiAgICAgKgogICAgICogUHVyZTogb3BlcmF0ZXMgb24gYEZsb2F0MzJBcnJheWAgYW5kIGhhcyBubyBXZWIgQXVkaW8gLyB3b3JrbGV0IGRlcGVuZGVuY2llcy4KICAgICAqCiAgICAgKiBAc2VlIGh0dHBzOi8vd3d3Lml0dS5pbnQvcmVjL1ItUkVDLUJTLjE3NzAtNS0yMDIzMTEtSS9lbgogICAgICovCiAgICAvKioKICAgICAqIFRoZSA0w5cgb3ZlcnNhbXBsaW5nIGludGVycG9sYXRpb24gRklSIG9mIElUVS1SIEJTLjE3NzAtNSBBbm5leCAyIChwLjE4LTE5KToKICAgICAqIG9yZGVyLTQ4LCBnaXZlbiBhcyBhIDQtcGhhc2UgcG9seXBoYXNlIGZpbHRlciAoZWFjaCBwaGFzZSBhIDEyLXRhcCBzdWJmaWx0ZXIsCiAgICAgKiA0IMOXIDEyID0gNDggdGFwcykuIFZFUkJBVElNIGNvZWZmaWNpZW50cy4gVGhlIHBoYXNlcyBhcmUgbWlycm9yLXN5bW1ldHJpYwogICAgICogKHBoYXNlIDMgPSBwaGFzZSAwIHJldmVyc2VkLCBwaGFzZSAyID0gcGhhc2UgMSByZXZlcnNlZCkg4oCUIHRoZSBzdGFuZGFyZAogICAgICogbGluZWFyLXBoYXNlIHBvbHlwaGFzZSBzdHJ1Y3R1cmUuCiAgICAgKgogICAgICogUGhhc2UgayBwcm9kdWNlcyB0aGUgb3ZlcnNhbXBsZWQgb3V0cHV0IHNhbXBsZSBhdCBmcmFjdGlvbmFsIHBvc2l0aW9uIGsvNAogICAgICogYmV0d2VlbiBpbnB1dCBzYW1wbGVzIChwaGFzZSAwIOKJiCB0aGUgaW5wdXQgc2FtcGxlIGl0c2VsZikuCiAgICAgKi8KICAgIGNvbnN0IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4SyA9IFsKICAgICAgICAvLyBQaGFzZSAwICh0YXAwLi50YXAxMSkKICAgICAgICBbCiAgICAgICAgICAgIDAuMDAxNzA4OTg0Mzc1LCAwLjAxMDk4NjMyODEyNSwgLTAuMDE5NjUzMzIwMzEyNSwgMC4wMzMyMDMxMjUsIC0wLjA1OTQ0ODI0MjE4NzUsIDAuMTM3MzI5MTAxNTYyNSwgMC45NzIxNjc5Njg3NSwKICAgICAgICAgICAgLTAuMTAyMjk0OTIxODc1LCAwLjA0NzYwNzQyMTg3NSwgLTAuMDI2NjExMzI4MTI1LCAwLjAxNDg5MjU3ODEyNSwgLTAuMDA4MzAwNzgxMjUsCiAgICAgICAgXSwKICAgICAgICAvLyBQaGFzZSAxICh0YXAwLi50YXAxMSkKICAgICAgICBbCiAgICAgICAgICAgIC0wLjAyOTE3NDgwNDY4NzUsIDAuMDI5Mjk2ODc1LCAtMC4wNTE3NTc4MTI1LCAwLjA4OTExMTMyODEyNSwgLTAuMTY2NTAzOTA2MjUsIDAuNDY1MDg3ODkwNjI1LCAwLjc3OTc4NTE1NjI1LAogICAgICAgICAgICAtMC4yMDAzMTczODI4MTI1LCAwLjEwMTU2MjUsIC0wLjA1ODIyNzUzOTA2MjUsIDAuMDMzMDgxMDU0Njg3NSwgLTAuMDE4OTIwODk4NDM3NSwKICAgICAgICBdLAogICAgICAgIC8vIFBoYXNlIDIgKHRhcDAuLnRhcDExKQogICAgICAgIFsKICAgICAgICAgICAgLTAuMDE4OTIwODk4NDM3NSwgMC4wMzMwODEwNTQ2ODc1LCAtMC4wNTgyMjc1MzkwNjI1LCAwLjEwMTU2MjUsIC0wLjIwMDMxNzM4MjgxMjUsIDAuNzc5Nzg1MTU2MjUsIDAuNDY1MDg3ODkwNjI1LAogICAgICAgICAgICAtMC4xNjY1MDM5MDYyNSwgMC4wODkxMTEzMjgxMjUsIC0wLjA1MTc1NzgxMjUsIDAuMDI5Mjk2ODc1LCAtMC4wMjkxNzQ4MDQ2ODc1LAogICAgICAgIF0sCiAgICAgICAgLy8gUGhhc2UgMyAodGFwMC4udGFwMTEpCiAgICAgICAgWwogICAgICAgICAgICAtMC4wMDgzMDA3ODEyNSwgMC4wMTQ4OTI1NzgxMjUsIC0wLjAyNjYxMTMyODEyNSwgMC4wNDc2MDc0MjE4NzUsIC0wLjEwMjI5NDkyMTg3NSwgMC45NzIxNjc5Njg3NSwgMC4xMzczMjkxMDE1NjI1LAogICAgICAgICAgICAtMC4wNTk0NDgyNDIxODc1LCAwLjAzMzIwMzEyNSwgLTAuMDE5NjUzMzIwMzEyNSwgMC4wMTA5ODYzMjgxMjUsIDAuMDAxNzA4OTg0Mzc1LAogICAgICAgIF0sCiAgICBdOwogICAgLyoqIE92ZXJzYW1wbGluZyByYXRpbyBvZiB0aGUgdmVyYmF0aW0gQW5uZXggMiBGSVIgKDTDlykuICovCiAgICBjb25zdCBUUlVFX1BFQUtfT1ZFUlNBTVBMRSA9IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4Sy5sZW5ndGg7CiAgICBjb25zdCBGSVJfVEFQUyA9IFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4S1swXS5sZW5ndGg7CiAgICAvKioKICAgICAqIEJTLjE3NzAtNSBBbm5leCAyIHJlcXVpcmVzIHRoZSBvdmVyc2FtcGxlZCByYXRlIHRvIGJlICoqYXQgbGVhc3QgMTkyIGtIeioqCiAgICAgKiAocC4xODogIlRoZSA0w5cgb3Zlci1zYW1wbGluZyBmaWx0ZXIgaW5jcmVhc2VzIHRoZSBzYW1wbGluZyByYXRlIG9mIHRoZSBzaWduYWwKICAgICAqIGZyb20gNDgga0h6IHRvIDE5MiBrSHoiKS4gVGhlIHZlcmJhdGltIEZJUidzIDTDlyBvbmx5IHJlYWNoZXMgdGhhdCBhdCDiiaU0OCBrSHo7CiAgICAgKiBhdCA0NC4xIGtIeiwgNMOXID0gMTc2LjQga0h6LCBCRUxPVyB0aGUgcmVxdWlyZW1lbnQuCiAgICAgKi8KICAgIGNvbnN0IFRSVUVfUEVBS19NSU5fT1ZFUlNBTVBMRURfUkFURSA9IDE5Ml8wMDA7CiAgICAvKioKICAgICAqIENob29zZXMgdGhlIGludGVnZXIgb3ZlcnNhbXBsaW5nIGZhY3RvciBmb3IgYSBnaXZlbiBpbnB1dCBzYW1wbGUgcmF0ZSBzbyB0aGUKICAgICAqIG92ZXJzYW1wbGVkIHJhdGUgcmVhY2hlcyBCUy4xNzcwLTUncyDiiaUxOTIga0h6IHJlcXVpcmVtZW50IChBbm5leCAyLCBwLjE4KS4gVGhlCiAgICAgKiBmYWN0b3IgaXMgbmV2ZXIgYmVsb3cgNCAodGhlIHZlcmJhdGltIEZJUidzIGRlc2lnbiByYXRpbyksIGFuZCBhdCBzYW1wbGUgcmF0ZXMKICAgICAqIHRoZSB2ZXJiYXRpbSA0w5cgYWxyZWFkeSBjb3ZlcnMgKOKJpTQ4IGtIeikgaXQgc3RheXMgNC4gRXhhbXBsZXM6IDQ4IGtIeiDihpIgNMOXCiAgICAgKiAoMTkyIGtIeik7IDQ0LjEga0h6IOKGkiA1w5cgKDIyMC41IGtIeik7IDk2IGtIeiDihpIgNMOXICh0aGUgc3BlYyBub3RlcyAyw5cgd291bGQKICAgICAqIHN1ZmZpY2UsIGJ1dCB0aGUgdmVyYmF0aW0gRklSIGZsb29yIG9mIDTDlyBpcyBrZXB0IGFzIHRoZSBjb25zZXJ2YXRpdmUgZGVmYXVsdCkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIHRydWVQZWFrT3ZlcnNhbXBsZUZhY3RvcihzYW1wbGVSYXRlKSB7CiAgICAgICAgaWYgKCEoc2FtcGxlUmF0ZSA+IDApKSB7CiAgICAgICAgICAgIHJldHVybiBUUlVFX1BFQUtfT1ZFUlNBTVBMRTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIE1hdGgubWF4KFRSVUVfUEVBS19PVkVSU0FNUExFLCBNYXRoLmNlaWwoVFJVRV9QRUFLX01JTl9PVkVSU0FNUExFRF9SQVRFIC8gc2FtcGxlUmF0ZSkpOwogICAgfQogICAgLyoqCiAgICAgKiBCdWlsZHMgYW4gTi1waGFzZSBwb2x5cGhhc2UgaW50ZXJwb2xhdGluZyBGSVIgKGVhY2ggcGhhc2UgYSAxMi10YXAgc3ViZmlsdGVyLAogICAgICogbWF0Y2hpbmcgdGhlIHZlcmJhdGltIGZpbHRlcidzIG9yZGVyKSBmcm9tIGEgSGFubi13aW5kb3dlZC1zaW5jIHByb3RvdHlwZS4gVGhlCiAgICAgKiBCUy4xNzcwLTUgdmVyYmF0aW0gY29lZmZpY2llbnRzIGFyZSAib25lIHNldCAuLi4gdGhhdCB3b3VsZCBzYXRpc2Z5IHRoZQogICAgICogcmVxdWlyZW1lbnRzIiAoQW5uZXggMiwgcC4xOCkgZm9yIHRoZSA0w5cgY2FzZTsgZm9yIHNhbXBsZSByYXRlcyB0aGF0IG5lZWQgYQogICAgICogaGlnaGVyIGZhY3RvciB0byBjbGVhciAxOTIga0h6LCBhbiBlcXVpdmFsZW50IHdpbmRvd2VkLXNpbmMgbG93LXBhc3MKICAgICAqIGludGVycG9sYXRvciBvZiB0aGUgc2FtZSBvcmRlciBpcyBnZW5lcmF0ZWQgaGVyZS4gUGhhc2UgayBzYW1wbGVzIHRoZQogICAgICogY29udGludW91cy10aW1lIHJlY29uc3RydWN0aW9uIGF0IGZyYWN0aW9uYWwgb2Zmc2V0IGsvTiBiZXR3ZWVuIGlucHV0IHNhbXBsZXMuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGJ1aWxkUG9seXBoYXNlRmlyKG92ZXJzYW1wbGUpIHsKICAgICAgICBpZiAob3ZlcnNhbXBsZSA9PT0gVFJVRV9QRUFLX09WRVJTQU1QTEUpIHsKICAgICAgICAgICAgcmV0dXJuIFRSVUVfUEVBS19QT0xZUEhBU0VfRklSXzQ4SzsKICAgICAgICB9CiAgICAgICAgY29uc3QgdGFwcyA9IEZJUl9UQVBTOwogICAgICAgIGNvbnN0IGNlbnRlciA9ICh0YXBzIC0gMSkgLyAyOyAvLyBzeW1tZXRyaWMgYWJvdXQgdGhlIG1pZGRsZSB0YXAKICAgICAgICBjb25zdCBwaGFzZXMgPSBbXTsKICAgICAgICBmb3IgKGxldCBwaGFzZSA9IDA7IHBoYXNlIDwgb3ZlcnNhbXBsZTsgcGhhc2UrKykgewogICAgICAgICAgICBjb25zdCBmcmFjID0gcGhhc2UgLyBvdmVyc2FtcGxlOyAvLyBmcmFjdGlvbmFsIHNhbXBsZSBwb3NpdGlvbiAwIOKJpCBmcmFjIDwgMQogICAgICAgICAgICBjb25zdCBjb2VmZnMgPSBuZXcgQXJyYXkodGFwcyk7CiAgICAgICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgdGFwczsgaysrKSB7CiAgICAgICAgICAgICAgICAvLyBJZGVhbCBpbnRlcnBvbGF0aW9uIGtlcm5lbCBzYW1wbGVkIGF0IGRpc3RhbmNlIChrIC0gY2VudGVyIC0gZnJhYyk6CiAgICAgICAgICAgICAgICBjb25zdCB0ID0gayAtIGNlbnRlciAtIGZyYWM7CiAgICAgICAgICAgICAgICBjb25zdCBzaW5jQXJnID0gTWF0aC5QSSAqIHQ7CiAgICAgICAgICAgICAgICBjb25zdCBzaW5jID0gdCA9PT0gMCA/IDEgOiBNYXRoLnNpbihzaW5jQXJnKSAvIHNpbmNBcmc7CiAgICAgICAgICAgICAgICAvLyBIYW5uIHdpbmRvdyBhY3Jvc3MgdGhlIHRhcCBzcGFuIGtlZXBzIHRoZSBGSVIgZmluaXRlICYgbG93LXJpcHBsZS4KICAgICAgICAgICAgICAgIGNvbnN0IHcgPSAwLjUgLSAwLjUgKiBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBrKSAvICh0YXBzIC0gMSkpOwogICAgICAgICAgICAgICAgY29lZmZzW2tdID0gc2luYyAqIHc7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gTm9ybWFsaXplIGVhY2ggcGhhc2UgdG8gdW5pdHkgREMgZ2FpbiBzbyBhIGNvbnN0YW50IHJlY29uc3RydWN0cyBleGFjdGx5LgogICAgICAgICAgICBsZXQgc3VtID0gMDsKICAgICAgICAgICAgZm9yIChjb25zdCBjIG9mIGNvZWZmcykgewogICAgICAgICAgICAgICAgc3VtICs9IGM7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKHN1bSAhPT0gMCkgewogICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCB0YXBzOyBrKyspIHsKICAgICAgICAgICAgICAgICAgICBjb2VmZnNba10gLz0gc3VtOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHBoYXNlcy5wdXNoKGNvZWZmcyk7CiAgICAgICAgfQogICAgICAgIHJldHVybiBwaGFzZXM7CiAgICB9CiAgICAvKioKICAgICAqIFN0cmVhbWluZyA0w5cgcG9seXBoYXNlIHRydWUtcGVhayBkZXRlY3RvciBmb3IgT05FIGNoYW5uZWwgKElUVS1SIEJTLjE3NzAtNQogICAgICogQW5uZXggMikuIE1haW50YWlucyBhIDEyLXNhbXBsZSBpbnB1dCBoaXN0b3J5IHNvIHN1Y2Nlc3NpdmUgYmxvY2tzIGpvaW4KICAgICAqIHNlYW1sZXNzbHksIHJ1bnMgZXZlcnkgcGhhc2Ugb2YgdGhlIHBvbHlwaGFzZSBGSVIgcGVyIGlucHV0IHNhbXBsZSAoZ2l2aW5nCiAgICAgKiB0aGUgNMOXIG92ZXJzYW1wbGVkIHN0cmVhbSksIHJlY3RpZmllcywgYW5kIHRyYWNrcyB0aGUgcnVubmluZyBtYXguCiAgICAgKgogICAgICogVGhlIHZlcmJhdGltIGNvZWZmaWNpZW50cyBhcmUgcXVvdGVkIGF0IDQ4IGtIei4gQXQgaGlnaGVyIGlucHV0IHJhdGVzIGZld2VyCiAgICAgKiBvdmVyc2FtcGxpbmcgcGhhc2VzIGFyZSBuZWVkZWQgZm9yIHRoZSBzYW1lIGFjY3VyYWN5IChBbm5leCAyOiA5NiBrSHog4oaSIDLDlwogICAgICogc3VmZmljZXMpOyB0aGlzIDTDlyBkZXNpZ24gaXMgdGhlIGNvbnNlcnZhdGl2ZSBkZWZhdWx0IGF0IGFueSByYXRlLgogICAgICovCiAgICBjbGFzcyBUcnVlUGVha0RldGVjdG9yIHsKICAgICAgICBoaXN0b3J5OwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIHBlYWsgPSAwOwogICAgICAgIC8qKiBUaGUgcG9seXBoYXNlIEZJUiBhY3R1YWxseSB1c2VkICh2ZXJiYXRpbSBhdCA0w5csIGdlbmVyYXRlZCBmb3IgPjTDlykuICovCiAgICAgICAgZmlyOwogICAgICAgIC8qKiBPdmVyc2FtcGxpbmcgZmFjdG9yIGNob3NlbiBmb3IgdGhlIGlucHV0IHNhbXBsZSByYXRlICjiiaU0OyDiiaUxOTIga0h6KS4gKi8KICAgICAgICBvdmVyc2FtcGxlOwogICAgICAgIC8qKgogICAgICAgICAqIEBwYXJhbSBzYW1wbGVSYXRlIElucHV0IHNhbXBsZSByYXRlIGluIEh6LiBTZWxlY3RzIHRoZSBvdmVyc2FtcGxpbmcgZmFjdG9yCiAgICAgICAgICogICBzbyB0aGUgb3ZlcnNhbXBsZWQgcmF0ZSByZWFjaGVzIEJTLjE3NzAtNSdzIOKJpTE5MiBrSHogcmVxdWlyZW1lbnQgKEFubmV4IDIsCiAgICAgICAgICogICBwLjE4KS4gRGVmYXVsdHMgdG8gNDgga0h6ICh0aGUgcmF0ZSB0aGUgdmVyYmF0aW0gRklSIGlzIHF1b3RlZCBhdCDihpIgNMOXKS4KICAgICAgICAgKi8KICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlID0gNDhfMDAwKSB7CiAgICAgICAgICAgIHRoaXMub3ZlcnNhbXBsZSA9IHRydWVQZWFrT3ZlcnNhbXBsZUZhY3RvcihzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5maXIgPSBidWlsZFBvbHlwaGFzZUZpcih0aGlzLm92ZXJzYW1wbGUpOwogICAgICAgICAgICB0aGlzLmhpc3RvcnkgPSBuZXcgRmxvYXQzMkFycmF5KEZJUl9UQVBTKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHVzaGVzIGEgYmxvY2sgb2YgaW5wdXQgc2FtcGxlcyB0aHJvdWdoIHRoZSBwb2x5cGhhc2UgaW50ZXJwb2xhdG9yIGFuZAogICAgICAgICAqIHVwZGF0ZXMgdGhlIHJ1bm5pbmcgdHJ1ZSBwZWFrIChtYXggYWJzb2x1dGUgb3ZlcnNhbXBsZWQgdmFsdWUpLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3Moc2FtcGxlcykgewogICAgICAgICAgICBmb3IgKGxldCBuID0gMDsgbiA8IHNhbXBsZXMubGVuZ3RoOyBuKyspIHsKICAgICAgICAgICAgICAgIC8vIFNoaWZ0IHRoZSBuZXdlc3Qgc2FtcGxlIGludG8gdGhlIGNpcmN1bGFyIGhpc3RvcnkuCiAgICAgICAgICAgICAgICB0aGlzLmhpc3RvcnlbdGhpcy53cml0ZUluZGV4XSA9IHNhbXBsZXNbbl07CiAgICAgICAgICAgICAgICB0aGlzLndyaXRlSW5kZXggPSAodGhpcy53cml0ZUluZGV4ICsgMSkgJSBGSVJfVEFQUzsKICAgICAgICAgICAgICAgIC8vIEZvciBlYWNoIHBvbHlwaGFzZSBicmFuY2gsIGNvbnZvbHZlIHRoZSAxMi10YXAgaGlzdG9yeS4gdGFwIDAgaXMgdGhlCiAgICAgICAgICAgICAgICAvLyBvbGRlc3Qgc2FtcGxlOyB0aGUgbmV3ZXN0IHNhbXBsZSBpcyBhdCAod3JpdGVJbmRleCAtIDEpLgogICAgICAgICAgICAgICAgZm9yIChsZXQgcGhhc2UgPSAwOyBwaGFzZSA8IHRoaXMuZmlyLmxlbmd0aDsgcGhhc2UrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGNvZWZmcyA9IHRoaXMuZmlyW3BoYXNlXTsKICAgICAgICAgICAgICAgICAgICBsZXQgYWNjID0gMDsKICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IEZJUl9UQVBTOyBrKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgaWR4ID0gKHRoaXMud3JpdGVJbmRleCArIGspICUgRklSX1RBUFM7CiAgICAgICAgICAgICAgICAgICAgICAgIGFjYyArPSBjb2VmZnNba10gKiB0aGlzLmhpc3RvcnlbaWR4XTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgY29uc3QgbWFnID0gTWF0aC5hYnMoYWNjKTsKICAgICAgICAgICAgICAgICAgICBpZiAobWFnID4gdGhpcy5wZWFrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucGVhayA9IG1hZzsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIExpbmVhciAobm90IGRCKSB0cnVlLXBlYWsgbWFnbml0dWRlIG9ic2VydmVkIHNvIGZhci4gKi8KICAgICAgICB0cnVlUGVhaygpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMucGVhazsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogVHJ1ZS1wZWFrIGxldmVsIGluIGRCVFAgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAyIHN0YWdlIDU6IDIwwrdsb2cxMCBvZiB0aGUKICAgICAgICAgKiByZWN0aWZpZWQgb3ZlcnNhbXBsZWQgcGVhaywgcmVsYXRpdmUgdG8gMTAwJSBmdWxsIHNjYWxlKS4gUmV0dXJucyAtSW5maW5pdHkKICAgICAgICAgKiBmb3IgcHVyZSBzaWxlbmNlLgogICAgICAgICAqLwogICAgICAgIHRydWVQZWFrRGIoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnBlYWsgPiAwID8gMjAgKiBNYXRoLmxvZzEwKHRoaXMucGVhaykgOiAtSW5maW5pdHk7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICB0aGlzLmhpc3RvcnkuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5wZWFrID0gMDsKICAgICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBMb3VkbmVzcyBtZXRlcmluZyBBdWRpb1dvcmtsZXRQcm9jZXNzb3Ig4oCUIElUVS1SIEJTLjE3NzAtNS4KICAgICAqCiAgICAgKiBBIFBBU1MtVEhST1VHSCBtZXRlcmluZyB0YXA6IGl0IGNvcGllcyBpdHMgaW5wdXQgc3RyYWlnaHQgdG8gaXRzIG91dHB1dAogICAgICogKHNvIGluc2VydGluZyBpdCBuZXZlciBhbHRlcnMgdGhlIGF1ZGlibGUgc2lnbmFsKSB3aGlsZSBtZWFzdXJpbmcgbG91ZG5lc3MKICAgICAqIGluIHJlYWwgdGltZSBhbmQgcG9zdGluZyB0aGUgbGF0ZXN0IHJlYWRpbmdzIGJhY2sgdG8gdGhlIG1haW4gdGhyZWFkIHZpYSB0aGUKICAgICAqIE1lc3NhZ2VQb3J0LgogICAgICoKICAgICAqIFRoZSBtZXRlcmluZyBNQVRIIGxpdmVzIGVudGlyZWx5IGluIHRoZSBjb250ZXh0LWZyZWUgY29yZXMKICAgICAqIGAuLi9tZXRlcnMvbG91ZG5lc3MtY29yZWAgYW5kIGAuLi9tZXRlcnMvdHJ1ZXBlYWstY29yZWAgKHZlcmJhdGltIElUVS1SCiAgICAgKiBCUy4xNzcwLTUgY29lZmZpY2llbnRzIGFuZCBmb3JtdWxhZSksIHdoaWNoIGFyZSB1bml0LXRlc3RlZCBkaXJlY3RseSBvbgogICAgICogYEZsb2F0MzJBcnJheWBzIOKAlCB0aGUgc3RhbmRhcmRpemVkLWF1ZGlvLWNvbnRleHQgbW9jayBjYW5ub3QgY2Fycnkgc2lnbmFsLCBzbwogICAgICogdGhlIHdvcmtsZXQgc2hlbGwgaGVyZSBpcyBpbnRlbnRpb25hbGx5IHRoaW4uIFJvbGx1cCBpbmxpbmVzIHRoZSBpbXBvcnRlZAogICAgICogY29yZXMgaW50byB0aGUgSUlGRSB3b3JrbGV0IGJ1bmRsZS4KICAgICAqCiAgICAgKiBSZXBvcnRlZCB2YWx1ZXM6CiAgICAgKiAgIC0gbW9tZW50YXJ5ICAoTSk6IEstd2VpZ2h0ZWQgbG91ZG5lc3Mgb3ZlciB0aGUgbGFzdCA0MDAgbXMgKEVCVSBUZWNoIDMzNDEpLgogICAgICogICAtIHNob3J0VGVybSAgKFMpOiBLLXdlaWdodGVkIGxvdWRuZXNzIG92ZXIgdGhlIGxhc3QgMyBzIChFQlUgVGVjaCAzMzQxKS4KICAgICAqICAgLSBpbnRlZ3JhdGVkIChJKTogZ2F0ZWQgaW50ZWdyYXRlZCBsb3VkbmVzcyBzaW5jZSB0aGUgbGFzdCByZXNldAogICAgICogICAgIChJVFUtUiBCUy4xNzcwLTUgQW5uZXggMSB0d28tc3RhZ2UgZ2F0ZSwgLTcwIGFicyAvIC0xMCByZWwpLgogICAgICogICAtIHRydWVQZWFrICAoVFApOiBydW5uaW5nIG1heCB0cnVlLXBlYWsgaW4gZEJUUCAoSVRVLVIgQlMuMTc3MC01IEFubmV4IDIsCiAgICAgKiAgICAgNMOXIHBvbHlwaGFzZSBvdmVyc2FtcGxpbmcpLgogICAgICoKICAgICAqIEBzZWUgaHR0cHM6Ly93d3cuaXR1LmludC9yZWMvUi1SRUMtQlMuMTc3MC01LTIwMjMxMS1JL2VuCiAgICAgKi8KICAgIC8qKiBEZWZhdWx0IGNoYW5uZWwgbGFiZWwgYXNzaWdubWVudCBieSBjaGFubmVsIGluZGV4IGZvciDiiaQ1LWNoYW5uZWwgaW5wdXQuICovCiAgICBjb25zdCBERUZBVUxUX0NIQU5ORUxfT1JERVIgPSBbIkwiLCAiUiIsICJDIiwgIkxzIiwgIlJzIl07CiAgICAvKioKICAgICAqIENoYW5uZWwgbGFiZWwgbGF5b3V0IGJ5IGNoYW5uZWwgQ09VTlQuIFR3byBkaXN0aW5jdCBhdXRob3JpdGllcyBhcHBseToKICAgICAqICAtIFRoZSBjaGFubmVsIElOREVYIG9yZGVyIGZvciBlYWNoIGNvdW50IGlzIHRoZSBXM0MgV2ViIEF1ZGlvIEFQSSAxLjAKICAgICAqICAgICJzcGVha2VycyIgbGF5b3V0OiA2LWNoYW5uZWwgNS4xIGlzIFtGTCwgRlIsIEZDLCBMRkUsIFNMLCBTUl0gPSBpbmRleCAzIGlzCiAgICAgKiAgICB0aGUgTEZFIChodHRwczovL3d3dy53My5vcmcvVFIvd2ViYXVkaW8vI0NoYW5uZWxPcmRlcmluZykuCiAgICAgKiAgLSBUaGUgbG91ZG5lc3MgVFJFQVRNRU5UIG9mIHRob3NlIGNoYW5uZWxzIGlzIElUVS1SIEJTLjE3NzAtNSBBbm5leCAxCiAgICAgKiAgICAoVGFibGUgMyAvIEZpZy4xLCBwLjMsNyk6IHRoZSBMRkUgaXMgRVhDTFVERUQgZnJvbSB0aGUgbG91ZG5lc3Mgc3VtCiAgICAgKiAgICAoQ0hBTk5FTF9XRUlHSFRTLkxGRSA9IDApIGFuZCBvbmx5IEwvUi9DL0xzL1JzIGFyZSBtZWFzdXJlZCAoTHMvUnMgYXQgMS40MSkuCiAgICAgKiBUaGUgZmxhdCBERUZBVUxUX0NIQU5ORUxfT1JERVIgYWJvdmUgY2Fubm90IGV4cHJlc3MgdGhlIExGRSBzbG90IOKAlCBpdCB3b3VsZAogICAgICogbWlzbGFiZWwgYSA1LjEgc3RyZWFtJ3MgaW5kZXgtMyBMRkUgYXMgYExzYCBhbmQgY291bnQgaXQgYXMgcHJvZ3JhbW1lCiAgICAgKiBsb3VkbmVzcyDigJQgc28gYSBsYXlvdXQgaXMgY2hvc2VuIGJ5IGNoYW5uZWwgY291bnQgaGVyZS4gQ291bnRzIG5vdCBsaXN0ZWQgZmFsbAogICAgICogYmFjayB0byBERUZBVUxUX0NIQU5ORUxfT1JERVIuCiAgICAgKi8KICAgIGNvbnN0IENIQU5ORUxfTEFZT1VUUyA9IHsKICAgICAgICAxOiBbIkMiXSwgLy8gbW9ubzogb25lIG1lYXN1cmVkIGNoYW5uZWwsIHdlaWdodCAxLjAKICAgICAgICAyOiBbIkwiLCAiUiJdLCAvLyBzdGVyZW8KICAgICAgICAzOiBbIkwiLCAiUiIsICJDIl0sCiAgICAgICAgNTogWyJMIiwgIlIiLCAiQyIsICJMcyIsICJScyJdLCAvLyA1LjAKICAgICAgICA2OiBbIkwiLCAiUiIsICJDIiwgIkxGRSIsICJMcyIsICJScyJdLCAvLyA1LjEg4oCUIExGRSAoaW5kZXggMykgZXhjbHVkZWQKICAgIH07CiAgICAvKioKICAgICAqIE1haW50YWlucyB0aGUgSy13ZWlnaHRlZCBtZWFuLXNxdWFyZSBvZiBvbmUgY2hhbm5lbCBvdmVyIGEgc2xpZGluZyB3aW5kb3csCiAgICAgKiBwbHVzIGEgMTAwIG1zLWhvcCBibG9jayBhY2N1bXVsYXRvciBmb3IgaW50ZWdyYXRlZCBnYXRpbmcuIFB1cmUgbnVtYmVyCiAgICAgKiBjcnVuY2hpbmc7IG9uZSBpbnN0YW5jZSBwZXIgaW5wdXQgY2hhbm5lbC4KICAgICAqLwogICAgY2xhc3MgQ2hhbm5lbE1ldGVyIHsKICAgICAgICBrRmlsdGVyOwogICAgICAgIHRydWVQZWFrOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5rRmlsdGVyID0gbmV3IEtXZWlnaHRpbmdGaWx0ZXIoc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIC8vIFNhbXBsZS1yYXRlLWF3YXJlOiB0aGUgZGV0ZWN0b3IgcGlja3MgYW4gb3ZlcnNhbXBsaW5nIGZhY3RvciByZWFjaGluZwogICAgICAgICAgICAvLyBCUy4xNzcwLTUncyDiiaUxOTIga0h6IHJlcXVpcmVtZW50ICg0w5cgYXQgNDgga0h6LCDiiaU1w5cgYXQgNDQuMSBrSHopLgogICAgICAgICAgICB0aGlzLnRydWVQZWFrID0gbmV3IFRydWVQZWFrRGV0ZWN0b3Ioc2FtcGxlUmF0ZSk7CiAgICAgICAgfQogICAgfQogICAgLyoqCiAgICAgKiBSaW5nIG9mIHJlY2VudCBLLXdlaWdodGVkIHNxdWFyZWQgc2FtcGxlcyBmb3Igb25lIGNoYW5uZWwsIHN1cHBvcnRpbmcgYQogICAgICogc2xpZGluZy13aW5kb3cgbWVhbi1zcXVhcmUgb3ZlciB0aGUgbW9zdCByZWNlbnQgYGNhcGFjaXR5YCBzYW1wbGVzLgogICAgICovCiAgICBjbGFzcyBTbGlkaW5nUG93ZXIgewogICAgICAgIHJpbmc7CiAgICAgICAgaGVhZCA9IDA7CiAgICAgICAgZmlsbGVkID0gMDsKICAgICAgICBzdW0gPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKGNhcGFjaXR5KSB7CiAgICAgICAgICAgIHRoaXMucmluZyA9IG5ldyBGbG9hdDMyQXJyYXkoTWF0aC5tYXgoMSwgY2FwYWNpdHkpKTsKICAgICAgICB9CiAgICAgICAgcHVzaChzcXVhcmVkKSB7CiAgICAgICAgICAgIGlmICh0aGlzLmZpbGxlZCA9PT0gdGhpcy5yaW5nLmxlbmd0aCkgewogICAgICAgICAgICAgICAgdGhpcy5zdW0gLT0gdGhpcy5yaW5nW3RoaXMuaGVhZF07CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICB0aGlzLmZpbGxlZCsrOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMucmluZ1t0aGlzLmhlYWRdID0gc3F1YXJlZDsKICAgICAgICAgICAgdGhpcy5zdW0gKz0gc3F1YXJlZDsKICAgICAgICAgICAgdGhpcy5oZWFkID0gKHRoaXMuaGVhZCArIDEpICUgdGhpcy5yaW5nLmxlbmd0aDsKICAgICAgICB9CiAgICAgICAgbWVhblNxdWFyZSgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuZmlsbGVkID4gMCA/IHRoaXMuc3VtIC8gdGhpcy5maWxsZWQgOiAwOwogICAgICAgIH0KICAgIH0KICAgIGNsYXNzIExvdWRuZXNzTWV0ZXJQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIGNoYW5uZWxNZXRlcnMgPSBbXTsKICAgICAgICBtb21lbnRhcnlXaW5kb3dzID0gW107CiAgICAgICAgc2hvcnRUZXJtV2luZG93cyA9IFtdOwogICAgICAgIC8vIEludGVncmF0ZWQgZ2F0aW5nIHN0YXRlOiBwZXItY2hhbm5lbCBtZWFuLXNxdWFyZSBhY2N1bXVsYXRlZCBwZXIgMTAwIG1zCiAgICAgICAgLy8gYmxvY2sgKElUVS1SIEJTLjE3NzAtNSBBbm5leCAxOyA0MDAgbXMgYmxvY2tzIGF0IDc1JSBvdmVybGFwID09IHN1bSBvZiB0aGUKICAgICAgICAvLyBtb3N0IHJlY2VudCBmb3VyIDEwMCBtcyBzdWItYmxvY2tzKS4gV2UgYWNjdW11bGF0ZSAxMDAgbXMgc3ViLWJsb2NrIHBvd2VyCiAgICAgICAgLy8gYW5kIGZvcm0gNDAwIG1zIGdhdGluZyBibG9ja3MgZnJvbSBhIHNsaWRpbmcgc3VtIG9mIGZvdXIgc3ViLWJsb2Nrcy4KICAgICAgICBzdWJCbG9ja1NhbXBsZXMgPSAwOwogICAgICAgIHNhbXBsZXNJbnRvU3ViQmxvY2sgPSAwOwogICAgICAgIHN1YkJsb2NrU3VtUGVyQ2hhbm5lbCA9IFtdOwogICAgICAgIHJlY2VudFN1YkJsb2NrcyA9IFtdOyAvLyB1cCB0byA0IHN1Yi1ibG9ja3Mgb2YgcGVyLWNoYW5uZWwgc3VtcwogICAgICAgIGdhdGVkQmxvY2tzUGVyQ2hhbm5lbCA9IFtdOyAvLyBzdXJ2aXZpbmctYWJzb2x1dGUtZ2F0ZSBibG9jayBwZXItY2hhbm5lbCBtZWFuLXNxdWFyZXMKICAgICAgICBnYXRlZEJsb2NrTG91ZG5lc3MgPSBbXTsKICAgICAgICBjaGFubmVsQ291bnQgPSAwOwogICAgICAgIHBvc3RDb3VudGVyID0gMDsKICAgICAgICBwb3N0RXZlcnlTYW1wbGVzID0gMDsKICAgICAgICBzYW1wbGVzU2luY2VQb3N0ID0gMDsKICAgICAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CiAgICAgICAgICAgIHN1cGVyKG9wdGlvbnMpOwogICAgICAgICAgICAvLyBQb3N0IHJlYWRpbmdzIH4xMMOXIHBlciBzZWNvbmQuCiAgICAgICAgICAgIHRoaXMucG9zdEV2ZXJ5U2FtcGxlcyA9IE1hdGgucm91bmQoc2FtcGxlUmF0ZSAvIDEwKTsKICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1NhbXBsZXMgPSBNYXRoLnJvdW5kKEdBVElOR19CTE9DS19TRUNPTkRTICogKDEgLSBHQVRJTkdfT1ZFUkxBUCkgKiBzYW1wbGVSYXRlKTsgLy8gMTAwIG1zCiAgICAgICAgICAgIHRoaXMucG9ydC5vbm1lc3NhZ2UgPSAoZXZlbnQpID0+IHsKICAgICAgICAgICAgICAgIGlmIChldmVudC5kYXRhICYmIGV2ZW50LmRhdGEuY29tbWFuZCA9PT0gInJlc2V0IikgewogICAgICAgICAgICAgICAgICAgIHRoaXMucmVzZXQoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgZW5zdXJlQ2hhbm5lbHMoY291bnQpIHsKICAgICAgICAgICAgaWYgKGNvdW50ID09PSB0aGlzLmNoYW5uZWxDb3VudCkgewogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbENvdW50ID0gY291bnQ7CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbE1ldGVycyA9IFtdOwogICAgICAgICAgICB0aGlzLm1vbWVudGFyeVdpbmRvd3MgPSBbXTsKICAgICAgICAgICAgdGhpcy5zaG9ydFRlcm1XaW5kb3dzID0gW107CiAgICAgICAgICAgIGNvbnN0IG1vbWVudGFyeUNhcGFjaXR5ID0gTWF0aC5yb3VuZChNT01FTlRBUllfU0VDT05EUyAqIHNhbXBsZVJhdGUpOwogICAgICAgICAgICBjb25zdCBzaG9ydFRlcm1DYXBhY2l0eSA9IE1hdGgucm91bmQoU0hPUlRfVEVSTV9TRUNPTkRTICogc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgY291bnQ7IGMrKykgewogICAgICAgICAgICAgICAgdGhpcy5jaGFubmVsTWV0ZXJzLnB1c2gobmV3IENoYW5uZWxNZXRlcihzYW1wbGVSYXRlKSk7CiAgICAgICAgICAgICAgICB0aGlzLm1vbWVudGFyeVdpbmRvd3MucHVzaChuZXcgU2xpZGluZ1Bvd2VyKG1vbWVudGFyeUNhcGFjaXR5KSk7CiAgICAgICAgICAgICAgICB0aGlzLnNob3J0VGVybVdpbmRvd3MucHVzaChuZXcgU2xpZGluZ1Bvd2VyKHNob3J0VGVybUNhcGFjaXR5KSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwgPSBuZXcgQXJyYXkoY291bnQpLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMucmVjZW50U3ViQmxvY2tzID0gW107CiAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsID0gW107CiAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja0xvdWRuZXNzID0gW107CiAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayA9IDA7CiAgICAgICAgfQogICAgICAgIHJlc2V0KCkgewogICAgICAgICAgICBjb25zdCBjb3VudCA9IHRoaXMuY2hhbm5lbENvdW50OwogICAgICAgICAgICB0aGlzLmNoYW5uZWxDb3VudCA9IDA7CiAgICAgICAgICAgIHRoaXMuZW5zdXJlQ2hhbm5lbHMoY291bnQpOwogICAgICAgICAgICBmb3IgKGNvbnN0IG1ldGVyIG9mIHRoaXMuY2hhbm5lbE1ldGVycykgewogICAgICAgICAgICAgICAgbWV0ZXIua0ZpbHRlci5yZXNldCgpOwogICAgICAgICAgICAgICAgbWV0ZXIudHJ1ZVBlYWsucmVzZXQoKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBjaGFubmVsTGFiZWwoaW5kZXgpIHsKICAgICAgICAgICAgLy8gUGljayB0aGUgbGF5b3V0IGZvciB0aGUgbGl2ZSBjaGFubmVsIGNvdW50IHNvIGEgNS4xIHN0cmVhbSdzIGluZGV4LTMgTEZFCiAgICAgICAgICAgIC8vIGlzIGxhYmVsbGVkIExGRSAod2VpZ2h0IDAsIGV4Y2x1ZGVkKSByYXRoZXIgdGhhbiBtaXNsYWJlbGxlZCBMcy4gQ291bnRzCiAgICAgICAgICAgIC8vIHdpdGhvdXQgYSBkZWZpbmVkIGxheW91dCBmYWxsIGJhY2sgdG8gdGhlIGZsYXQgZGVmYXVsdCBvcmRlci4KICAgICAgICAgICAgY29uc3QgbGF5b3V0ID0gQ0hBTk5FTF9MQVlPVVRTW3RoaXMuY2hhbm5lbENvdW50XSA/PyBERUZBVUxUX0NIQU5ORUxfT1JERVI7CiAgICAgICAgICAgIHJldHVybiBsYXlvdXRbaW5kZXhdID8/ICJDIjsKICAgICAgICB9CiAgICAgICAgLyoqIExvdWRuZXNzIG9mIGEgcGVyLWNoYW5uZWwgbWVhbi1zcXVhcmUgdmVjdG9yIChBbm5leCAxLCBlcXMuMS0yKS4gKi8KICAgICAgICBsb3VkbmVzc09mKG1lYW5TcXVhcmVzUGVyQ2hhbm5lbCkgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG1lYW5TcXVhcmVzUGVyQ2hhbm5lbC5sZW5ndGg7IGMrKykgewogICAgICAgICAgICAgICAgd2VpZ2h0ZWRTdW0gKz0gQ0hBTk5FTF9XRUlHSFRTW3RoaXMuY2hhbm5lbExhYmVsKGMpXSAqIG1lYW5TcXVhcmVzUGVyQ2hhbm5lbFtjXTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkU3VtKTsKICAgICAgICB9CiAgICAgICAgd2luZG93TG91ZG5lc3Mod2luZG93cykgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHdpbmRvd3MubGVuZ3RoOyBjKyspIHsKICAgICAgICAgICAgICAgIHdlaWdodGVkU3VtICs9IENIQU5ORUxfV0VJR0hUU1t0aGlzLmNoYW5uZWxMYWJlbChjKV0gKiB3aW5kb3dzW2NdLm1lYW5TcXVhcmUoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gcG93ZXJTdW1Ub0xvdWRuZXNzKHdlaWdodGVkU3VtKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogQ2xvc2VzIHRoZSBjdXJyZW50IDEwMCBtcyBzdWItYmxvY2sgZXhhY3RseSBhdCBpdHMgYm91bmRhcnk6IGNvbnZlcnRzIHRoZQogICAgICAgICAqIGFjY3VtdWxhdGVkIHBlci1jaGFubmVsIHBvd2VyIHN1bSB0byBhIG1lYW4sIHB1c2hlcyBpdCBvbnRvIHRoZSByaW5nIG9mIHRoZQogICAgICAgICAqIGZvdXIgbW9zdC1yZWNlbnQgc3ViLWJsb2NrcywgZm9ybXMgYSA0MDAgbXMgZ2F0aW5nIGJsb2NrIChlcS4zKSB3aGVuIGZvdXIKICAgICAgICAgKiBzdWItYmxvY2tzIGFyZSBhdmFpbGFibGUsIGFwcGxpZXMgdGhlIGFic29sdXRlIGdhdGUgKGVxLjYpLCB0aGVuIHJlc2V0cyB0aGUKICAgICAgICAgKiBzdWItYmxvY2sgYWNjdW11bGF0b3IuIENhbGxlZCB0aGUgaW5zdGFudCBgc2FtcGxlc0ludG9TdWJCbG9ja2AgcmVhY2hlcyB0aGUKICAgICAgICAgKiBib3VuZGFyeSBzbyBubyBzYW1wbGUgbGFuZHMgaW4gdGhlIHdyb25nIHN1Yi1ibG9jay4KICAgICAgICAgKi8KICAgICAgICBjbG9zZVN1YkJsb2NrKCkgewogICAgICAgICAgICBjb25zdCBzdWJNZWFuID0gdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwubWFwKChzKSA9PiBzIC8gdGhpcy5zdWJCbG9ja1NhbXBsZXMpOwogICAgICAgICAgICB0aGlzLnJlY2VudFN1YkJsb2Nrcy5wdXNoKHN1Yk1lYW4pOwogICAgICAgICAgICBpZiAodGhpcy5yZWNlbnRTdWJCbG9ja3MubGVuZ3RoID4gNCkgewogICAgICAgICAgICAgICAgdGhpcy5yZWNlbnRTdWJCbG9ja3Muc2hpZnQoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBBIGNvbXBsZXRlIDQwMCBtcyBnYXRpbmcgYmxvY2sgZXhpc3RzIG9uY2UgZm91ciBzdWItYmxvY2tzIGFjY3VtdWxhdGUuCiAgICAgICAgICAgIGlmICh0aGlzLnJlY2VudFN1YkJsb2Nrcy5sZW5ndGggPT09IDQpIHsKICAgICAgICAgICAgICAgIGNvbnN0IGJsb2NrTWVhbiA9IG5ldyBBcnJheSh0aGlzLmNoYW5uZWxDb3VudCkuZmlsbCgwKTsKICAgICAgICAgICAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgdGhpcy5jaGFubmVsQ291bnQ7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGxldCBzID0gMDsKICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IHN1YiBvZiB0aGlzLnJlY2VudFN1YkJsb2NrcykgewogICAgICAgICAgICAgICAgICAgICAgICBzICs9IHN1YltjXTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgYmxvY2tNZWFuW2NdID0gcyAvIDQ7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBjb25zdCBsID0gdGhpcy5sb3VkbmVzc09mKGJsb2NrTWVhbik7CiAgICAgICAgICAgICAgICAvLyBBYnNvbHV0ZSBnYXRlIM6TX2EgKC03MCBMS0ZTKTogb25seSBzdG9yZSBwYXNzaW5nIGJsb2NrcyAoQW5uZXggMSBlcS42KS4KICAgICAgICAgICAgICAgIGlmIChsID4gQUJTT0xVVEVfR0FURV9MS0ZTKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5nYXRlZEJsb2Nrc1BlckNoYW5uZWwucHVzaChibG9ja01lYW4pOwogICAgICAgICAgICAgICAgICAgIHRoaXMuZ2F0ZWRCbG9ja0xvdWRuZXNzLnB1c2gobCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWwgPSBuZXcgQXJyYXkodGhpcy5jaGFubmVsQ291bnQpLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayAtPSB0aGlzLnN1YkJsb2NrU2FtcGxlczsKICAgICAgICB9CiAgICAgICAgLyoqIEdhdGVkIGludGVncmF0ZWQgbG91ZG5lc3Mgb3ZlciBzdXJ2aXZpbmcgYmxvY2tzIChBbm5leCAxLCBlcXMuNS03KS4gKi8KICAgICAgICBjb21wdXRlSW50ZWdyYXRlZCgpIHsKICAgICAgICAgICAgaWYgKHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsLmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIC1JbmZpbml0eTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBNZWFuIG9mIGFic29sdXRlLWdhdGVkIHNldCBhbHJlYWR5IGZpbHRlcmVkIG9uIHRoZSBhYnNvbHV0ZSBnYXRlIHdoZW4gdGhlCiAgICAgICAgICAgIC8vIGJsb2NrIHdhcyBzdG9yZWQuIENvbXB1dGUgcmVsYXRpdmUgdGhyZXNob2xkIHRoZW4gcmUtZmlsdGVyLgogICAgICAgICAgICBjb25zdCByZWxhdGl2ZVRocmVzaG9sZCA9IHRoaXMuZ2F0ZWRMb3VkbmVzc09mU2V0KHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsKSArIFJFTEFUSVZFX0dBVEVfT0ZGU0VUX0xVOwogICAgICAgICAgICBjb25zdCBmaW5hbFNldCA9IFtdOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZ2F0ZWRCbG9ja3NQZXJDaGFubmVsLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5nYXRlZEJsb2NrTG91ZG5lc3NbaV0gPiByZWxhdGl2ZVRocmVzaG9sZCkgewogICAgICAgICAgICAgICAgICAgIGZpbmFsU2V0LnB1c2godGhpcy5nYXRlZEJsb2Nrc1BlckNoYW5uZWxbaV0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChmaW5hbFNldC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiAtSW5maW5pdHk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2F0ZWRMb3VkbmVzc09mU2V0KGZpbmFsU2V0KTsKICAgICAgICB9CiAgICAgICAgZ2F0ZWRMb3VkbmVzc09mU2V0KHNldCkgewogICAgICAgICAgICBsZXQgd2VpZ2h0ZWRTdW0gPSAwOwogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IHRoaXMuY2hhbm5lbENvdW50OyBjKyspIHsKICAgICAgICAgICAgICAgIGxldCBtZWFuID0gMDsKICAgICAgICAgICAgICAgIGZvciAoY29uc3QgYmxvY2sgb2Ygc2V0KSB7CiAgICAgICAgICAgICAgICAgICAgbWVhbiArPSBibG9ja1tjXTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIG1lYW4gLz0gc2V0Lmxlbmd0aDsKICAgICAgICAgICAgICAgIHdlaWdodGVkU3VtICs9IENIQU5ORUxfV0VJR0hUU1t0aGlzLmNoYW5uZWxMYWJlbChjKV0gKiBtZWFuOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBwb3dlclN1bVRvTG91ZG5lc3Mod2VpZ2h0ZWRTdW0pOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgICAgICAgICAgLy8gTm8gaW5wdXQgY29ubmVjdGVkIHRoaXMgcXVhbnR1bSDigJQga2VlcCB0aGUgcHJvY2Vzc29yIGFsaXZlLgogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5lbnN1cmVDaGFubmVscyhpbnB1dC5sZW5ndGgpOwogICAgICAgICAgICBjb25zdCBmcmFtZUNvdW50ID0gaW5wdXRbMF0/Lmxlbmd0aCA/PyAwOwogICAgICAgICAgICAvLyBUcnVlLXBlYWsgcnVucyBvbiB0aGUgUkFXICh1bi13ZWlnaHRlZCkgY2hhbm5lbCBzaWduYWwgKEFubmV4IDIpOyBpdCBoYXMKICAgICAgICAgICAgLy8gbm8gc3ViLWJsb2NrIGJvdW5kYXJ5IGRlcGVuZGVuY3ksIHNvIHByb2Nlc3Mgd2hvbGUgcXVhbnRhIHBlciBjaGFubmVsLgogICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGlucHV0Lmxlbmd0aDsgYysrKSB7CiAgICAgICAgICAgICAgICB0aGlzLmNoYW5uZWxNZXRlcnNbY10udHJ1ZVBlYWsucHJvY2VzcyhpbnB1dFtjXSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gUGFzcy10aHJvdWdoIGNvcHk6IG1ldGVyaW5nIG11c3Qgbm90IGFsdGVyIHRoZSBhdWRpYmxlIHBhdGguIERvbmUgaW4gb25lCiAgICAgICAgICAgIC8vIHBhc3Mgc28gdGhlIGJvdW5kYXJ5LXNwbGl0IGxvdWRuZXNzIGxvb3AgYmVsb3cgbmVlZCBub3QgYWxzbyBjb3B5LgogICAgICAgICAgICBpZiAob3V0cHV0KSB7CiAgICAgICAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGlucHV0Lmxlbmd0aDsgYysrKSB7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5DaCA9IGlucHV0W2NdOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG91dENoID0gb3V0cHV0W2NdOwogICAgICAgICAgICAgICAgICAgIGlmIChvdXRDaCkgewogICAgICAgICAgICAgICAgICAgICAgICBvdXRDaC5zZXQoaW5DaCk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIEstd2VpZ2h0LCBzcXVhcmUsIGFuZCBhY2N1bXVsYXRlIHRoZSBzbGlkaW5nIHdpbmRvd3MgKyAxMDAgbXMgc3ViLWJsb2NrLgogICAgICAgICAgICAvLyBDUklUSUNBTCAoSVRVLVIgQlMuMTc3MC01IEFubmV4IDEsIGVxLjMgLyBwLjYg4oCUIGEgZ2F0aW5nIGJsb2NrIGlzIGEgc2V0IG9mCiAgICAgICAgICAgIC8vIENPTlRJR1VPVVMgc2FtcGxlcyBvZiBleGFjdGx5IHRoZSBibG9jayBkdXJhdGlvbik6IGEgMTI4LXNhbXBsZSByZW5kZXIKICAgICAgICAgICAgLy8gcXVhbnR1bSBjYW4gU1RSQURETEUgYSBzdWItYmxvY2sgYm91bmRhcnkgKDQ4MDAgc2FtcGxlcyBhdCA0OCBrSHopLiBXZSBtdXN0CiAgICAgICAgICAgIC8vIGNsb3NlIHRoZSBzdWItYmxvY2sgZXhhY3RseSBBVCB0aGUgYm91bmRhcnkgc28gbm8gc2FtcGxlIGxhbmRzIGluIHRoZSB3cm9uZwogICAgICAgICAgICAvLyAxMDAgbXMgc3ViLWJsb2NrLiBXYWxrIHRoZSBxdWFudHVtIGluIHNlZ21lbnRzIGJvdW5kZWQgYnkgdGhlIG5leHQgc3ViLWJsb2NrCiAgICAgICAgICAgIC8vIGVkZ2U7IGNsb3NlIChhbmQgZm9ybSBnYXRpbmcgYmxvY2tzIGZyb20pIHRoZSBzdWItYmxvY2sgd2hlbiBpdCBmaWxscy4KICAgICAgICAgICAgbGV0IG9mZnNldCA9IDA7CiAgICAgICAgICAgIHdoaWxlIChvZmZzZXQgPCBmcmFtZUNvdW50KSB7CiAgICAgICAgICAgICAgICBjb25zdCByZW1haW5pbmdJblN1YkJsb2NrID0gdGhpcy5zdWJCbG9ja1NhbXBsZXMgPiAwID8gdGhpcy5zdWJCbG9ja1NhbXBsZXMgLSB0aGlzLnNhbXBsZXNJbnRvU3ViQmxvY2sgOiBmcmFtZUNvdW50IC0gb2Zmc2V0OwogICAgICAgICAgICAgICAgY29uc3Qgc2VnbWVudCA9IE1hdGgubWluKHJlbWFpbmluZ0luU3ViQmxvY2ssIGZyYW1lQ291bnQgLSBvZmZzZXQpOwogICAgICAgICAgICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBpbnB1dC5sZW5ndGg7IGMrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IGluQ2ggPSBpbnB1dFtjXTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBtZXRlciA9IHRoaXMuY2hhbm5lbE1ldGVyc1tjXTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBtb21lbnRhcnkgPSB0aGlzLm1vbWVudGFyeVdpbmRvd3NbY107CiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2hvcnRUZXJtID0gdGhpcy5zaG9ydFRlcm1XaW5kb3dzW2NdOwogICAgICAgICAgICAgICAgICAgIGxldCBzdWJTdW0gPSB0aGlzLnN1YkJsb2NrU3VtUGVyQ2hhbm5lbFtjXTsKICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gb2Zmc2V0OyBpIDwgb2Zmc2V0ICsgc2VnbWVudDsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEstd2VpZ2h0IHRoZW4gc3F1YXJlIGZvciB0aGUgbG91ZG5lc3Mgd2luZG93cyAvIGJsb2Nrcy4KICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgeSA9IG1ldGVyLmtGaWx0ZXIucHJvY2VzcyhpbkNoW2ldKTsKICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3EgPSB5ICogeTsKICAgICAgICAgICAgICAgICAgICAgICAgbW9tZW50YXJ5LnB1c2goc3EpOwogICAgICAgICAgICAgICAgICAgICAgICBzaG9ydFRlcm0ucHVzaChzcSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1YlN1bSArPSBzcTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdWJCbG9ja1N1bVBlckNoYW5uZWxbY10gPSBzdWJTdW07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBvZmZzZXQgKz0gc2VnbWVudDsKICAgICAgICAgICAgICAgIHRoaXMuc2FtcGxlc0ludG9TdWJCbG9jayArPSBzZWdtZW50OwogICAgICAgICAgICAgICAgLy8gQ2xvc2UgYSAxMDAgbXMgc3ViLWJsb2NrIGV4YWN0bHkgYXQgdGhlIGJvdW5kYXJ5LgogICAgICAgICAgICAgICAgaWYgKHRoaXMuc3ViQmxvY2tTYW1wbGVzID4gMCAmJiB0aGlzLnNhbXBsZXNJbnRvU3ViQmxvY2sgPj0gdGhpcy5zdWJCbG9ja1NhbXBsZXMpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmNsb3NlU3ViQmxvY2soKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLnNhbXBsZXNTaW5jZVBvc3QgKz0gZnJhbWVDb3VudDsKICAgICAgICAgICAgaWYgKHRoaXMuc2FtcGxlc1NpbmNlUG9zdCA+PSB0aGlzLnBvc3RFdmVyeVNhbXBsZXMpIHsKICAgICAgICAgICAgICAgIHRoaXMuc2FtcGxlc1NpbmNlUG9zdCA9IDA7CiAgICAgICAgICAgICAgICBsZXQgbWF4VHJ1ZVBlYWsgPSAtSW5maW5pdHk7CiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IG1ldGVyIG9mIHRoaXMuY2hhbm5lbE1ldGVycykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRwID0gbWV0ZXIudHJ1ZVBlYWsudHJ1ZVBlYWtEYigpOwogICAgICAgICAgICAgICAgICAgIGlmICh0cCA+IG1heFRydWVQZWFrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIG1heFRydWVQZWFrID0gdHA7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgcmVwb3J0ID0gewogICAgICAgICAgICAgICAgICAgIHR5cGU6ICJsb3VkbmVzcyIsCiAgICAgICAgICAgICAgICAgICAgbW9tZW50YXJ5OiB0aGlzLndpbmRvd0xvdWRuZXNzKHRoaXMubW9tZW50YXJ5V2luZG93cyksCiAgICAgICAgICAgICAgICAgICAgc2hvcnRUZXJtOiB0aGlzLndpbmRvd0xvdWRuZXNzKHRoaXMuc2hvcnRUZXJtV2luZG93cyksCiAgICAgICAgICAgICAgICAgICAgaW50ZWdyYXRlZDogdGhpcy5jb21wdXRlSW50ZWdyYXRlZCgpLAogICAgICAgICAgICAgICAgICAgIHRydWVQZWFrOiBtYXhUcnVlUGVhaywKICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2UocmVwb3J0KTsKICAgICAgICAgICAgICAgIHRoaXMucG9zdENvdW50ZXIrKzsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3NvcigibG91ZG5lc3MtbWV0ZXIiLCBMb3VkbmVzc01ldGVyUHJvY2Vzc29yKTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWxvdWRuZXNzLW1ldGVyLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIG1vZHVsYXRlZERlbGF5ID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CiAgICAndXNlIHN0cmljdCc7CgogICAgLyoKICAgICAqIE1vZHVsYXRlZC1kZWxheSBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciB0aGUgdW5pZmllZAogICAgICogZGVsYXktbGluZSBtb2R1bGF0aW9uIGVmZmVjdCBiZWhpbmQgZGVsYXkvZWNobywgY2hvcnVzLCBmbGFuZ2VyLCB2aWJyYXRvIGFuZAogICAgICogZG91YmxpbmcuCiAgICAgKgogICAgICogSW1wbGVtZW50cyB0aGUgY2Fub25pY2FsIHVuaWZpZWQgY2lyY3VpdCBmcm9tOgogICAgICogICBKLiBEYXR0b3JybywgIkVmZmVjdCBEZXNpZ24sIFBhcnQgMjogRGVsYXktTGluZSBNb2R1bGF0aW9uIGFuZCBDaG9ydXMiLAogICAgICogICBKLiBBdWRpbyBFbmcuIFNvYy4sIFZvbC4gNDUsIE5vLiAxMCwgcHAuIDc2NC03ODggKDE5OTcpLgogICAgICoKICAgICAqIFRvcG9sb2d5IChEYXR0b3JybyAxOTk3LCBGaWcuIDM2LCBwLjc3NSkg4oCUIE9ORSBtb2R1bGF0ZWQgZGVsYXkgbGluZSB3cmFwcGVkCiAgICAgKiBpbiBkcnkvd2V0L2ZlZWRiYWNrIHBhdGhzOgogICAgICoKICAgICAqICAgICBIKHopID0gKGJsZW5kICsgZmVlZGZvcndhcmQgKiB6Xi1pLmZyYWMpIC8gKDEgKyBmZWVkYmFjayAqIHpeLWNlbnRlcikKICAgICAqCiAgICAgKiAgIC0gYmxlbmQgICAgICAg4oCUIGRyeSBwYXRoIGdhaW4gb24geFtuXS4KICAgICAqICAgLSBmZWVkZm9yd2FyZCDigJQgZ2FpbiBvbiB0aGUgTU9EVUxBVEVEIHdldCB0YXAgel4taS5mcmFjLCB3aGVyZSB0aGUgcmVhZAogICAgICogICAgIHBvaW50ZXIgaS5mcmFjID0gTk9NSU5BTF9ERUxBWSArIENIT1JVU19XSURUSCAqIHlbbl0gc3dlZXBzIHdpdGggdGhlIExGTy4KICAgICAqICAgLSBmZWVkYmFjayAgICDigJQgZ2FpbiBvbiBhIEZJWEVEIGNlbnRlciB0YXAgel4tY2VudGVyLiBEYXR0b3JybyBwLjc3NTogdGhlCiAgICAgKiAgICAgZmVlZGJhY2sgaXMgdGFrZW4gZnJvbSBhIFNFUEFSQVRFIGZpeGVkIHRhcCBhdCB0aGUgbm9taW5hbCBjZW50ZXIsIE5FVkVSCiAgICAgKiAgICAgdGhlIG1vZHVsYXRlZCB0YXAg4oCUIGZlZWRpbmcgYmFjayBhIHBpdGNoLW1vZHVsYXRlZCBzaWduYWwgaXMgb2JqZWN0aW9uYWJsZS4KICAgICAqCiAgICAgKiBLbm9iIHByZXNldHMgKFRhYmxlIDYsIHAuNzc1KSB5aWVsZCBlYWNoIGVmZmVjdCBmcm9tIHRoaXMgb25lIGNpcmN1aXQ6CiAgICAgKiAgIHZpYnJhdG8gIGJsZW5kPTAsICAgICAgZmY9MSwgICAgICBmYj0wICAgICAgICAoMTAwJSB3ZXQpCiAgICAgKiAgIGZsYW5nZXIgIGJsZW5kPTAuNzA3MSwgZmY9MC43MDcxLCBmYj0tMC43MDcxICAoYmxlbmQ9ZmYgZm9yIGRlZXBlc3QgdHJvdWdoKQogICAgICogICBjaG9ydXMgICBibGVuZD0wLjcwNzEsIGZmPTEsICAgICAgZmI9MC43MDcxICAgKHdoaXRlLWNob3J1cyBrbm9iczogYmxlbmQ9ZmIsIGZmPTEpCiAgICAgKiAgIGRvdWJsaW5nIGJsZW5kPTAuNzA3MSwgZmY9MC43MDcxLCBmYj0wCiAgICAgKiAgIGVjaG8gICAgIGJsZW5kPTEsICAgICAgZmY8PTEsICAgICBmYjwxICAgICAgICAobW9kdWxhdGlvbiBvZmYpCiAgICAgKiBTdGFiaWxpdHkgcmVxdWlyZXMgfGZlZWRiYWNrfCA8PSAwLjk5OTk5OTkgKFRhYmxlIDYsIHAuNzc2KS4KICAgICAqCiAgICAgKiBMRk86IHNpbnVzb2lkYWwsIGJpcG9sYXIgWy0xLCAxXSAoRGF0dG9ycm8gcC43Njcg4oCUIGEgdHJpYW5ndWxhciBMRk8gZ2l2ZXMgYW4KICAgICAqIHVubmF0dXJhbCBwaWVjZXdpc2UtY29uc3RhbnQgaW5zdGFudGFuZW91cyBwaXRjaCkuIE1vZHVsYXRlZCBkZWxheSBpbiBzYW1wbGVzCiAgICAgKiBpcyBkZWxheVRpbWUgKyBkZXB0aCpzaW4ocGhhc2UpOyBwaGFzZSBhZHZhbmNlcyBhdCBgcmF0ZWAgSHouIFN0ZXJlbyB1c2VzIGEKICAgICAqIHF1YWRyYXR1cmUgTEZPICg5MCBkZWcgcGVyIGNoYW5uZWwsIHAuNzc2KSwgcmVhbGl6ZWQgYnkgc2VlZGluZyBlYWNoIGNoYW5uZWwncwogICAgICogY29yZSB3aXRoIGxmb1BoYXNlMCA9IGNoICogcGkvMiBhdCBjb25zdHJ1Y3Rpb24gKHNlZSBtb2R1bGF0ZWQtZGVsYXkudHMpLgogICAgICoKICAgICAqIEludGVycG9sYXRpb246IHRoZSByZWFkIHBvaW50ZXIgbGFuZHMgYmV0d2VlbiBzYW1wbGVzLCBzbyB0aGUgZnJhY3Rpb25hbCBkZWxheQogICAgICogaXMgaW50ZXJwb2xhdGVkIHdpdGggYSBMYWdyYW5nZSBGSVIgZmlsdGVyIChMYWFrc28sIFZhbGltYWtpLCBLYXJqYWxhaW5lbiAmCiAgICAgKiBMYWluZSwgIlNwbGl0dGluZyB0aGUgVW5pdCBEZWxheSIsIElFRUUgU1AgTWFnIDE5OTYsIEVxLiA0MiAvIE49MSwzIHRhYmxlKS4KICAgICAqIExhZ3JhbmdlIGlzIEZJUiAoc3RhdGVsZXNzIGNvZWZmaWNpZW50cyk6IHVwZGF0aW5nIHRoZSBjb2VmZmljaWVudHMgZXZlcnkKICAgICAqIHNhbXBsZSB1bmRlciBtb2R1bGF0aW9uIGRvZXMgTk9UIGV4Y2l0ZSB0aGUgcmVjdXJzaXZlIHRyYW5zaWVudCB0aGF0IHBsYWd1ZXMKICAgICAqIGFuIGFsbHBhc3MgZnJhY3Rpb25hbC1kZWxheSBmaWx0ZXIgKExhYWtzbyBwLjUyKSwgYW5kIGl0cyBtYWduaXR1ZGUgc3RheXMgPD0gMQogICAgICogZm9yIGZlZWRiYWNrLWxvb3Agc2FmZXR5IChMYWFrc28gcC40MikuIFR3byBvcmRlcnMgYXJlIGV4cG9zZWQ6CiAgICAgKiAgIC0gImxpbmVhciIgKE49MSwgTGFha3NvIEVxLjQzKTogaDAgPSAxLWQsIGgxID0gZC4KICAgICAqICAgLSAiY3ViaWMiICAoTj0zLCBMYWFrc28gdGFibGUpOiB0aGUgNC10YXAgbWF4aW1hbGx5LWZsYXQgaW50ZXJwb2xhdG9yLCB3aXRoCiAgICAgKiAgICAgRCA9IDEgKyBkIHNvIHRoZSBmcmFjdGlvbiBzaXRzIGJldHdlZW4gdGFwcyAxIGFuZCAyIChNX29wdCBjZW50ZXJpbmcpLgogICAgICogQWxscGFzcyBpbnRlcnBvbGF0aW9uIGlzIGRlbGliZXJhdGVseSBkZWZlcnJlZDogdW5kZXIgbW9kdWxhdGlvbiBpdCBwcm9kdWNlcwogICAgICogdHJhbnNpZW50cyBhbmQgaXMgY2xlYW4gb25seSBvdmVyIH4rLy0xIHNlbWl0b25lIChEYXR0b3JybyBwLjc3MC03NzQpLgogICAgICoKICAgICAqIE5PVEUgb24gIndoaXRlIGNob3J1cyI6IERhdHRvcnJvIChwLjc3NikgZGVmaW5lcyB3aGl0ZSBjaG9ydXMgYXMgbmVnYXRpdmUKICAgICAqIGZlZWRiYWNrIEFORCBhbGxwYXNzIGZyYWN0aW9uYWwtZGVsYXkgaW50ZXJwb2xhdGlvbi4gVGhpcyBjb3JlIHVzZXMgdGhlCiAgICAgKiB3aGl0ZS1jaG9ydXMgS05PQiBzZXR0aW5ncyAodGhlIG5lZ2F0aXZlLWZlZWRiYWNrIHBhdGgpIGJ1dCBzdWJzdGl0dXRlcwogICAgICogY3ViaWMtTGFncmFuZ2UgaW50ZXJwb2xhdGlvbiAoTGFha3NvIDE5OTYpIGZvciBEYXR0b3JybydzIGFsbHBhc3Mg4oCUIGl0IGlzCiAgICAgKiB0cmFuc2llbnQtc2FmZSB1bmRlciBtb2R1bGF0aW9uIChMYWFrc28gcC41MikgYXQgdGhlIGNvc3Qgb2Ygc29tZQogICAgICogaGlnaC1mcmVxdWVuY3kgdHJvdWdoIGRlcHRoIHZlcnN1cyBhIHRydWUgYWxscGFzcy1pbnRlcnBvbGF0ZWQgd2hpdGUgY2hvcnVzLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzICsgRmxvYXQzMkFycmF5KS4gSXQgaGFzCiAgICAgKiBubyBBdWRpb1dvcmtsZXQgLyBnbG9iYWwgZGVwZW5kZW5jaWVzLCBzbyBpdCBpcyB1bml0LXRlc3RhYmxlIGRpcmVjdGx5OyB0aGUKICAgICAqIHdvcmtsZXQgc2hlbGwgaW4gbW9kdWxhdGVkLWRlbGF5LnRzIGRlbGVnYXRlcyB0byBpdC4KICAgICAqLwogICAgLyoqCiAgICAgKiBUaGUgRGF0dG9ycm8gVGFibGUgNiBrbm9iIGNvbnN0YW50IDEvc3FydCgyKSwgcHJpbnRlZCBWRVJCQVRJTSBhcyAwLjcwNzEgaW4KICAgICAqIHRoZSBwYXBlciAocC43NzUg4oCUIERhdHRvcnJvIHF1YW50aXplZCBpdCB0byA0IGRlY2ltYWxzIGZvciBoaXMgcTIzIGZpeGVkLXBvaW50CiAgICAgKiBoYXJkd2FyZSkuIFRoZSBmYWN0b3J5IHByZXNldHMgKGNhY29waG9ueS50cykgYW5kIHRoZSB3aGl0ZS1jaG9ydXMgY29uZGl0aW9uCiAgICAgKiBibGVuZCA9IGZlZWRiYWNrIHVzZSB0aGlzIGV4YWN0IHByaW50ZWQgdmFsdWUsIE5PVCB0aGUgZnVsbC1wcmVjaXNpb24KICAgICAqIE1hdGguU1FSVDFfMiwgc28gdGhlIHNoaXBwZWQga25vYnMgbWF0Y2ggdGhlIHBhcGVyJ3MgdGFibGUgZGlnaXQtZm9yLWRpZ2l0LgogICAgICovCiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQXBwcm94aW1hdGl2ZU51bWVyaWNDb25zdGFudDogRGF0dG9ycm8gVGFibGUgNiBwcmludHMgMC43MDcxIHZlcmJhdGltOyBtYXRjaGluZyB0aGUgcGFwZXIgZGlnaXQtZm9yLWRpZ2l0IGlzIGludGVudGlvbmFsLCBub3QgYW4gaW5leGFjdCBNYXRoLlNRUlQxXzIuCiAgICBjb25zdCBEQVRUT1JST19JTlZfU1FSVDIgPSAwLjcwNzE7CiAgICAvKioKICAgICAqIERlZmF1bHQgQXVkaW9QYXJhbSB2YWx1ZXMgZm9yIHRoZSBtb2R1bGF0ZWQtZGVsYXkgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncwogICAgICogb3duIHVuaXRzLiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AKICAgICAqIChtb2R1bGF0ZWQtZGVsYXkudHMpIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdAogICAgICogbmVlZCB0aGUgc2hpcHBlZCBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgc2hvcnQsIGRyeSwgc2xvdyBzZXR0aW5nIChhIHRyYW5zcGFyZW50IG5lYXItYnlwYXNzKSDigJQgdGhlCiAgICAgKiBjcmVhdGVEZWxheS9jcmVhdGVDaG9ydXMvY3JlYXRlRmxhbmdlci9jcmVhdGVWaWJyYXRvL2NyZWF0ZURvdWJsaW5nIGZhY3RvcmllcwogICAgICogKGNhY29waG9ueS50cykgc3ByZWFkIERhdHRvcnJvIFRhYmxlIDYga25vYiBwcmVzZXRzIG92ZXIgdGhlc2UuCiAgICAgKi8KICAgIGNvbnN0IE1PRFVMQVRFRF9ERUxBWV9ERUZBVUxUUyA9IHsKICAgICAgICBkZWxheVRpbWU6IDUsCiAgICAgICAgZGVwdGg6IDAsCiAgICAgICAgcmF0ZTogMC41LAogICAgICAgIGZlZWRiYWNrOiAwLAogICAgICAgIGJsZW5kOiAxLAogICAgICAgIGZlZWRmb3J3YXJkOiBEQVRUT1JST19JTlZfU1FSVDJ9OwogICAgLyoqIE1heGltdW0gbm9taW5hbCBkZWxheSAobXMpIHRoZSBidWZmZXIgaXMgc2l6ZWQgZm9yIChkZWxheVRpbWUgQXVkaW9QYXJhbSBtYXgpLiAqLwogICAgY29uc3QgTUFYX0RFTEFZX01TID0gMTAwMDsKICAgIC8qKiBNYXhpbXVtIExGTyBleGN1cnNpb24gKG1zKSB0aGUgYnVmZmVyIGlzIHNpemVkIGZvciAoZGVwdGggQXVkaW9QYXJhbSBtYXgpLiAqLwogICAgY29uc3QgTUFYX0RFUFRIX01TID0gNTA7CiAgICAvKiogU3RhYmlsaXR5IGJvdW5kIG9uIHxmZWVkYmFja3wgKERhdHRvcnJvIFRhYmxlIDYsIHEyMyBtYXgsIHAuNzc2KS4gKi8KICAgIGNvbnN0IE1BWF9GRUVEQkFDSyA9IDAuOTk5OTk5OTsKICAgIC8qKgogICAgICogTGFncmFuZ2UgbGluZWFyIChOPTEpIGZyYWN0aW9uYWwtZGVsYXkgY29lZmZpY2llbnRzIChMYWFrc28gMTk5NiBFcS40Myk6CiAgICAgKiAgIGgwID0gMSAtIGQsICBoMSA9IGQgICAgICAgIGZvciBmcmFjdGlvbmFsIGRlbGF5IGQgaW4gWzAsIDEpLgogICAgICogVHdvLXRhcCBGSVI7IGgwIHdlaWdodHMgdGhlIGludGVnZXIgdGFwLCBoMSB0aGUgbmV4dCBzYW1wbGUuIGgwICsgaDEgPSAxCiAgICAgKiAocGFydGl0aW9uIG9mIHVuaXR5LCBEQyBnYWluIDEpLiBFeHBvc2VkIHB1cmUgZm9yIGlzb2xhdGVkIHRlc3RpbmcuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGxhZ3JhbmdlTGluZWFyKGQpIHsKICAgICAgICByZXR1cm4gWzEgLSBkLCBkXTsKICAgIH0KICAgIC8qKgogICAgICogTGFncmFuZ2UgY3ViaWMgKE49MykgZnJhY3Rpb25hbC1kZWxheSBjb2VmZmljaWVudHMgKExhYWtzbyAxOTk2LCBOPTMgdGFibGUsCiAgICAgKiBwLjQxKS4gVGhlIDQtdGFwIG1heGltYWxseS1mbGF0IGludGVycG9sYXRvciBldmFsdWF0ZWQgYXQgdG90YWwgZGVsYXkKICAgICAqIEQgPSAxICsgZCwgc28gdGhlIGRlc2lyZWQgZnJhY3Rpb24gZCBpbiBbMCwgMSkgc2l0cyBiZXR3ZWVuIHRhcHMgMSBhbmQgMgogICAgICogKExhYWtzbyBNX29wdCBjZW50ZXJpbmcsIEVxLjIxKSDigJQgdGhlIHJlZ2ltZSB3aGVyZSB8SHwgPD0gMToKICAgICAqCiAgICAgKiAgIGgwID0gLShELTEpKEQtMikoRC0zKS82CiAgICAgKiAgIGgxID0gIEQoRC0yKShELTMpLzIKICAgICAqICAgaDIgPSAtRChELTEpKEQtMykvMgogICAgICogICBoMyA9ICBEKEQtMSkoRC0yKS82CiAgICAgKgogICAgICogVGhlIHJldHVybmVkIHRhcHMgd2VpZ2h0IGRlbGF5LWxpbmUgc2FtcGxlcyBhdCBpbnRlZ2VyIG9mZnNldHMge2ktMSwgaSwgaSsxLAogICAgICogaSsyfSByZWxhdGl2ZSB0byB0aGUgaW50ZWdlciBwYXJ0IG9mIHRoZSByZWFkIHBvaW50ZXIuIFN1bSA9IDEgKERDIGdhaW4gMSkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGxhZ3JhbmdlQ3ViaWMoZCkgewogICAgICAgIGNvbnN0IEQgPSAxICsgZDsKICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAoLShEIC0gMSkgKiAoRCAtIDIpICogKEQgLSAzKSkgLyA2LAogICAgICAgICAgICAoRCAqIChEIC0gMikgKiAoRCAtIDMpKSAvIDIsCiAgICAgICAgICAgICgtRCAqIChEIC0gMSkgKiAoRCAtIDMpKSAvIDIsCiAgICAgICAgICAgIChEICogKEQgLSAxKSAqIChEIC0gMikpIC8gNiwKICAgICAgICBdOwogICAgfQogICAgLyoqIENvbnZlcnQgbWlsbGlzZWNvbmRzIHRvIGEgKGZyYWN0aW9uYWwpIHNhbXBsZSBjb3VudCBhdCB0aGUgZ2l2ZW4gcmF0ZS4gKi8KICAgIGZ1bmN0aW9uIG1zVG9TYW1wbGVzKG1zLCBzYW1wbGVSYXRlKSB7CiAgICAgICAgcmV0dXJuIChtcyAvIDEwMDApICogc2FtcGxlUmF0ZTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgbW9kdWxhdGVkLWRlbGF5IHByb2Nlc3NvciBmb3IgT05FIGNoYW5uZWwuIE93bnMgdGhlIGNpcmN1bGFyIGRlbGF5CiAgICAgKiBidWZmZXIsIHRoZSB3cml0ZSBwb2ludGVyIGFuZCB0aGUgTEZPIHBoYXNlLCBhbmQgaXMgcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvCiAgICAgKiB0aGUgZGVsYXktbGluZSBjb250ZW50cywgZmVlZGJhY2sgcmVjaXJjdWxhdGlvbiBhbmQgTEZPIGNvbnRpbnVpdHkgYWxsIHN1cnZpdmUKICAgICAqIGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMg4oCUIGNvbnN0cnVjdGluZyBhIGZyZXNoIGluc3RhbmNlIHBlciBibG9jayB3b3VsZCBjbGVhcgogICAgICogdGhlIGJ1ZmZlciBhbmQgcmVzZXQgdGhlIExGTywgcHJvZHVjaW5nIGF1ZGlibGUgY2xpY2tzIChzYW1lIGhhemFyZCBkb2N1bWVudGVkCiAgICAgKiBmb3IgdGhlIGR5bmFtaWNzIGJhbGxpc3RpY3MgYW5kIEFEQUEgaGlzdG9yeSBjb3JlcykuCiAgICAgKgogICAgICogVGhlIGJ1ZmZlciBpcyBzaXplZCBPTkNFIGluIHRoZSBjb25zdHJ1Y3RvciBmb3IgTUFYX0RFTEFZX01TICsgTUFYX0RFUFRIX01TCiAgICAgKiBwbHVzIGEgbWFyZ2luIGZvciB0aGUgY3ViaWMgaW50ZXJwb2xhdG9yJ3MgNC10YXAgd2luZG93LCBzbyB0aGUgcmVhZCBwb2ludGVyCiAgICAgKiBjYW4gbmV2ZXIgcnVuIHBhc3QgdGhlIGFsbG9jYXRpb24gcmVnYXJkbGVzcyBvZiB0aGUgKGNsYW1wZWQpIHBhcmFtcy4KICAgICAqLwogICAgY2xhc3MgTW9kdWxhdGVkRGVsYXlQcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgLyoqIENpcmN1bGFyIGRlbGF5IGxpbmUuICovCiAgICAgICAgYnVmZmVyOwogICAgICAgIC8qKiBMZW5ndGggb2YgdGhlIGNpcmN1bGFyIGJ1ZmZlciAoc2FtcGxlcykuICovCiAgICAgICAgc2l6ZTsKICAgICAgICAvKiogV3JpdGUgaGVhZCBpbmRleCBpbnRvIGBidWZmZXJgLiAqLwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIC8qKiBDdXJyZW50IExGTyBwaGFzZSAocmFkaWFucykuICovCiAgICAgICAgbGZvUGhhc2U7CiAgICAgICAgY29uc3RydWN0b3Ioc2FtcGxlUmF0ZSwgbGZvUGhhc2UwID0gMCkgewogICAgICAgICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlOwogICAgICAgICAgICAvLyBTaXplIGZvciB0aGUgd29yc3QgY2FzZTogbWF4IG5vbWluYWwgZGVsYXkgKyBtYXggZXhjdXJzaW9uICsgY3ViaWMgbWFyZ2luCiAgICAgICAgICAgIC8vICh0aGUgY3ViaWMgaW50ZXJwb2xhdG9yIHJlYWRzIG9uZSBzYW1wbGUgYmVmb3JlIGFuZCB0d28gYWZ0ZXIgdGhlIGludGVnZXIKICAgICAgICAgICAgLy8gdGFwKS4gKzQgc2FtcGxlcyBvZiBzbGFjayBrZWVwcyBldmVyeSByZWFkIGluZGV4IGluc2lkZSB0aGUgYWxsb2NhdGlvbi4KICAgICAgICAgICAgY29uc3QgbWF4RGVsYXlTYW1wbGVzID0gbXNUb1NhbXBsZXMoTUFYX0RFTEFZX01TICsgTUFYX0RFUFRIX01TLCBzYW1wbGVSYXRlKTsKICAgICAgICAgICAgdGhpcy5zaXplID0gTWF0aC5jZWlsKG1heERlbGF5U2FtcGxlcykgKyA0OwogICAgICAgICAgICB0aGlzLmJ1ZmZlciA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5zaXplKTsKICAgICAgICAgICAgdGhpcy5sZm9QaGFzZSA9IGxmb1BoYXNlMDsKICAgICAgICB9CiAgICAgICAgLyoqIENsZWFyIHRoZSBkZWxheSBsaW5lLCB3cml0ZSBoZWFkIGFuZCBMRk8gcGhhc2UuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyLmZpbGwoMCk7CiAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9IDA7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKiogUmVhZCB0aGUgZGVsYXkgbGluZSBhdCBpbnRlZ2VyIG9mZnNldCBgZGVsYXlTYW1wbGVzYCBiZWhpbmQgdGhlIHdyaXRlIGhlYWQuICovCiAgICAgICAgcmVhZEludGVnZXIoZGVsYXlTYW1wbGVzKSB7CiAgICAgICAgICAgIGxldCBpZHggPSB0aGlzLndyaXRlSW5kZXggLSBkZWxheVNhbXBsZXM7CiAgICAgICAgICAgIGlkeCAlPSB0aGlzLnNpemU7CiAgICAgICAgICAgIGlmIChpZHggPCAwKQogICAgICAgICAgICAgICAgaWR4ICs9IHRoaXMuc2l6ZTsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyW2lkeF07CiAgICAgICAgfQogICAgICAgIC8qKgogICAgICAgICAqIFJlYWQgdGhlIGRlbGF5IGxpbmUgYXQgYSBmcmFjdGlvbmFsIGRlbGF5IGBkZWxheVNhbXBsZXNgIGJlaGluZCB0aGUgd3JpdGUKICAgICAgICAgKiBoZWFkLCBpbnRlcnBvbGF0aW5nIHdpdGggdGhlIHNlbGVjdGVkIExhZ3JhbmdlIEZJUiAoTGFha3NvIDE5OTYpLgogICAgICAgICAqLwogICAgICAgIHJlYWRGcmFjdGlvbmFsKGRlbGF5U2FtcGxlcywgbW9kZSkgewogICAgICAgICAgICBjb25zdCBpbnREZWxheSA9IE1hdGguZmxvb3IoZGVsYXlTYW1wbGVzKTsKICAgICAgICAgICAgY29uc3QgZnJhYyA9IGRlbGF5U2FtcGxlcyAtIGludERlbGF5OwogICAgICAgICAgICAvLyBMaW5lYXIgKE49MSksIG9yIGN1YmljIGRlZ3JhZGVkIHRvIGxpbmVhciBuZWFyIHRoZSBoZWFkOiB3aGVuIGludERlbGF5IDwgMQogICAgICAgICAgICAvLyB0aGUgY3ViaWMncyBuZXdlc3QgdGFwIChpbnREZWxheS0xKSB3b3VsZCBiZSBhIEZVVFVSRSBzYW1wbGUsIHNvIGZhbGwgYmFjawogICAgICAgICAgICAvLyB0byB0aGUgMi10YXAgbGluZWFyIGludGVycG9sYXRvci4gTGluZWFyIHJlYWNoZXMgZGVsYXkgMCBleGFjdGx5IChvZmZzZXQgMAogICAgICAgICAgICAvLyA9IHRoZSBqdXN0LXdyaXR0ZW4gY3VycmVudCBzYW1wbGUpIGZvciB0cnVlIHRocm91Z2gtemVybyBmbGFuZ2UvdmlicmF0by4KICAgICAgICAgICAgaWYgKG1vZGUgPT09ICJsaW5lYXIiIHx8IGludERlbGF5IDwgMSkgewogICAgICAgICAgICAgICAgY29uc3QgW2gwLCBoMV0gPSBsYWdyYW5nZUxpbmVhcihmcmFjKTsKICAgICAgICAgICAgICAgIC8vIFRoZSBkZXNpcmVkIGRlbGF5IGludERlbGF5K2ZyYWMgc2l0cyBiZXR3ZWVuIG9mZnNldCBpbnREZWxheSAobmV3ZXIpIGFuZAogICAgICAgICAgICAgICAgLy8gb2Zmc2V0IGludERlbGF5KzEgKG9sZGVyKS4gaDAgd2VpZ2h0cyB0aGUgaW50ZWdlciB0YXAsIGgxIHRoZSBuZXh0LU9MREVSCiAgICAgICAgICAgICAgICAvLyBzYW1wbGUsIHNvIGEgbGFyZ2VyIGZyYWMgcHVsbHMgdG93YXJkIHRoZSBsYXJnZXIgKG9sZGVyKSBkZWxheS4KICAgICAgICAgICAgICAgIHJldHVybiBoMCAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkpICsgaDEgKiB0aGlzLnJlYWRJbnRlZ2VyKGludERlbGF5ICsgMSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gQ3ViaWM6IEQgPSAxICsgZnJhYyBjZW50ZXJzIHRoZSBmcmFjdGlvbiBiZXR3ZWVuIHRhcHMgMSBhbmQgMiwgc28gdGhlIGZvdXIKICAgICAgICAgICAgLy8gdGFwcyBydW4gbmV3ZXN0LT5vbGRlc3QgYXQgb2Zmc2V0cyB7aW50RGVsYXktMSwgaW50RGVsYXksIGludERlbGF5KzEsCiAgICAgICAgICAgIC8vIGludERlbGF5KzJ9LCB3ZWlnaHRlZCBieSBoMC4uaDMgZnJvbSB0aGUgTGFha3NvIE49MyB0YWJsZSBpbiB0aGF0IG9yZGVyLgogICAgICAgICAgICBjb25zdCBbaDAsIGgxLCBoMiwgaDNdID0gbGFncmFuZ2VDdWJpYyhmcmFjKTsKICAgICAgICAgICAgcmV0dXJuIChoMCAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkgLSAxKSArCiAgICAgICAgICAgICAgICBoMSAqIHRoaXMucmVhZEludGVnZXIoaW50RGVsYXkpICsKICAgICAgICAgICAgICAgIGgyICogdGhpcy5yZWFkSW50ZWdlcihpbnREZWxheSArIDEpICsKICAgICAgICAgICAgICAgIGgzICogdGhpcy5yZWFkSW50ZWdlcihpbnREZWxheSArIDIpKTsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogUHJvY2VzcyBhIGJsb2NrLiBgaW5wdXRgIGFuZCBgb3V0cHV0YCBtYXkgYWxpYXMgKGluLXBsYWNlIGlzIGZpbmUpLgogICAgICAgICAqCiAgICAgICAgICogUGVyIHNhbXBsZSAoRGF0dG9ycm8gMTk5NyBGaWcuIDM2KToKICAgICAgICAgKiAgIDEuIGNlbnRlciAgPSBmaXhlZCBub21pbmFsIGRlbGF5IHRhcCAoc2FtcGxlcykgICAgICAgICAgICAgICAgICAoZmVlZGJhY2sgdGFwKQogICAgICAgICAqICAgMi4gZmIgICAgICA9IGZlZWRiYWNrICogZGVsYXlMaW5lW2NlbnRlcl0gICAgICAgICAgICAgICAgICAgICAgIChmaXhlZCB0YXApCiAgICAgICAgICogICAzLiB3W25dICAgID0geFtuXSAtIGZlZWRiYWNrKmRlbGF5TGluZVtjZW50ZXJdIC0+IHdyaXR0ZW4gICAgICAgKHJlY2lyY3VsYXRpb24pCiAgICAgICAgICogICAgICBUaGUgc3VtbWVyIFNVQlRSQUNUUyB0aGUgZmVlZGJhY2sgKEZpZy4gMzYncyBuZWdhdGl2ZS1mZWVkYmFjayBzdW1tZXIsCiAgICAgICAgICogICAgICBwLjc3NSksIHJlYWxpemluZyB0aGUgcGFwZXIncyBkZW5vbWluYXRvciAxICsgZmVlZGJhY2sqel4tY2VudGVyIChwLjc3NikuCiAgICAgICAgICogICA0LiBpLmZyYWMgID0gY2VudGVyICsgZGVwdGggKiBzaW4obGZvUGhhc2UpICAgICAgICAgICAgICAgICAgICAgKG1vZHVsYXRlZCB0YXApCiAgICAgICAgICogICA1LiB3ZXQgICAgID0gaW50ZXJwb2xhdGUgZGVsYXlMaW5lIGF0IGkuZnJhYyAgICAgICAgICAgICAgICAgICAgKExhZ3JhbmdlIEZJUikKICAgICAgICAgKiAgIDYuIHlbbl0gICAgPSBibGVuZCAqIHdbbl0gKyBmZWVkZm9yd2FyZCAqIHdldCAgICAgICAgICAgICAgICAgICAoZHJ5ICsgd2V0IG1peCkKICAgICAgICAgKiAgICAgIFRoZSBkcnkgKGJsZW5kKSB0YXAgcmVhZHMgdGhlIHJlY2lyY3VsYXRpb24gbm9kZSB3W25dLCBOT1QgdGhlIHJhdyBpbnB1dAogICAgICAgICAqICAgICAgeFtuXSwgc28gYm90aCBudW1lcmF0b3IgdGFwcyBzaGFyZSB0aGUgZmVlZGJhY2sgZGVub21pbmF0b3IgYW5kIHRoZQogICAgICAgICAqICAgICAgdHJhbnNmZXIgZnVuY3Rpb24gaXMgZXhhY3RseSBIKHopID0gKGJsZW5kICsgZmVlZGZvcndhcmQqel4taSkvKDEgKwogICAgICAgICAqICAgICAgZmVlZGJhY2sqel4tY2VudGVyKSAocC43NzYpLiBUaGlzIGlzIHRoZSBjYW5vbmljYWwgYWxscGFzcy1jb21iOiB3aXRoCiAgICAgICAgICogICAgICBibGVuZCA9IGZlZWRiYWNrIGFuZCBmZWVkZm9yd2FyZCA9IDEgaXQgY29sbGFwc2VzIHRvIGEgdW5pdHktbWFnbml0dWRlCiAgICAgICAgICogICAgICBhbGxwYXNzICh0aGUgd2hpdGUtY2hvcnVzIGNvbmRpdGlvbiwgcC43NzYpLiBXaGVuIGZlZWRiYWNrID0gMCwgd1tuXSA9CiAgICAgICAgICogICAgICB4W25dLCBzbyB0aGUgZHJ5IHBhdGggaXMgdGhlIHBsYWluIGlucHV0IGFzIGV4cGVjdGVkIGZvciB0aGUgbm8tZmVlZGJhY2sKICAgICAgICAgKiAgICAgIHByZXNldHMgKHZpYnJhdG8vaW5kdXN0cnkgY2hvcnVzL2RvdWJsaW5nL2VjaG8pLgogICAgICAgICAqIFRoZSBMRk8gcGhhc2UgYWR2YW5jZXMgYnkgMipwaSpyYXRlL3NhbXBsZVJhdGUgZWFjaCBzYW1wbGUuCiAgICAgICAgICovCiAgICAgICAgcHJvY2VzcyhpbnB1dCwgb3V0cHV0LCBwYXJhbXMpIHsKICAgICAgICAgICAgLy8gay1yYXRlLXN0eWxlIGNvZWZmaWNpZW50cyBjb21wdXRlZCBvbmNlIHBlciBibG9jay4KICAgICAgICAgICAgY29uc3QgY2VudGVyU2FtcGxlcyA9IG1zVG9TYW1wbGVzKHBhcmFtcy5kZWxheVRpbWUsIHRoaXMuc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgIGNvbnN0IGRlcHRoU2FtcGxlcyA9IG1zVG9TYW1wbGVzKHBhcmFtcy5kZXB0aCwgdGhpcy5zYW1wbGVSYXRlKTsKICAgICAgICAgICAgY29uc3QgZmVlZGJhY2sgPSBNYXRoLm1heCgtTUFYX0ZFRURCQUNLLCBNYXRoLm1pbihNQVhfRkVFREJBQ0ssIHBhcmFtcy5mZWVkYmFjaykpOwogICAgICAgICAgICBjb25zdCBibGVuZCA9IHBhcmFtcy5ibGVuZDsKICAgICAgICAgICAgY29uc3QgZmVlZGZvcndhcmQgPSBwYXJhbXMuZmVlZGZvcndhcmQ7CiAgICAgICAgICAgIGNvbnN0IG1vZGUgPSBwYXJhbXMuaW50ZXJwb2xhdGlvbjsKICAgICAgICAgICAgY29uc3QgcGhhc2VJbmMgPSAoMiAqIE1hdGguUEkgKiBwYXJhbXMucmF0ZSkgLyB0aGlzLnNhbXBsZVJhdGU7CiAgICAgICAgICAgIC8vIFRoZSBmaXhlZCBmZWVkYmFjayB0YXAgcmVhZHMgdGhlIGludGVnZXIgY2VudGVyOyBjbGFtcCB0byBhIHZhbGlkIG9mZnNldC4KICAgICAgICAgICAgY29uc3QgY2VudGVyVGFwID0gTWF0aC5tYXgoMCwgTWF0aC5taW4odGhpcy5zaXplIC0gMSwgTWF0aC5yb3VuZChjZW50ZXJTYW1wbGVzKSkpOwogICAgICAgICAgICBjb25zdCBuID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICAgICAgICAgIGNvbnN0IHggPSBpbnB1dFtpXTsKICAgICAgICAgICAgICAgIC8vIEZlZWRiYWNrIGZyb20gdGhlIEZJWEVEIGNlbnRlciB0YXAgKERhdHRvcnJvIHAuNzc1OiBuZXZlciB0aGUgbW9kdWxhdGVkCiAgICAgICAgICAgICAgICAvLyB0YXApLiBSZWFkIEJFRk9SRSB3cml0aW5nIHRoaXMgc2FtcGxlIHNvIHRoZSBsb29wIGRlbGF5IGlzIGBjZW50ZXJgLgogICAgICAgICAgICAgICAgY29uc3QgZmIgPSBmZWVkYmFjayAqIHRoaXMucmVhZEludGVnZXIoY2VudGVyVGFwKTsKICAgICAgICAgICAgICAgIC8vIFdyaXRlIHRoZSByZWNpcmN1bGF0ZWQgc2FtcGxlIGludG8gdGhlIGRlbGF5IGxpbmUgYXQgdGhlIHdyaXRlIGhlYWQuCiAgICAgICAgICAgICAgICAvLyBUaGUgZmVlZGJhY2sgaXMgU1VCVFJBQ1RFRCAoRGF0dG9ycm8gRmlnLiAzNidzIG5lZ2F0aXZlLWZlZWRiYWNrIHN1bW1lciwKICAgICAgICAgICAgICAgIC8vIHAuNzc1KSwgc28gdGhlIHRyYW5zZmVyIGZ1bmN0aW9uIGRlbm9taW5hdG9yIGlzIDEgKyBmZWVkYmFjayp6Xi1jZW50ZXIuCiAgICAgICAgICAgICAgICBjb25zdCB3ID0geCAtIGZiOwogICAgICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZUluZGV4XSA9IHc7CiAgICAgICAgICAgICAgICAvLyBNb2R1bGF0ZWQgcmVhZCBwb2ludGVyIGkuZnJhYyA9IGNlbnRlciArIGRlcHRoICogTEZPIChEYXR0b3JybyBlcSwgcC43NjUpLgogICAgICAgICAgICAgICAgbGV0IHJlYWREZWxheSA9IGNlbnRlclNhbXBsZXMgKyBkZXB0aFNhbXBsZXMgKiBNYXRoLnNpbih0aGlzLmxmb1BoYXNlKTsKICAgICAgICAgICAgICAgIC8vIEFsbG93IHRoZSBkZWxheSBkb3duIHRvIDAgc28gZmxhbmdlcnMvdmlicmF0byBjYW4gc3dlZXAgdGhyb3VnaCBhYnNvbHV0ZQogICAgICAgICAgICAgICAgLy8gemVybyAoRGF0dG9ycm8gcC43NzU7IFRhYmxlIDcgdmlicmF0by9mbGFuZ2Ugb25zZXQgMCBtcykuIE9mZnNldCAwIGlzIHRoZQogICAgICAgICAgICAgICAgLy8gbGVnaXRpbWF0ZSBqdXN0LXdyaXR0ZW4gY3VycmVudCBzYW1wbGUgKHdlIHdyaXRlIGJlZm9yZSByZWFkaW5nIHRoZSB3ZXQKICAgICAgICAgICAgICAgIC8vIHRhcCkuIHJlYWRGcmFjdGlvbmFsIGRlZ3JhZGVzIGN1YmljIC0+IGxpbmVhciBuZWFyIHRoZSBoZWFkIHNvIG5vIGZ1dHVyZQogICAgICAgICAgICAgICAgLy8gc2FtcGxlIGlzIGV2ZXIgcmVhZC4gVGhlIHVwcGVyIGNsYW1wIGtlZXBzIHRoZSBjdWJpYydzIG9sZGVzdCB0YXAKICAgICAgICAgICAgICAgIC8vIChpbnREZWxheSsyKSBpbnNpZGUgdGhlIGFsbG9jYXRpb24uCiAgICAgICAgICAgICAgICBpZiAocmVhZERlbGF5IDwgMCkKICAgICAgICAgICAgICAgICAgICByZWFkRGVsYXkgPSAwOwogICAgICAgICAgICAgICAgaWYgKHJlYWREZWxheSA+IHRoaXMuc2l6ZSAtIDMpCiAgICAgICAgICAgICAgICAgICAgcmVhZERlbGF5ID0gdGhpcy5zaXplIC0gMzsKICAgICAgICAgICAgICAgIGNvbnN0IHdldCA9IHRoaXMucmVhZEZyYWN0aW9uYWwocmVhZERlbGF5LCBtb2RlKTsKICAgICAgICAgICAgICAgIC8vIEZpZy4gMzYgb3V0cHV0OiBkcnkgKGJsZW5kLCBvZmYgdGhlIHJlY2lyY3VsYXRpb24gbm9kZSB3W25dKSArIG1vZHVsYXRlZAogICAgICAgICAgICAgICAgLy8gd2V0IChmZWVkZm9yd2FyZCkuIEJvdGggdGFwcyBzaGFyZSB0aGUgZmVlZGJhY2sgZGVub21pbmF0b3IsIGdpdmluZwogICAgICAgICAgICAgICAgLy8gSCh6KSA9IChibGVuZCArIGZlZWRmb3J3YXJkKnpeLWkpLygxICsgZmVlZGJhY2sqel4tY2VudGVyKSAocC43NzYpLgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0gYmxlbmQgKiB3ICsgZmVlZGZvcndhcmQgKiB3ZXQ7CiAgICAgICAgICAgICAgICAvLyBBZHZhbmNlIHdyaXRlIGhlYWQgYW5kIExGTyBwaGFzZS4KICAgICAgICAgICAgICAgIHRoaXMud3JpdGVJbmRleCA9ICh0aGlzLndyaXRlSW5kZXggKyAxKSAlIHRoaXMuc2l6ZTsKICAgICAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgKz0gcGhhc2VJbmM7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5sZm9QaGFzZSA+PSAyICogTWF0aC5QSSkKICAgICAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlIC09IDIgKiBNYXRoLlBJOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBNb2R1bGF0ZWQtZGVsYXkgQXVkaW9Xb3JrbGV0IHNoZWxsIOKAlCB0aGluIEF1ZGlvV29ya2xldFByb2Nlc3NvciB0aGF0IGRlbGVnYXRlcwogICAgICogQUxMIERTUCBtYXRoIHRvIHRoZSBjb250ZXh0LWZyZWUgTW9kdWxhdGVkRGVsYXlQcm9jZXNzb3IgaW4KICAgICAqIG1vZHVsYXRlZC1kZWxheS1jb3JlLnRzLiBNaXJyb3JzIHRoZSBkeW5hbWljcy50cyAvIHdhdmVzaGFwZXIudHMgY29yZS9zaGVsbAogICAgICogc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKHBhcmFtZXRlckRlc2NyaXB0b3JzLAogICAgICogcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IgY2FsbCk7IHRoZSBhbGdvcml0aG0gbGl2ZXMgaW4gdGhlIHVuaXQtdGVzdGVkCiAgICAgKiBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogRGF0dG9ycm8ncyB1bmlmaWVkIG1vZHVsYXRlZC1kZWxheSBjaXJjdWl0IChGaWcuIDM2LCBKQUVTIDE5OTcpCiAgICAgKiBkcml2aW5nIGRlbGF5L2Nob3J1cy9mbGFuZ2VyL3ZpYnJhdG8vZG91YmxpbmcgZnJvbSBibGVuZC9mZWVkZm9yd2FyZC9mZWVkYmFjawogICAgICoga25vYnMsIHdpdGggTGFncmFuZ2UgRklSIGZyYWN0aW9uYWwtZGVsYXkgaW50ZXJwb2xhdGlvbiAoTGFha3NvIDE5OTYpLiBTZWUKICAgICAqIG1vZHVsYXRlZC1kZWxheS1jb3JlLnRzIGhlYWRlciBmb3IgdGhlIHRyYW5zZmVyIGZ1bmN0aW9uLCBUYWJsZSA2IHByZXNldHMsIHRoZQogICAgICogZml4ZWQtZmVlZGJhY2stdGFwIHJhdGlvbmFsZSwgdGhlIHNpbnVzb2lkYWwgTEZPIGFuZCB0aGUgaW50ZXJwb2xhdGlvbiBjaG9pY2UuCiAgICAgKi8KICAgIGNvbnN0IFdPUktMRVRfTE9HX1BSRUZJWCA9ICJbY2Fjb3Bob255L3dvcmtsZXQ6bW9kdWxhdGVkLWRlbGF5XSI7CiAgICAvKioKICAgICAqIEludGVycG9sYXRpb24gc2VsZWN0aW9uIHJpZGVzIG9uIGFuIEF1ZGlvUGFyYW0gKHdoaWNoIGNhbiBvbmx5IGNhcnJ5IG51bWJlcnMpLAogICAgICogc28gdGhlICJpbnRlcnBvbGF0aW9uIiBwYXJhbSBpcyBhbiBlbnVtIGluZGV4OiAwID0gY3ViaWMgKDQtdGFwIExhZ3JhbmdlIE49MywKICAgICAqIHRoZSBkZWZhdWx0KSwgMSA9IGxpbmVhciAoMi10YXAgTGFncmFuZ2UgTj0xKS4gQm90aCBhcmUgRklSIChMYWFrc28gMTk5NikuCiAgICAgKi8KICAgIGNvbnN0IElOVEVSUE9MQVRJT05fQllfSU5ERVggPSBbImN1YmljIiwgImxpbmVhciJdOwogICAgZnVuY3Rpb24gaW50ZXJwb2xhdGlvbkZyb21JbmRleChpbmRleCkgewogICAgICAgIGNvbnN0IGkgPSBNYXRoLnJvdW5kKGluZGV4KTsKICAgICAgICByZXR1cm4gSU5URVJQT0xBVElPTl9CWV9JTkRFWFtpXSA/PyAiY3ViaWMiOwogICAgfQogICAgY2xhc3MgTW9kdWxhdGVkRGVsYXlXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBkZWxheSBsaW5lLAogICAgICAgIC8vIGZlZWRiYWNrIHJlY2lyY3VsYXRpb24gYW5kIExGTyBwaGFzZSBhY3Jvc3MgcHJvY2VzcygpIGJsb2Nrcy4gRWFjaCBjaGFubmVsJ3MKICAgICAgICAvLyBMRk8gaXMgc2VlZGVkIGluIHF1YWRyYXR1cmUgKGNoICogcGkvMikgc28gYSBzdGVyZW8gcGFpciBnZXRzIHRoZSBkeW5hbWljCiAgICAgICAgLy8gc3RlcmVvIGZpZWxkIERhdHRvcnJvIGRlc2NyaWJlcyAocC43NzYpLgogICAgICAgIGNvcmVzID0gW107CiAgICAgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKICAgICAgICAgICAgLy8gRGVmYXVsdCBWQUxVRVMgY29tZSBmcm9tIE1PRFVMQVRFRF9ERUxBWV9ERUZBVUxUUyAobW9kdWxhdGVkLWRlbGF5LWNvcmUudHMpCiAgICAgICAgICAgIC8vIOKAlCB0aGUgc2luZ2xlIHNvdXJjZSBvZiB0cnV0aCBzaGFyZWQgd2l0aCB0aGUgY29yZSB0ZXN0cy4gUmFuZ2VzIGxpdmUgaGVyZQogICAgICAgICAgICAvLyBhbmQgYm91bmQgdGhlIGJ1ZmZlciBzaXppbmcgKGRlbGF5VGltZSA8PSBNQVhfREVMQVlfTVMsIGRlcHRoIDw9CiAgICAgICAgICAgIC8vIE1BWF9ERVBUSF9NUykgYW5kIHN0YWJpbGl0eSAofGZlZWRiYWNrfCA8PSAwLjk5OTk5OTksIERhdHRvcnJvIFRhYmxlIDYpLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJkZWxheVRpbWUiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuZGVsYXlUaW1lLCAwLCAxMDAwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImRlcHRoIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLmRlcHRoLCAwLCA1MCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJyYXRlIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLnJhdGUsIDAsIDIwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbImZlZWRiYWNrIiwgTU9EVUxBVEVEX0RFTEFZX0RFRkFVTFRTLmZlZWRiYWNrLCAtMC45OTk5OTk5LCAwLjk5OTk5OTksICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiYmxlbmQiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuYmxlbmQsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZmVlZGZvcndhcmQiLCBNT0RVTEFURURfREVMQVlfREVGQVVMVFMuZmVlZGZvcndhcmQsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiaW50ZXJwb2xhdGlvbiIsIDAsIDAsIDEsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCB8fCAhb3V0cHV0IHx8IG91dHB1dC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIGstcmF0ZSBwYXJhbXM6IHRha2UgdGhlIGZpcnN0IChjb25zdGFudC1vdmVyLWJsb2NrKSB2YWx1ZS4KICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgZGVsYXlUaW1lOiBwYXJhbWV0ZXJzLmRlbGF5VGltZVswXSwKICAgICAgICAgICAgICAgIGRlcHRoOiBwYXJhbWV0ZXJzLmRlcHRoWzBdLAogICAgICAgICAgICAgICAgcmF0ZTogcGFyYW1ldGVycy5yYXRlWzBdLAogICAgICAgICAgICAgICAgZmVlZGJhY2s6IHBhcmFtZXRlcnMuZmVlZGJhY2tbMF0sCiAgICAgICAgICAgICAgICBibGVuZDogcGFyYW1ldGVycy5ibGVuZFswXSwKICAgICAgICAgICAgICAgIGZlZWRmb3J3YXJkOiBwYXJhbWV0ZXJzLmZlZWRmb3J3YXJkWzBdLAogICAgICAgICAgICAgICAgaW50ZXJwb2xhdGlvbjogaW50ZXJwb2xhdGlvbkZyb21JbmRleChwYXJhbWV0ZXJzLmludGVycG9sYXRpb25bMF0pLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gU2VlZCB0aGUgTEZPIDkwIGRlZyBhcGFydCBwZXIgY2hhbm5lbCBmb3IgdGhlIHF1YWRyYXR1cmUgc3RlcmVvIGZpZWxkCiAgICAgICAgICAgICAgICAgICAgLy8gKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBNb2R1bGF0ZWREZWxheVByb2Nlc3NvcihzYW1wbGVSYXRlLCAoY2ggKiBNYXRoLlBJKSAvIDIpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoIm1vZHVsYXRlZC1kZWxheSIsIE1vZHVsYXRlZERlbGF5V29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5Nb2R1bGF0ZWREZWxheVdvcmtsZXRQcm9jZXNzb3IgPSBNb2R1bGF0ZWREZWxheVdvcmtsZXRQcm9jZXNzb3I7CgogICAgcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPW1vZHVsYXRlZC1kZWxheS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHBjbVN0cmVhbSA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNsYXNzIFBjbVJpbmdCdWZmZXIgewogICAgICAgIGNhcGFjaXR5RnJhbWVzOwogICAgICAgIGNoYW5uZWxDb3VudDsKICAgICAgICBjaGFubmVsczsKICAgICAgICByZWFkSW5kZXggPSAwOwogICAgICAgIHdyaXRlSW5kZXggPSAwOwogICAgICAgIHNpemUgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKGNhcGFjaXR5RnJhbWVzLCBjaGFubmVsQ291bnQpIHsKICAgICAgICAgICAgdGhpcy5jYXBhY2l0eUZyYW1lcyA9IGNhcGFjaXR5RnJhbWVzOwogICAgICAgICAgICB0aGlzLmNoYW5uZWxDb3VudCA9IGNoYW5uZWxDb3VudDsKICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGNhcGFjaXR5RnJhbWVzKSB8fCBjYXBhY2l0eUZyYW1lcyA8PSAwKSB7CiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcigiUENNIHJpbmctYnVmZmVyIGNhcGFjaXR5IG11c3QgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGNoYW5uZWxDb3VudCkgfHwgY2hhbm5lbENvdW50IDw9IDApIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCJQQ00gY2hhbm5lbCBjb3VudCBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlciIpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY2hhbm5lbHMgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiBjaGFubmVsQ291bnQgfSwgKCkgPT4gbmV3IEZsb2F0MzJBcnJheShjYXBhY2l0eUZyYW1lcykpOwogICAgICAgIH0KICAgICAgICBnZXQgYnVmZmVyZWRGcmFtZXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnNpemU7CiAgICAgICAgfQogICAgICAgIHdyaXRlSW50ZXJsZWF2ZWQoc2FtcGxlcykgewogICAgICAgICAgICBpZiAoc2FtcGxlcy5sZW5ndGggJSB0aGlzLmNoYW5uZWxDb3VudCAhPT0gMCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYEludGVybGVhdmVkIFBDTSBsZW5ndGggbXVzdCBiZSBkaXZpc2libGUgYnkgY2hhbm5lbENvdW50ICgke3RoaXMuY2hhbm5lbENvdW50fSlgKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBmcmFtZUNvdW50ID0gc2FtcGxlcy5sZW5ndGggLyB0aGlzLmNoYW5uZWxDb3VudDsKICAgICAgICAgICAgaWYgKGZyYW1lQ291bnQgPiB0aGlzLmNhcGFjaXR5RnJhbWVzIC0gdGhpcy5zaXplKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZm9yIChsZXQgZnJhbWUgPSAwOyBmcmFtZSA8IGZyYW1lQ291bnQ7IGZyYW1lKyspIHsKICAgICAgICAgICAgICAgIGZvciAobGV0IGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgdGhpcy5jaGFubmVsQ291bnQ7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbm5lbHNbY2hhbm5lbF1bdGhpcy53cml0ZUluZGV4XSA9IHNhbXBsZXNbZnJhbWUgKiB0aGlzLmNoYW5uZWxDb3VudCArIGNoYW5uZWxdOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gKHRoaXMud3JpdGVJbmRleCArIDEpICUgdGhpcy5jYXBhY2l0eUZyYW1lczsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLnNpemUgKz0gZnJhbWVDb3VudDsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgICAgIHJlYWQob3V0cHV0KSB7CiAgICAgICAgICAgIGNvbnN0IGZyYW1lQ291bnQgPSBNYXRoLm1pbih0aGlzLnNpemUsIG91dHB1dFswXT8ubGVuZ3RoID8/IDApOwogICAgICAgICAgICBmb3IgKGxldCBmcmFtZSA9IDA7IGZyYW1lIDwgZnJhbWVDb3VudDsgZnJhbWUrKykgewogICAgICAgICAgICAgICAgZm9yIChsZXQgY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCBNYXRoLm1pbih0aGlzLmNoYW5uZWxDb3VudCwgb3V0cHV0Lmxlbmd0aCk7IGNoYW5uZWwrKykgewogICAgICAgICAgICAgICAgICAgIG91dHB1dFtjaGFubmVsXVtmcmFtZV0gPSB0aGlzLmNoYW5uZWxzW2NoYW5uZWxdW3RoaXMucmVhZEluZGV4XTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMucmVhZEluZGV4ID0gKHRoaXMucmVhZEluZGV4ICsgMSkgJSB0aGlzLmNhcGFjaXR5RnJhbWVzOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuc2l6ZSAtPSBmcmFtZUNvdW50OwogICAgICAgICAgICByZXR1cm4gZnJhbWVDb3VudDsKICAgICAgICB9CiAgICAgICAgY2xlYXIoKSB7CiAgICAgICAgICAgIHRoaXMucmVhZEluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy53cml0ZUluZGV4ID0gMDsKICAgICAgICAgICAgdGhpcy5zaXplID0gMDsKICAgICAgICB9CiAgICB9CiAgICAvKioKICAgICAqIENvbnRleHQtZnJlZSBzdGF0ZSBtYWNoaW5lIGZvciB0aGUgUENNIHN0cmVhbSB3b3JrbGV0LgogICAgICoKICAgICAqIFRoZSB3b3JrbGV0IHNoZWxsIG93bnMgTWVzc2FnZVBvcnQgcGx1bWJpbmc7IHRoaXMgY2xhc3Mgb3ducyB0aGUgZml4ZWQtc2l6ZQogICAgICogcmluZyBidWZmZXIsIGxhdGVuY3kgZ2F0ZSwgcGF1c2UvcmVzdW1lIHBvc2l0aW9uLCB1bmRlcnJ1biBlcGlzb2RlcywgYW5kCiAgICAgKiB0ZXJtaW5hbCBlbmQtb2YtaW5wdXQgYmVoYXZpb3IuCiAgICAgKi8KICAgIGNsYXNzIFBjbVN0cmVhbUVuZ2luZSB7CiAgICAgICAgcmluZzsKICAgICAgICBsYXRlbmN5RnJhbWVzOwogICAgICAgIHBsYXlpbmcgPSBmYWxzZTsKICAgICAgICBzdGFydGVkID0gZmFsc2U7CiAgICAgICAgaW5wdXRFbmRlZCA9IGZhbHNlOwogICAgICAgIHVuZGVycnVuQWN0aXZlID0gZmFsc2U7CiAgICAgICAgZW5kZWRSZXBvcnRlZCA9IGZhbHNlOwogICAgICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKICAgICAgICAgICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKG9wdGlvbnMubGF0ZW5jeUZyYW1lcykgfHwgb3B0aW9ucy5sYXRlbmN5RnJhbWVzIDwgMCkgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoIlBDTSBsYXRlbmN5IG11c3QgYmUgYSBub24tbmVnYXRpdmUgaW50ZWdlciBudW1iZXIgb2YgZnJhbWVzIik7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKG9wdGlvbnMubGF0ZW5jeUZyYW1lcyA+IG9wdGlvbnMuY2FwYWNpdHlGcmFtZXMpIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCJQQ00gbGF0ZW5jeSBjYW5ub3QgZXhjZWVkIHRoZSByaW5nLWJ1ZmZlciBjYXBhY2l0eSIpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMucmluZyA9IG5ldyBQY21SaW5nQnVmZmVyKG9wdGlvbnMuY2FwYWNpdHlGcmFtZXMsIG9wdGlvbnMuY2hhbm5lbENvdW50KTsKICAgICAgICAgICAgdGhpcy5sYXRlbmN5RnJhbWVzID0gb3B0aW9ucy5sYXRlbmN5RnJhbWVzOwogICAgICAgIH0KICAgICAgICBnZXQgYnVmZmVyZWRGcmFtZXMoKSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLnJpbmcuYnVmZmVyZWRGcmFtZXM7CiAgICAgICAgfQogICAgICAgIHdyaXRlSW50ZXJsZWF2ZWQoc2FtcGxlcykgewogICAgICAgICAgICBpZiAodGhpcy5pbnB1dEVuZGVkKSB7CiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCB3cml0ZSBQQ00gYWZ0ZXIgZW5kKCkiKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBhY2NlcHRlZCA9IHRoaXMucmluZy53cml0ZUludGVybGVhdmVkKHNhbXBsZXMpOwogICAgICAgICAgICBpZiAoYWNjZXB0ZWQpIHsKICAgICAgICAgICAgICAgIHRoaXMudW5kZXJydW5BY3RpdmUgPSBmYWxzZTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gYWNjZXB0ZWQ7CiAgICAgICAgfQogICAgICAgIHBsYXkoKSB7CiAgICAgICAgICAgIGlmICghdGhpcy5lbmRlZFJlcG9ydGVkKSB7CiAgICAgICAgICAgICAgICB0aGlzLnBsYXlpbmcgPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHBhdXNlKCkgewogICAgICAgICAgICB0aGlzLnBsYXlpbmcgPSBmYWxzZTsKICAgICAgICB9CiAgICAgICAgc3RvcCgpIHsKICAgICAgICAgICAgdGhpcy5yaW5nLmNsZWFyKCk7CiAgICAgICAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlOwogICAgICAgICAgICB0aGlzLnN0YXJ0ZWQgPSBmYWxzZTsKICAgICAgICAgICAgdGhpcy5pbnB1dEVuZGVkID0gZmFsc2U7CiAgICAgICAgICAgIHRoaXMudW5kZXJydW5BY3RpdmUgPSBmYWxzZTsKICAgICAgICAgICAgdGhpcy5lbmRlZFJlcG9ydGVkID0gZmFsc2U7CiAgICAgICAgfQogICAgICAgIGVuZCgpIHsKICAgICAgICAgICAgdGhpcy5pbnB1dEVuZGVkID0gdHJ1ZTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhvdXRwdXQpIHsKICAgICAgICAgICAgZm9yIChjb25zdCBjaGFubmVsIG9mIG91dHB1dCkgewogICAgICAgICAgICAgICAgY2hhbm5lbC5maWxsKDApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHsKICAgICAgICAgICAgICAgIGNvbnN1bWVkRnJhbWVzOiAwLAogICAgICAgICAgICAgICAgZW5kZWQ6IGZhbHNlLAogICAgICAgICAgICAgICAgdW5kZXJydW46IGZhbHNlLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBxdWFudHVtRnJhbWVzID0gb3V0cHV0WzBdPy5sZW5ndGggPz8gMDsKICAgICAgICAgICAgaWYgKCF0aGlzLnBsYXlpbmcgfHwgcXVhbnR1bUZyYW1lcyA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoIXRoaXMuc3RhcnRlZCkgewogICAgICAgICAgICAgICAgaWYgKHRoaXMucmluZy5idWZmZXJlZEZyYW1lcyA8IHRoaXMubGF0ZW5jeUZyYW1lcyAmJiAhdGhpcy5pbnB1dEVuZGVkKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRlZCA9IHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmVzdWx0LmNvbnN1bWVkRnJhbWVzID0gdGhpcy5yaW5nLnJlYWQob3V0cHV0KTsKICAgICAgICAgICAgaWYgKHRoaXMuaW5wdXRFbmRlZCAmJiB0aGlzLnJpbmcuYnVmZmVyZWRGcmFtZXMgPT09IDApIHsKICAgICAgICAgICAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlOwogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmVuZGVkUmVwb3J0ZWQpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZGVkUmVwb3J0ZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgIHJlc3VsdC5lbmRlZCA9IHRydWU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChyZXN1bHQuY29uc3VtZWRGcmFtZXMgPCBxdWFudHVtRnJhbWVzICYmIHRoaXMucmluZy5idWZmZXJlZEZyYW1lcyA9PT0gMCAmJiAhdGhpcy51bmRlcnJ1bkFjdGl2ZSkgewogICAgICAgICAgICAgICAgdGhpcy51bmRlcnJ1bkFjdGl2ZSA9IHRydWU7CiAgICAgICAgICAgICAgICByZXN1bHQudW5kZXJydW4gPSB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQXVkaW9Xb3JrbGV0IHNoZWxsIGZvciBwdXNoLWJhc2VkIFBDTSBwbGF5YmFjay4KICAgICAqCiAgICAgKiBBbGwgYnVmZmVyaW5nIGFuZCBwbGF5YmFjayBzdGF0ZSBsaXZlcyBpbiB0aGUgY29udGV4dC1mcmVlCiAgICAgKiB7QGxpbmsgUGNtU3RyZWFtRW5naW5lfTsgdGhpcyBjbGFzcyBvbmx5IHRyYW5zbGF0ZXMgTWVzc2FnZVBvcnQgY29tbWFuZHMsCiAgICAgKiByZW5kZXJzIG9uZSBXZWIgQXVkaW8gcXVhbnR1bSwgYW5kIHJlcG9ydHMgY29uc3VtcHRpb24vc3RhdGUgdXBzdHJlYW0uCiAgICAgKi8KICAgIGNsYXNzIFBjbVN0cmVhbVdvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIGVuZ2luZTsKICAgICAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CiAgICAgICAgICAgIHN1cGVyKG9wdGlvbnMpOwogICAgICAgICAgICBjb25zdCBwcm9jZXNzb3JPcHRpb25zID0gKG9wdGlvbnM/LnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwogICAgICAgICAgICB0aGlzLmVuZ2luZSA9IG5ldyBQY21TdHJlYW1FbmdpbmUoewogICAgICAgICAgICAgICAgY2FwYWNpdHlGcmFtZXM6IHByb2Nlc3Nvck9wdGlvbnMuY2FwYWNpdHlGcmFtZXMgPz8gc2FtcGxlUmF0ZSwKICAgICAgICAgICAgICAgIGNoYW5uZWxDb3VudDogcHJvY2Vzc29yT3B0aW9ucy5jaGFubmVsQ291bnQgPz8gMSwKICAgICAgICAgICAgICAgIGxhdGVuY3lGcmFtZXM6IHByb2Nlc3Nvck9wdGlvbnMubGF0ZW5jeUZyYW1lcyA/PyAwLAogICAgICAgICAgICB9KTsKICAgICAgICAgICAgdGhpcy5wb3J0Lm9ubWVzc2FnZSA9IChldmVudCkgPT4gewogICAgICAgICAgICAgICAgY29uc3QgY29tbWFuZCA9IGV2ZW50LmRhdGE7CiAgICAgICAgICAgICAgICBzd2l0Y2ggKGNvbW1hbmQudHlwZSkgewogICAgICAgICAgICAgICAgICAgIGNhc2UgIndyaXRlIjoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLmVuZ2luZS53cml0ZUludGVybGVhdmVkKGNvbW1hbmQuc2FtcGxlcykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZSh7IHR5cGU6ICJvdmVyZmxvdyIgfSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSAicGxheSI6CiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZW5naW5lLnBsYXkoKTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSAicGF1c2UiOgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5wYXVzZSgpOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlICJzdG9wIjoKICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbmdpbmUuc3RvcCgpOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlICJlbmQiOgogICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5lbmQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoX2lucHV0cywgb3V0cHV0cykgewogICAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLmVuZ2luZS5wcm9jZXNzKG91dHB1dHNbMF0gPz8gW10pOwogICAgICAgICAgICBpZiAocmVzdWx0LmNvbnN1bWVkRnJhbWVzID4gMCkgewogICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHsgdHlwZTogImNvbnN1bWVkIiwgZnJhbWVzOiByZXN1bHQuY29uc3VtZWRGcmFtZXMgfSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKHJlc3VsdC51bmRlcnJ1bikgewogICAgICAgICAgICAgICAgdGhpcy5wb3J0LnBvc3RNZXNzYWdlKHsgdHlwZTogInVuZGVycnVuIiB9KTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzdWx0LmVuZGVkKSB7CiAgICAgICAgICAgICAgICB0aGlzLnBvcnQucG9zdE1lc3NhZ2UoeyB0eXBlOiAiZW5kZWQiIH0pOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJwY20tc3RyZWFtIiwgUGNtU3RyZWFtV29ya2xldFByb2Nlc3Nvcik7CgogICAgZXhwb3J0cy5QY21TdHJlYW1Xb3JrbGV0UHJvY2Vzc29yID0gUGNtU3RyZWFtV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9cGNtLXN0cmVhbS1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHBoYXNlVm9jb2RlciA9IChmdW5jdGlvbiAoZXhwb3J0cykgewoJJ3VzZSBzdHJpY3QnOwoKCWZ1bmN0aW9uIGdldERlZmF1bHRFeHBvcnRGcm9tQ2pzICh4KSB7CgkJcmV0dXJuIHggJiYgeC5fX2VzTW9kdWxlICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnZGVmYXVsdCcpID8geFsnZGVmYXVsdCddIDogeDsKCX0KCgl2YXIgZmZ0OwoJdmFyIGhhc1JlcXVpcmVkRmZ0OwoKCWZ1bmN0aW9uIHJlcXVpcmVGZnQgKCkgewoJCWlmIChoYXNSZXF1aXJlZEZmdCkgcmV0dXJuIGZmdDsKCQloYXNSZXF1aXJlZEZmdCA9IDE7CgoJCWZ1bmN0aW9uIEZGVChzaXplKSB7CgkJICB0aGlzLnNpemUgPSBzaXplIHwgMDsKCQkgIGlmICh0aGlzLnNpemUgPD0gMSB8fCAodGhpcy5zaXplICYgKHRoaXMuc2l6ZSAtIDEpKSAhPT0gMCkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdGRlQgc2l6ZSBtdXN0IGJlIGEgcG93ZXIgb2YgdHdvIGFuZCBiaWdnZXIgdGhhbiAxJyk7CgoJCSAgdGhpcy5fY3NpemUgPSBzaXplIDw8IDE7CgoJCSAgLy8gTk9URTogVXNlIG9mIGB2YXJgIGlzIGludGVudGlvbmFsIGZvciBvbGQgVjggdmVyc2lvbnMKCQkgIHZhciB0YWJsZSA9IG5ldyBBcnJheSh0aGlzLnNpemUgKiAyKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgdGFibGUubGVuZ3RoOyBpICs9IDIpIHsKCQkgICAgY29uc3QgYW5nbGUgPSBNYXRoLlBJICogaSAvIHRoaXMuc2l6ZTsKCQkgICAgdGFibGVbaV0gPSBNYXRoLmNvcyhhbmdsZSk7CgkJICAgIHRhYmxlW2kgKyAxXSA9IC1NYXRoLnNpbihhbmdsZSk7CgkJICB9CgkJICB0aGlzLnRhYmxlID0gdGFibGU7CgoJCSAgLy8gRmluZCBzaXplJ3MgcG93ZXIgb2YgdHdvCgkJICB2YXIgcG93ZXIgPSAwOwoJCSAgZm9yICh2YXIgdCA9IDE7IHRoaXMuc2l6ZSA+IHQ7IHQgPDw9IDEpCgkJICAgIHBvd2VyKys7CgoJCSAgLy8gQ2FsY3VsYXRlIGluaXRpYWwgc3RlcCdzIHdpZHRoOgoJCSAgLy8gICAqIElmIHdlIGFyZSBmdWxsIHJhZGl4LTQgLSBpdCBpcyAyeCBzbWFsbGVyIHRvIGdpdmUgaW5pdGFsIGxlbj04CgkJICAvLyAgICogT3RoZXJ3aXNlIGl0IGlzIHRoZSBzYW1lIGFzIGBwb3dlcmAgdG8gZ2l2ZSBsZW49NAoJCSAgdGhpcy5fd2lkdGggPSBwb3dlciAlIDIgPT09IDAgPyBwb3dlciAtIDEgOiBwb3dlcjsKCgkJICAvLyBQcmUtY29tcHV0ZSBiaXQtcmV2ZXJzYWwgcGF0dGVybnMKCQkgIHRoaXMuX2JpdHJldiA9IG5ldyBBcnJheSgxIDw8IHRoaXMuX3dpZHRoKTsKCQkgIGZvciAodmFyIGogPSAwOyBqIDwgdGhpcy5fYml0cmV2Lmxlbmd0aDsgaisrKSB7CgkJICAgIHRoaXMuX2JpdHJldltqXSA9IDA7CgkJICAgIGZvciAodmFyIHNoaWZ0ID0gMDsgc2hpZnQgPCB0aGlzLl93aWR0aDsgc2hpZnQgKz0gMikgewoJCSAgICAgIHZhciByZXZTaGlmdCA9IHRoaXMuX3dpZHRoIC0gc2hpZnQgLSAyOwoJCSAgICAgIHRoaXMuX2JpdHJldltqXSB8PSAoKGogPj4+IHNoaWZ0KSAmIDMpIDw8IHJldlNoaWZ0OwoJCSAgICB9CgkJICB9CgoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCSAgdGhpcy5faW52ID0gMDsKCQl9CgkJZmZ0ID0gRkZUOwoKCQlGRlQucHJvdG90eXBlLmZyb21Db21wbGV4QXJyYXkgPSBmdW5jdGlvbiBmcm9tQ29tcGxleEFycmF5KGNvbXBsZXgsIHN0b3JhZ2UpIHsKCQkgIHZhciByZXMgPSBzdG9yYWdlIHx8IG5ldyBBcnJheShjb21wbGV4Lmxlbmd0aCA+Pj4gMSk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IGNvbXBsZXgubGVuZ3RoOyBpICs9IDIpCgkJICAgIHJlc1tpID4+PiAxXSA9IGNvbXBsZXhbaV07CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuY3JlYXRlQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gY3JlYXRlQ29tcGxleEFycmF5KCkgewoJCSAgY29uc3QgcmVzID0gbmV3IEFycmF5KHRoaXMuX2NzaXplKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzLmxlbmd0aDsgaSsrKQoJCSAgICByZXNbaV0gPSAwOwoJCSAgcmV0dXJuIHJlczsKCQl9OwoKCQlGRlQucHJvdG90eXBlLnRvQ29tcGxleEFycmF5ID0gZnVuY3Rpb24gdG9Db21wbGV4QXJyYXkoaW5wdXQsIHN0b3JhZ2UpIHsKCQkgIHZhciByZXMgPSBzdG9yYWdlIHx8IHRoaXMuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkJICBmb3IgKHZhciBpID0gMDsgaSA8IHJlcy5sZW5ndGg7IGkgKz0gMikgewoJCSAgICByZXNbaV0gPSBpbnB1dFtpID4+PiAxXTsKCQkgICAgcmVzW2kgKyAxXSA9IDA7CgkJICB9CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuY29tcGxldGVTcGVjdHJ1bSA9IGZ1bmN0aW9uIGNvbXBsZXRlU3BlY3RydW0oc3BlY3RydW0pIHsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgkJICB2YXIgaGFsZiA9IHNpemUgPj4+IDE7CgkJICBmb3IgKHZhciBpID0gMjsgaSA8IGhhbGY7IGkgKz0gMikgewoJCSAgICBzcGVjdHJ1bVtzaXplIC0gaV0gPSBzcGVjdHJ1bVtpXTsKCQkgICAgc3BlY3RydW1bc2l6ZSAtIGkgKyAxXSA9IC1zcGVjdHJ1bVtpICsgMV07CgkJICB9CgkJfTsKCgkJRkZULnByb3RvdHlwZS50cmFuc2Zvcm0gPSBmdW5jdGlvbiB0cmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMDsKCQkgIHRoaXMuX3RyYW5zZm9ybTQoKTsKCQkgIHRoaXMuX291dCA9IG51bGw7CgkJICB0aGlzLl9kYXRhID0gbnVsbDsKCQl9OwoKCQlGRlQucHJvdG90eXBlLnJlYWxUcmFuc2Zvcm0gPSBmdW5jdGlvbiByZWFsVHJhbnNmb3JtKG91dCwgZGF0YSkgewoJCSAgaWYgKG91dCA9PT0gZGF0YSkKCQkgICAgdGhyb3cgbmV3IEVycm9yKCdJbnB1dCBhbmQgb3V0cHV0IGJ1ZmZlcnMgbXVzdCBiZSBkaWZmZXJlbnQnKTsKCgkJICB0aGlzLl9vdXQgPSBvdXQ7CgkJICB0aGlzLl9kYXRhID0gZGF0YTsKCQkgIHRoaXMuX2ludiA9IDA7CgkJICB0aGlzLl9yZWFsVHJhbnNmb3JtNCgpOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCUZGVC5wcm90b3R5cGUuaW52ZXJzZVRyYW5zZm9ybSA9IGZ1bmN0aW9uIGludmVyc2VUcmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMTsKCQkgIHRoaXMuX3RyYW5zZm9ybTQoKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgb3V0Lmxlbmd0aDsgaSsrKQoJCSAgICBvdXRbaV0gLz0gdGhpcy5zaXplOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCS8vIHJhZGl4LTQgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IFVzZXMgb2YgYHZhcmAgYXJlIGludGVudGlvbmFsIGZvciBvbGRlciBWOCB2ZXJzaW9uIHRoYXQgZG8gbm90CgkJLy8gc3VwcG9ydCBib3RoIGBsZXQgY29tcG91bmQgYXNzaWdubWVudHNgIGFuZCBgY29uc3QgcGhpYAoJCUZGVC5wcm90b3R5cGUuX3RyYW5zZm9ybTQgPSBmdW5jdGlvbiBfdHJhbnNmb3JtNCgpIHsKCQkgIHZhciBvdXQgPSB0aGlzLl9vdXQ7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoKCQkgIC8vIEluaXRpYWwgc3RlcCAocGVybXV0ZSBhbmQgdHJhbnNmb3JtKQoJCSAgdmFyIHdpZHRoID0gdGhpcy5fd2lkdGg7CgkJICB2YXIgc3RlcCA9IDEgPDwgd2lkdGg7CgkJICB2YXIgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoKCQkgIHZhciBvdXRPZmY7CgkJICB2YXIgdDsKCQkgIHZhciBiaXRyZXYgPSB0aGlzLl9iaXRyZXY7CgkJICBpZiAobGVuID09PSA0KSB7CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmLCBzdGVwKTsKCQkgICAgfQoJCSAgfSBlbHNlIHsKCQkgICAgLy8gbGVuID09PSA4CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVUcmFuc2Zvcm00KG91dE9mZiwgb2ZmLCBzdGVwKTsKCQkgICAgfQoJCSAgfQoKCQkgIC8vIExvb3AgdGhyb3VnaCBzdGVwcyBpbiBkZWNyZWFzaW5nIG9yZGVyCgkJICB2YXIgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgdmFyIHRhYmxlID0gdGhpcy50YWJsZTsKCQkgIGZvciAoc3RlcCA+Pj0gMjsgc3RlcCA+PSAyOyBzdGVwID4+PSAyKSB7CgkJICAgIGxlbiA9IChzaXplIC8gc3RlcCkgPDwgMTsKCQkgICAgdmFyIHF1YXJ0ZXJMZW4gPSBsZW4gPj4+IDI7CgoJCSAgICAvLyBMb29wIHRocm91Z2ggb2Zmc2V0cyBpbiB0aGUgZGF0YQoJCSAgICBmb3IgKG91dE9mZiA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4pIHsKCQkgICAgICAvLyBGdWxsIGNhc2UKCQkgICAgICB2YXIgbGltaXQgPSBvdXRPZmYgKyBxdWFydGVyTGVuOwoJCSAgICAgIGZvciAodmFyIGkgPSBvdXRPZmYsIGsgPSAwOyBpIDwgbGltaXQ7IGkgKz0gMiwgayArPSBzdGVwKSB7CgkJICAgICAgICBjb25zdCBBID0gaTsKCQkgICAgICAgIGNvbnN0IEIgPSBBICsgcXVhcnRlckxlbjsKCQkgICAgICAgIGNvbnN0IEMgPSBCICsgcXVhcnRlckxlbjsKCQkgICAgICAgIGNvbnN0IEQgPSBDICsgcXVhcnRlckxlbjsKCgkJICAgICAgICAvLyBPcmlnaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IEFyID0gb3V0W0FdOwoJCSAgICAgICAgY29uc3QgQWkgPSBvdXRbQSArIDFdOwoJCSAgICAgICAgY29uc3QgQnIgPSBvdXRbQl07CgkJICAgICAgICBjb25zdCBCaSA9IG91dFtCICsgMV07CgkJICAgICAgICBjb25zdCBDciA9IG91dFtDXTsKCQkgICAgICAgIGNvbnN0IENpID0gb3V0W0MgKyAxXTsKCQkgICAgICAgIGNvbnN0IERyID0gb3V0W0RdOwoJCSAgICAgICAgY29uc3QgRGkgPSBvdXRbRCArIDFdOwoKCQkgICAgICAgIC8vIE1pZGRsZSB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IE1BciA9IEFyOwoJCSAgICAgICAgY29uc3QgTUFpID0gQWk7CgoJCSAgICAgICAgY29uc3QgdGFibGVCciA9IHRhYmxlW2tdOwoJCSAgICAgICAgY29uc3QgdGFibGVCaSA9IGludiAqIHRhYmxlW2sgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1CciA9IEJyICogdGFibGVCciAtIEJpICogdGFibGVCaTsKCQkgICAgICAgIGNvbnN0IE1CaSA9IEJyICogdGFibGVCaSArIEJpICogdGFibGVCcjsKCgkJICAgICAgICBjb25zdCB0YWJsZUNyID0gdGFibGVbMiAqIGtdOwoJCSAgICAgICAgY29uc3QgdGFibGVDaSA9IGludiAqIHRhYmxlWzIgKiBrICsgMV07CgkJICAgICAgICBjb25zdCBNQ3IgPSBDciAqIHRhYmxlQ3IgLSBDaSAqIHRhYmxlQ2k7CgkJICAgICAgICBjb25zdCBNQ2kgPSBDciAqIHRhYmxlQ2kgKyBDaSAqIHRhYmxlQ3I7CgoJCSAgICAgICAgY29uc3QgdGFibGVEciA9IHRhYmxlWzMgKiBrXTsKCQkgICAgICAgIGNvbnN0IHRhYmxlRGkgPSBpbnYgKiB0YWJsZVszICogayArIDFdOwoJCSAgICAgICAgY29uc3QgTURyID0gRHIgKiB0YWJsZURyIC0gRGkgKiB0YWJsZURpOwoJCSAgICAgICAgY29uc3QgTURpID0gRHIgKiB0YWJsZURpICsgRGkgKiB0YWJsZURyOwoKCQkgICAgICAgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IFQwciA9IE1BciArIE1DcjsKCQkgICAgICAgIGNvbnN0IFQwaSA9IE1BaSArIE1DaTsKCQkgICAgICAgIGNvbnN0IFQxciA9IE1BciAtIE1DcjsKCQkgICAgICAgIGNvbnN0IFQxaSA9IE1BaSAtIE1DaTsKCQkgICAgICAgIGNvbnN0IFQyciA9IE1CciArIE1EcjsKCQkgICAgICAgIGNvbnN0IFQyaSA9IE1CaSArIE1EaTsKCQkgICAgICAgIGNvbnN0IFQzciA9IGludiAqIChNQnIgLSBNRHIpOwoJCSAgICAgICAgY29uc3QgVDNpID0gaW52ICogKE1CaSAtIE1EaSk7CgoJCSAgICAgICAgLy8gRmluYWwgdmFsdWVzCgkJICAgICAgICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgkJICAgICAgICBjb25zdCBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgICAgICAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoJCSAgICAgICAgY29uc3QgRkNpID0gVDBpIC0gVDJpOwoKCQkgICAgICAgIGNvbnN0IEZCciA9IFQxciArIFQzaTsKCQkgICAgICAgIGNvbnN0IEZCaSA9IFQxaSAtIFQzcjsKCgkJICAgICAgICBjb25zdCBGRHIgPSBUMXIgLSBUM2k7CgkJICAgICAgICBjb25zdCBGRGkgPSBUMWkgKyBUM3I7CgoJCSAgICAgICAgb3V0W0FdID0gRkFyOwoJCSAgICAgICAgb3V0W0EgKyAxXSA9IEZBaTsKCQkgICAgICAgIG91dFtCXSA9IEZCcjsKCQkgICAgICAgIG91dFtCICsgMV0gPSBGQmk7CgkJICAgICAgICBvdXRbQ10gPSBGQ3I7CgkJICAgICAgICBvdXRbQyArIDFdID0gRkNpOwoJCSAgICAgICAgb3V0W0RdID0gRkRyOwoJCSAgICAgICAgb3V0W0QgKyAxXSA9IEZEaTsKCQkgICAgICB9CgkJICAgIH0KCQkgIH0KCQl9OwoKCQkvLyByYWRpeC0yIGltcGxlbWVudGF0aW9uCgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTQKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVUcmFuc2Zvcm0yID0gZnVuY3Rpb24gX3NpbmdsZVRyYW5zZm9ybTIob3V0T2ZmLCBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoKCQkgIGNvbnN0IGV2ZW5SID0gZGF0YVtvZmZdOwoJCSAgY29uc3QgZXZlbkkgPSBkYXRhW29mZiArIDFdOwoJCSAgY29uc3Qgb2RkUiA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBvZGRJID0gZGF0YVtvZmYgKyBzdGVwICsgMV07CgoJCSAgY29uc3QgbGVmdFIgPSBldmVuUiArIG9kZFI7CgkJICBjb25zdCBsZWZ0SSA9IGV2ZW5JICsgb2RkSTsKCQkgIGNvbnN0IHJpZ2h0UiA9IGV2ZW5SIC0gb2RkUjsKCQkgIGNvbnN0IHJpZ2h0SSA9IGV2ZW5JIC0gb2RkSTsKCgkJICBvdXRbb3V0T2ZmXSA9IGxlZnRSOwoJCSAgb3V0W291dE9mZiArIDFdID0gbGVmdEk7CgkJICBvdXRbb3V0T2ZmICsgMl0gPSByaWdodFI7CgkJICBvdXRbb3V0T2ZmICsgM10gPSByaWdodEk7CgkJfTsKCgkJLy8gcmFkaXgtNAoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj04CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9zaW5nbGVUcmFuc2Zvcm00KG91dE9mZiwgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCQkgIGNvbnN0IGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIGNvbnN0IHN0ZXAyID0gc3RlcCAqIDI7CgkJICBjb25zdCBzdGVwMyA9IHN0ZXAgKiAzOwoKCQkgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgY29uc3QgQXIgPSBkYXRhW29mZl07CgkJICBjb25zdCBBaSA9IGRhdGFbb2ZmICsgMV07CgkJICBjb25zdCBCciA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBCaSA9IGRhdGFbb2ZmICsgc3RlcCArIDFdOwoJCSAgY29uc3QgQ3IgPSBkYXRhW29mZiArIHN0ZXAyXTsKCQkgIGNvbnN0IENpID0gZGF0YVtvZmYgKyBzdGVwMiArIDFdOwoJCSAgY29uc3QgRHIgPSBkYXRhW29mZiArIHN0ZXAzXTsKCQkgIGNvbnN0IERpID0gZGF0YVtvZmYgKyBzdGVwMyArIDFdOwoKCQkgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IFQwciA9IEFyICsgQ3I7CgkJICBjb25zdCBUMGkgPSBBaSArIENpOwoJCSAgY29uc3QgVDFyID0gQXIgLSBDcjsKCQkgIGNvbnN0IFQxaSA9IEFpIC0gQ2k7CgkJICBjb25zdCBUMnIgPSBCciArIERyOwoJCSAgY29uc3QgVDJpID0gQmkgKyBEaTsKCQkgIGNvbnN0IFQzciA9IGludiAqIChCciAtIERyKTsKCQkgIGNvbnN0IFQzaSA9IGludiAqIChCaSAtIERpKTsKCgkJICAvLyBGaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCQkgIGNvbnN0IEZBaSA9IFQwaSArIFQyaTsKCgkJICBjb25zdCBGQnIgPSBUMXIgKyBUM2k7CgkJICBjb25zdCBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgY29uc3QgRkNyID0gVDByIC0gVDJyOwoJCSAgY29uc3QgRkNpID0gVDBpIC0gVDJpOwoKCQkgIGNvbnN0IEZEciA9IFQxciAtIFQzaTsKCQkgIGNvbnN0IEZEaSA9IFQxaSArIFQzcjsKCgkJICBvdXRbb3V0T2ZmXSA9IEZBcjsKCQkgIG91dFtvdXRPZmYgKyAxXSA9IEZBaTsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IEZCcjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IEZCaTsKCQkgIG91dFtvdXRPZmYgKyA0XSA9IEZDcjsKCQkgIG91dFtvdXRPZmYgKyA1XSA9IEZDaTsKCQkgIG91dFtvdXRPZmYgKyA2XSA9IEZEcjsKCQkgIG91dFtvdXRPZmYgKyA3XSA9IEZEaTsKCQl9OwoKCQkvLyBSZWFsIGlucHV0IHJhZGl4LTQgaW1wbGVtZW50YXRpb24KCQlGRlQucHJvdG90eXBlLl9yZWFsVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9yZWFsVHJhbnNmb3JtNCgpIHsKCQkgIHZhciBvdXQgPSB0aGlzLl9vdXQ7CgkJICB2YXIgc2l6ZSA9IHRoaXMuX2NzaXplOwoKCQkgIC8vIEluaXRpYWwgc3RlcCAocGVybXV0ZSBhbmQgdHJhbnNmb3JtKQoJCSAgdmFyIHdpZHRoID0gdGhpcy5fd2lkdGg7CgkJICB2YXIgc3RlcCA9IDEgPDwgd2lkdGg7CgkJICB2YXIgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoKCQkgIHZhciBvdXRPZmY7CgkJICB2YXIgdDsKCQkgIHZhciBiaXRyZXYgPSB0aGlzLl9iaXRyZXY7CgkJICBpZiAobGVuID09PSA0KSB7CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVSZWFsVHJhbnNmb3JtMihvdXRPZmYsIG9mZiA+Pj4gMSwgc3RlcCA+Pj4gMSk7CgkJICAgIH0KCQkgIH0gZWxzZSB7CgkJICAgIC8vIGxlbiA9PT0gOAoJCSAgICBmb3IgKG91dE9mZiA9IDAsIHQgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuLCB0KyspIHsKCQkgICAgICBjb25zdCBvZmYgPSBiaXRyZXZbdF07CgkJICAgICAgdGhpcy5fc2luZ2xlUmVhbFRyYW5zZm9ybTQob3V0T2ZmLCBvZmYgPj4+IDEsIHN0ZXAgPj4+IDEpOwoJCSAgICB9CgkJICB9CgoJCSAgLy8gTG9vcCB0aHJvdWdoIHN0ZXBzIGluIGRlY3JlYXNpbmcgb3JkZXIKCQkgIHZhciBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICB2YXIgdGFibGUgPSB0aGlzLnRhYmxlOwoJCSAgZm9yIChzdGVwID4+PSAyOyBzdGVwID49IDI7IHN0ZXAgPj49IDIpIHsKCQkgICAgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoJCSAgICB2YXIgaGFsZkxlbiA9IGxlbiA+Pj4gMTsKCQkgICAgdmFyIHF1YXJ0ZXJMZW4gPSBoYWxmTGVuID4+PiAxOwoJCSAgICB2YXIgaHF1YXJ0ZXJMZW4gPSBxdWFydGVyTGVuID4+PiAxOwoKCQkgICAgLy8gTG9vcCB0aHJvdWdoIG9mZnNldHMgaW4gdGhlIGRhdGEKCQkgICAgZm9yIChvdXRPZmYgPSAwOyBvdXRPZmYgPCBzaXplOyBvdXRPZmYgKz0gbGVuKSB7CgkJICAgICAgZm9yICh2YXIgaSA9IDAsIGsgPSAwOyBpIDw9IGhxdWFydGVyTGVuOyBpICs9IDIsIGsgKz0gc3RlcCkgewoJCSAgICAgICAgdmFyIEEgPSBvdXRPZmYgKyBpOwoJCSAgICAgICAgdmFyIEIgPSBBICsgcXVhcnRlckxlbjsKCQkgICAgICAgIHZhciBDID0gQiArIHF1YXJ0ZXJMZW47CgkJICAgICAgICB2YXIgRCA9IEMgKyBxdWFydGVyTGVuOwoKCQkgICAgICAgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIEFyID0gb3V0W0FdOwoJCSAgICAgICAgdmFyIEFpID0gb3V0W0EgKyAxXTsKCQkgICAgICAgIHZhciBCciA9IG91dFtCXTsKCQkgICAgICAgIHZhciBCaSA9IG91dFtCICsgMV07CgkJICAgICAgICB2YXIgQ3IgPSBvdXRbQ107CgkJICAgICAgICB2YXIgQ2kgPSBvdXRbQyArIDFdOwoJCSAgICAgICAgdmFyIERyID0gb3V0W0RdOwoJCSAgICAgICAgdmFyIERpID0gb3V0W0QgKyAxXTsKCgkJICAgICAgICAvLyBNaWRkbGUgdmFsdWVzCgkJICAgICAgICB2YXIgTUFyID0gQXI7CgkJICAgICAgICB2YXIgTUFpID0gQWk7CgoJCSAgICAgICAgdmFyIHRhYmxlQnIgPSB0YWJsZVtrXTsKCQkgICAgICAgIHZhciB0YWJsZUJpID0gaW52ICogdGFibGVbayArIDFdOwoJCSAgICAgICAgdmFyIE1CciA9IEJyICogdGFibGVCciAtIEJpICogdGFibGVCaTsKCQkgICAgICAgIHZhciBNQmkgPSBCciAqIHRhYmxlQmkgKyBCaSAqIHRhYmxlQnI7CgoJCSAgICAgICAgdmFyIHRhYmxlQ3IgPSB0YWJsZVsyICoga107CgkJICAgICAgICB2YXIgdGFibGVDaSA9IGludiAqIHRhYmxlWzIgKiBrICsgMV07CgkJICAgICAgICB2YXIgTUNyID0gQ3IgKiB0YWJsZUNyIC0gQ2kgKiB0YWJsZUNpOwoJCSAgICAgICAgdmFyIE1DaSA9IENyICogdGFibGVDaSArIENpICogdGFibGVDcjsKCgkJICAgICAgICB2YXIgdGFibGVEciA9IHRhYmxlWzMgKiBrXTsKCQkgICAgICAgIHZhciB0YWJsZURpID0gaW52ICogdGFibGVbMyAqIGsgKyAxXTsKCQkgICAgICAgIHZhciBNRHIgPSBEciAqIHRhYmxlRHIgLSBEaSAqIHRhYmxlRGk7CgkJICAgICAgICB2YXIgTURpID0gRHIgKiB0YWJsZURpICsgRGkgKiB0YWJsZURyOwoKCQkgICAgICAgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBUMHIgPSBNQXIgKyBNQ3I7CgkJICAgICAgICB2YXIgVDBpID0gTUFpICsgTUNpOwoJCSAgICAgICAgdmFyIFQxciA9IE1BciAtIE1DcjsKCQkgICAgICAgIHZhciBUMWkgPSBNQWkgLSBNQ2k7CgkJICAgICAgICB2YXIgVDJyID0gTUJyICsgTURyOwoJCSAgICAgICAgdmFyIFQyaSA9IE1CaSArIE1EaTsKCQkgICAgICAgIHZhciBUM3IgPSBpbnYgKiAoTUJyIC0gTURyKTsKCQkgICAgICAgIHZhciBUM2kgPSBpbnYgKiAoTUJpIC0gTURpKTsKCgkJICAgICAgICAvLyBGaW5hbCB2YWx1ZXMKCQkgICAgICAgIHZhciBGQXIgPSBUMHIgKyBUMnI7CgkJICAgICAgICB2YXIgRkFpID0gVDBpICsgVDJpOwoKCQkgICAgICAgIHZhciBGQnIgPSBUMXIgKyBUM2k7CgkJICAgICAgICB2YXIgRkJpID0gVDFpIC0gVDNyOwoKCQkgICAgICAgIG91dFtBXSA9IEZBcjsKCQkgICAgICAgIG91dFtBICsgMV0gPSBGQWk7CgkJICAgICAgICBvdXRbQl0gPSBGQnI7CgkJICAgICAgICBvdXRbQiArIDFdID0gRkJpOwoKCQkgICAgICAgIC8vIE91dHB1dCBmaW5hbCBtaWRkbGUgcG9pbnQKCQkgICAgICAgIGlmIChpID09PSAwKSB7CgkJICAgICAgICAgIHZhciBGQ3IgPSBUMHIgLSBUMnI7CgkJICAgICAgICAgIHZhciBGQ2kgPSBUMGkgLSBUMmk7CgkJICAgICAgICAgIG91dFtDXSA9IEZDcjsKCQkgICAgICAgICAgb3V0W0MgKyAxXSA9IEZDaTsKCQkgICAgICAgICAgY29udGludWU7CgkJICAgICAgICB9CgoJCSAgICAgICAgLy8gRG8gbm90IG92ZXJ3cml0ZSBvdXJzZWx2ZXMKCQkgICAgICAgIGlmIChpID09PSBocXVhcnRlckxlbikKCQkgICAgICAgICAgY29udGludWU7CgoJCSAgICAgICAgLy8gSW4gdGhlIGZsaXBwZWQgY2FzZToKCQkgICAgICAgIC8vIE1BaSA9IC1NQWkKCQkgICAgICAgIC8vIE1Ccj0tTUJpLCBNQmk9LU1CcgoJCSAgICAgICAgLy8gTUNyPS1NQ3IKCQkgICAgICAgIC8vIE1Ecj1NRGksIE1EaT1NRHIKCQkgICAgICAgIHZhciBTVDByID0gVDFyOwoJCSAgICAgICAgdmFyIFNUMGkgPSAtVDFpOwoJCSAgICAgICAgdmFyIFNUMXIgPSBUMHI7CgkJICAgICAgICB2YXIgU1QxaSA9IC1UMGk7CgkJICAgICAgICB2YXIgU1QyciA9IC1pbnYgKiBUM2k7CgkJICAgICAgICB2YXIgU1QyaSA9IC1pbnYgKiBUM3I7CgkJICAgICAgICB2YXIgU1QzciA9IC1pbnYgKiBUMmk7CgkJICAgICAgICB2YXIgU1QzaSA9IC1pbnYgKiBUMnI7CgoJCSAgICAgICAgdmFyIFNGQXIgPSBTVDByICsgU1QycjsKCQkgICAgICAgIHZhciBTRkFpID0gU1QwaSArIFNUMmk7CgoJCSAgICAgICAgdmFyIFNGQnIgPSBTVDFyICsgU1QzaTsKCQkgICAgICAgIHZhciBTRkJpID0gU1QxaSAtIFNUM3I7CgoJCSAgICAgICAgdmFyIFNBID0gb3V0T2ZmICsgcXVhcnRlckxlbiAtIGk7CgkJICAgICAgICB2YXIgU0IgPSBvdXRPZmYgKyBoYWxmTGVuIC0gaTsKCgkJICAgICAgICBvdXRbU0FdID0gU0ZBcjsKCQkgICAgICAgIG91dFtTQSArIDFdID0gU0ZBaTsKCQkgICAgICAgIG91dFtTQl0gPSBTRkJyOwoJCSAgICAgICAgb3V0W1NCICsgMV0gPSBTRkJpOwoJCSAgICAgIH0KCQkgICAgfQoJCSAgfQoJCX07CgoJCS8vIHJhZGl4LTIgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49NAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVJlYWxUcmFuc2Zvcm0yID0gZnVuY3Rpb24gX3NpbmdsZVJlYWxUcmFuc2Zvcm0yKG91dE9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgoJCSAgY29uc3QgZXZlblIgPSBkYXRhW29mZl07CgkJICBjb25zdCBvZGRSID0gZGF0YVtvZmYgKyBzdGVwXTsKCgkJICBjb25zdCBsZWZ0UiA9IGV2ZW5SICsgb2RkUjsKCQkgIGNvbnN0IHJpZ2h0UiA9IGV2ZW5SIC0gb2RkUjsKCgkJICBvdXRbb3V0T2ZmXSA9IGxlZnRSOwoJCSAgb3V0W291dE9mZiArIDFdID0gMDsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IHJpZ2h0UjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IDA7CgkJfTsKCgkJLy8gcmFkaXgtNAoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj04CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlUmVhbFRyYW5zZm9ybTQgPSBmdW5jdGlvbiBfc2luZ2xlUmVhbFRyYW5zZm9ybTQob3V0T2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCQkgIGNvbnN0IGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIGNvbnN0IHN0ZXAyID0gc3RlcCAqIDI7CgkJICBjb25zdCBzdGVwMyA9IHN0ZXAgKiAzOwoKCQkgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgY29uc3QgQXIgPSBkYXRhW29mZl07CgkJICBjb25zdCBCciA9IGRhdGFbb2ZmICsgc3RlcF07CgkJICBjb25zdCBDciA9IGRhdGFbb2ZmICsgc3RlcDJdOwoJCSAgY29uc3QgRHIgPSBkYXRhW29mZiArIHN0ZXAzXTsKCgkJICAvLyBQcmUtRmluYWwgdmFsdWVzCgkJICBjb25zdCBUMHIgPSBBciArIENyOwoJCSAgY29uc3QgVDFyID0gQXIgLSBDcjsKCQkgIGNvbnN0IFQyciA9IEJyICsgRHI7CgkJICBjb25zdCBUM3IgPSBpbnYgKiAoQnIgLSBEcik7CgoJCSAgLy8gRmluYWwgdmFsdWVzCgkJICBjb25zdCBGQXIgPSBUMHIgKyBUMnI7CgoJCSAgY29uc3QgRkJyID0gVDFyOwoJCSAgY29uc3QgRkJpID0gLVQzcjsKCgkJICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgoJCSAgY29uc3QgRkRyID0gVDFyOwoJCSAgY29uc3QgRkRpID0gVDNyOwoKCQkgIG91dFtvdXRPZmZdID0gRkFyOwoJCSAgb3V0W291dE9mZiArIDFdID0gMDsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IEZCcjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IEZCaTsKCQkgIG91dFtvdXRPZmYgKyA0XSA9IEZDcjsKCQkgIG91dFtvdXRPZmYgKyA1XSA9IDA7CgkJICBvdXRbb3V0T2ZmICsgNl0gPSBGRHI7CgkJICBvdXRbb3V0T2ZmICsgN10gPSBGRGk7CgkJfTsKCQlyZXR1cm4gZmZ0OwoJfQoKCXZhciBmZnRFeHBvcnRzID0gcmVxdWlyZUZmdCgpOwoJdmFyIEZGVCA9IC8qQF9fUFVSRV9fKi9nZXREZWZhdWx0RXhwb3J0RnJvbUNqcyhmZnRFeHBvcnRzKTsKCgljb25zdCBXRUJBVURJT19CTE9DS19TSVpFID0gMTI4OwoJY29uc3QgREVGQVVMVF9CTE9DS19TSVpFID0gMTAyNDsgLy8gRGVmYXVsdCBibG9jayBzaXplIGlmIG5vdCBwcm92aWRlZCBpbiBvcHRpb25zCgkvKiogT3ZlcmxhcC1BZGQgTm9kZSAqLwoJY2xhc3MgT0xBUHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKCSAgICBuYklucHV0czsKCSAgICBuYk91dHB1dHM7CgkgICAgYmxvY2tTaXplOwoJICAgIGhvcFNpemU7CgkgICAgbmJPdmVybGFwczsKCSAgICBpbnB1dEJ1ZmZlcnMgPSBbXTsKCSAgICBpbnB1dEJ1ZmZlcnNIZWFkID0gW107CgkgICAgaW5wdXRCdWZmZXJzVG9TZW5kID0gW107CgkgICAgb3V0cHV0QnVmZmVycyA9IFtdOwoJICAgIG91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlID0gW107CgkgICAgY29uc3RydWN0b3Iob3B0aW9ucykgewoJICAgICAgICBzdXBlcihvcHRpb25zKTsKCSAgICAgICAgdGhpcy5uYklucHV0cyA9IG9wdGlvbnM/Lm51bWJlck9mSW5wdXRzIHx8IDE7CgkgICAgICAgIHRoaXMubmJPdXRwdXRzID0gb3B0aW9ucz8ubnVtYmVyT2ZPdXRwdXRzIHx8IDE7CgkgICAgICAgIC8vIHByb2Nlc3Nvck9wdGlvbnMgaXMgdHlwZWQgYHVua25vd25gIGF0IHRoZSBhbWJpZW50IGJvdW5kYXJ5OyBuYXJyb3cgaGVyZS4KCSAgICAgICAgY29uc3QgcHJvY09wdHMgPSAob3B0aW9ucz8ucHJvY2Vzc29yT3B0aW9ucyA/PyB7fSk7CgkgICAgICAgIHRoaXMuYmxvY2tTaXplID0gcHJvY09wdHMuYmxvY2tTaXplIHx8IERFRkFVTFRfQkxPQ0tfU0laRTsKCSAgICAgICAgdGhpcy5ob3BTaXplID0gV0VCQVVESU9fQkxPQ0tfU0laRTsKCSAgICAgICAgdGhpcy5uYk92ZXJsYXBzID0gTWF0aC5mbG9vcih0aGlzLmJsb2NrU2l6ZSAvIHRoaXMuaG9wU2l6ZSk7CgkgICAgICAgIHRoaXMuaW5pdGlhbGl6ZUJ1ZmZlcnMoKTsKCSAgICB9CgkgICAgaW5pdGlhbGl6ZUJ1ZmZlcnMoKSB7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWQgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kID0gbmV3IEFycmF5KHRoaXMubmJJbnB1dHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnMgPSBuZXcgQXJyYXkodGhpcy5uYk91dHB1dHMpOwoJICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlID0gbmV3IEFycmF5KHRoaXMubmJPdXRwdXRzKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVJbnB1dENoYW5uZWxzKGksIDEpOwoJICAgICAgICB9CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZU91dHB1dENoYW5uZWxzKGksIDEpOwoJICAgICAgICB9CgkgICAgfQoJICAgIGFsbG9jYXRlSW5wdXRDaGFubmVscyhpbnB1dEluZGV4LCBuYkNoYW5uZWxzKSB7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWRbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kW2lucHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iQ2hhbm5lbHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplICsgV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXS5maWxsKDApOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2lucHV0SW5kZXhdW2ldID0gdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF1baV0uc3ViYXJyYXkoMCwgdGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaW5wdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICBhbGxvY2F0ZU91dHB1dENoYW5uZWxzKG91dHB1dEluZGV4LCBuYkNoYW5uZWxzKSB7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tvdXRwdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbb3V0cHV0SW5kZXhdID0gbmV3IEFycmF5KG5iQ2hhbm5lbHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iQ2hhbm5lbHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XVtpXS5maWxsKDApOwoJICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtvdXRwdXRJbmRleF1baV0gPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgfQoJICAgIH0KCSAgICByZWFsbG9jYXRlQ2hhbm5lbHNJZk5lZWRlZChpbnB1dHMsIG91dHB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IG5iQ2hhbm5lbHMgPSBpbnB1dHNbaV0ubGVuZ3RoOwoJICAgICAgICAgICAgaWYgKG5iQ2hhbm5lbHMgIT09IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aCkgewoJICAgICAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVJbnB1dENoYW5uZWxzKGksIG5iQ2hhbm5lbHMpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgY29uc3QgbmJDaGFubmVscyA9IG91dHB1dHNbaV0ubGVuZ3RoOwoJICAgICAgICAgICAgaWYgKG5iQ2hhbm5lbHMgIT09IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGgpIHsKCSAgICAgICAgICAgICAgICB0aGlzLmFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMoaSwgbmJDaGFubmVscyk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcmVhZElucHV0cyhpbnB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCB3ZWJBdWRpb0Jsb2NrID0gaW5wdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2ldW2pdLnNldCh3ZWJBdWRpb0Jsb2NrLCB0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgd3JpdGVPdXRwdXRzKG91dHB1dHMpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGNvbnN0IHdlYkF1ZGlvQmxvY2sgPSBvdXRwdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIHdlYkF1ZGlvQmxvY2suc2V0KHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5zdWJhcnJheSgwLCBXRUJBVURJT19CTE9DS19TSVpFKSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgc2hpZnRJbnB1dEJ1ZmZlcnMoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaV1bal0uY29weVdpdGhpbigwLCBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBzaGlmdE91dHB1dEJ1ZmZlcnMoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uY29weVdpdGhpbigwLCBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal0uZmlsbCgwLCB0aGlzLmJsb2NrU2l6ZSAtIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHByZXBhcmVJbnB1dEJ1ZmZlcnNUb1NlbmQoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYklucHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaV1bal0uc2V0KHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpXVtqXSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgaGFuZGxlT3V0cHV0QnVmZmVyc1RvUmV0cmlldmUoKSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5uYk91dHB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLm91dHB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHRoaXMuYmxvY2tTaXplOyBrKyspIHsKCSAgICAgICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdW2tdICs9IHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmVbaV1bal1ba10gLyB0aGlzLm5iT3ZlcmxhcHM7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CgkgICAgICAgIHRoaXMucmVhbGxvY2F0ZUNoYW5uZWxzSWZOZWVkZWQoaW5wdXRzLCBvdXRwdXRzKTsKCSAgICAgICAgdGhpcy5yZWFkSW5wdXRzKGlucHV0cyk7CgkgICAgICAgIHRoaXMuc2hpZnRJbnB1dEJ1ZmZlcnMoKTsKCSAgICAgICAgdGhpcy5wcmVwYXJlSW5wdXRCdWZmZXJzVG9TZW5kKCk7CgkgICAgICAgIHRoaXMucHJvY2Vzc09MQSh0aGlzLmlucHV0QnVmZmVyc1RvU2VuZCwgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSwgcGFyYW1ldGVycyk7CgkgICAgICAgIHRoaXMuaGFuZGxlT3V0cHV0QnVmZmVyc1RvUmV0cmlldmUoKTsKCSAgICAgICAgdGhpcy53cml0ZU91dHB1dHMob3V0cHV0cyk7CgkgICAgICAgIHRoaXMuc2hpZnRPdXRwdXRCdWZmZXJzKCk7CgkgICAgICAgIHJldHVybiB0cnVlOwoJICAgIH0KCX0KCgkvKgoJICogUGhhc2Utdm9jb2RlciBjb3JlIOKAlCBjb250ZXh0LWZyZWUsIHVuaXQtdGVzdGFibGUgRFNQIGZvciBwZWFrLWJhc2VkCgkgKiBwaXRjaC1zaGlmdGluZyB3aXRoIElkZW50aXR5IFBoYXNlLUxvY2tpbmcuCgkgKgoJICogQWxnb3JpdGhtOiBKZWFuIExhcm9jaGUgJiBNYXJrIERvbHNvbiwgIk5ldyBQaGFzZS1Wb2NvZGVyIFRlY2huaXF1ZXMgZm9yCgkgKiBQaXRjaC1TaGlmdGluZywgSGFybW9uaXppbmcgYW5kIE90aGVyIEV4b3RpYyBFZmZlY3RzIiwgUHJvYy4gMTk5OSBJRUVFCgkgKiBXQVNQQUEsIE5ldyBQYWx0eiwgTlkuIFRoZSBwaXRjaC1zaGlmdCBkZXRlY3RzIHNwZWN0cmFsIHBlYWtzLCBkaXZpZGVzIHRoZQoJICogZnJlcXVlbmN5IGF4aXMgaW50byBwZXItcGVhayAicmVnaW9ucyBvZiBpbmZsdWVuY2UiLCBhbmQgcmlnaWRseSB0cmFuc2xhdGVzCgkgKiBlYWNoIHJlZ2lvbiB0byB0aGUgcGVhaydzIHNoaWZ0ZWQgZnJlcXVlbmN5IChTZWN0aW9uIDMuMS0zLjQpLgoJICoKCSAqIFRoZSBwaGFzZSBoYW5kbGluZyBpcyB0aGUgIklkZW50aXR5IFBoYXNlLUxvY2tpbmciIHJ1bGUgb2YgTGFyb2NoZS1Eb2xzb24KCSAqIDE5OTkgU2VjdGlvbiAzLjU6IHRvIG1haW50YWluIGZyYW1lLXRvLWZyYW1lIChob3Jpem9udGFsKSBwaGFzZSBjb2hlcmVuY2UKCSAqIGFmdGVyIHNoaWZ0aW5nIGEgcGVhayBieSBEZWx0YS1vbWVnYSwgRVZFUlkgZnJlcXVlbmN5IGJpbiBpbiB0aGF0IHBlYWsncwoJICogcmVnaW9uIG9mIGluZmx1ZW5jZSBpcyBtdWx0aXBsaWVkIGJ5IGEgU0lOR0xFIGNvbXBsZXggbnVtYmVyCgkgKgoJICogICAgIFpfdSA9IGV4cChqICogRGVsdGEtb21lZ2EgKiBSKSAgICAgICAgICAgIChMYXJvY2hlLURvbHNvbiAxOTk5LCBlcS4gcC4zKQoJICoKCSAqIChSID0gaG9wIHNpemUpLCBhbmQgdGhlIHJvdGF0aW9uIGlzIGN1bXVsYXRlZCBmcmFtZSB0byBmcmFtZQoJICoKCSAqICAgICBaX3t1KzF9ID0gWl91ICogZXhwKGogKiBEZWx0YS1vbWVnYV97dSsxfSAqIFIpLgoJICoKCSAqIEJlY2F1c2UgYWxsIGJpbnMgaW4gYSByZWdpb24gYXJlIHJvdGF0ZWQgYnkgdGhlIFNBTUUgYW5nbGUsIHRoZSBwaGFzZQoJICogcmVsYXRpb25zaGlwcyBCRVRXRUVOIGJpbnMgYXJvdW5kIGEgcGVhayAodGhlIHZlcnRpY2FsIC8gaW50cmEtcGVhawoJICogY29oZXJlbmNlIHRoYXQgaWRlbnRpZmllcyB0aGUgc2ludXNvaWQpIGFyZSBQUkVTRVJWRUQgYWNyb3NzIHRoZSBtb3ZlIOKAlCB0aGlzCgkgKiBpcyBleGFjdGx5IHdoYXQgcmVtb3ZlcyB0aGUgInBoYXNpbmVzcyIgYXJ0aWZhY3Qgb2YgdGhlIG5haXZlIGJpbi1pbmRlcGVuZGVudAoJICogcGhhc2Ugdm9jb2Rlci4gTm8ga25vd2xlZGdlIG9mIHRoZSB0cnVlIHBlYWsgZnJlcXVlbmN5IG9tZWdhIGlzIG5lZWRlZCwgc28gbm8KCSAqIGFyY3RhbmdlbnQgYW5kIG5vIHBoYXNlLXVud3JhcHBpbmcgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjUpLgoJICoKCSAqIFRoaXMgbW9kdWxlIG1pcnJvcnMgdGhlIHByb2plY3QncyBjb3JlL3NoZWxsIHNwbGl0IChjZi4gd2F2ZXNoYXBlci1jb3JlLnRzLAoJICogZHluYW1pY3MtY29yZS50cyk6IHRoZSBGRlQgZnJhbWluZyBhbmQgd29ya2xldCBwbHVtYmluZyBsaXZlIGluIHRoZQoJICogQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHNoZWxsIChwaGFzZS12b2NvZGVyLnRzKTsgdGhlIHNwZWN0cnVtIG1hbmlwdWxhdGlvbgoJICogdGhhdCBjYXJyaWVzIHRoZSB0ZXN0YWJsZSBpbnZhcmlhbnRzIGxpdmVzIGhlcmUuCgkgKi8KCS8qKgoJICogU3F1YXJlZC1tYWduaXR1ZGUgc3BlY3RydW0gZnJvbSBhbiBpbnRlcmxlYXZlZCBbcmUsIGltLCByZSwgaW0sIC4uLl0gY29tcGxleAoJICogYnVmZmVyLiBgbWFnbml0dWRlc1tpXWAgY29ycmVzcG9uZHMgdG8gYmluIGBpYC4gV3JpdGVzIGludG8gYG91dGAgKGxlbmd0aAoJICogZmZ0U2l6ZS8yICsgMSkgdG8gYXZvaWQgcGVyLWNhbGwgYWxsb2NhdGlvbiBpbiB0aGUgd29ya2xldC4KCSAqLwoJZnVuY3Rpb24gY29tcHV0ZU1hZ25pdHVkZXMoY29tcGxleCwgb3V0KSB7CgkgICAgZm9yIChsZXQgaSA9IDAsIGogPSAwOyBpIDwgb3V0Lmxlbmd0aDsgaSsrLCBqICs9IDIpIHsKCSAgICAgICAgY29uc3QgcmVhbCA9IGNvbXBsZXhbal07CgkgICAgICAgIGNvbnN0IGltYWcgPSBjb21wbGV4W2ogKyAxXTsKCSAgICAgICAgb3V0W2ldID0gcmVhbCAqKiAyICsgaW1hZyAqKiAyOwoJICAgIH0KCX0KCS8qKgoJICogUGVhayBkZXRlY3Rpb24gcGVyIExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjI6IGEgYmluIGlzIGEgcGVhayBpZmYgaXRzCgkgKiBtYWduaXR1ZGUgc3RyaWN0bHkgZXhjZWVkcyBpdHMgdHdvIG5lYXJlc3QgbmVpZ2hib3VycyBvbiBlYWNoIHNpZGUuIFdyaXRlcwoJICogdGhlIHBlYWsgYmluIGluZGljZXMgaW50byBgcGVha0luZGV4ZXNgIGFuZCByZXR1cm5zIHRoZSBjb3VudC4KCSAqLwoJZnVuY3Rpb24gZmluZFBlYWtzKG1hZ25pdHVkZXMsIHBlYWtJbmRleGVzKSB7CgkgICAgbGV0IG5iUGVha3MgPSAwOwoJICAgIGZvciAobGV0IGkgPSAyLCBlbmQgPSBtYWduaXR1ZGVzLmxlbmd0aCAtIDI7IGkgPCBlbmQ7IGkrKykgewoJICAgICAgICBjb25zdCBtYWcgPSBtYWduaXR1ZGVzW2ldOwoJICAgICAgICBpZiAobWFnbml0dWRlc1tpIC0gMV0gPj0gbWFnIHx8IG1hZ25pdHVkZXNbaSAtIDJdID49IG1hZyB8fCBtYWduaXR1ZGVzW2kgKyAxXSA+PSBtYWcgfHwgbWFnbml0dWRlc1tpICsgMl0gPj0gbWFnKSB7CgkgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgfQoJICAgICAgICBwZWFrSW5kZXhlc1tuYlBlYWtzKytdID0gaTsKCSAgICB9CgkgICAgcmV0dXJuIG5iUGVha3M7Cgl9CgkvKioKCSAqIFRoZSBwZXItZnJhbWUgcGhhc2UgaW5jcmVtZW50IGV4cChqICogRGVsdGEtb21lZ2EgKiBSKSBmb3IgT05FIHBlYWsgc2hpZnRlZAoJICogYnkgRGVsdGEtb21lZ2Egb3ZlciBhIGhvcCBvZiBSIHN5bnRoZXNpcyBzYW1wbGVzLiBMYXJvY2hlLURvbHNvbiAxOTk5IGVxLgoJICogcC4zIChTZWN0aW9uIDMuNSksIHRoZSBzaW5nbGUgY29tcGxleCBudW1iZXIgYXBwbGllZCB1bmlmb3JtbHkgdG8gdGhlIHBlYWsncwoJICogd2hvbGUgcmVnaW9uIG9mIGluZmx1ZW5jZS4KCSAqCgkgKiBEZWx0YS1vbWVnYSAocmFkL3NhbXBsZSkgaXMgdGhlIGZyZXF1ZW5jeSBzaGlmdCB0aGUgcGVhayB1bmRlcmdvZXM6CgkgKiAgIERlbHRhLW9tZWdhID0gMipwaSAqIChwZWFrSW5kZXhTaGlmdGVkIC0gcGVha0luZGV4KSAvIGZmdFNpemUuCgkgKgoJICogVGhpcyBpcyB0aGUgcGVyLWZyYW1lIGZhY3RvciwgTk9UIHRoZSBjdW11bGF0aXZlIHJvdGF0b3IuIENyb3NzLWZyYW1lCgkgKiBjdW11bGF0aW9uIFpfe3UrMX0gPSBaX3UgKiBleHAoaipEZWx0YS1vbWVnYV97dSsxfSpSKSBpcyB0aGUgam9iIG9mCgkgKiB7QGxpbmsgUGVha1JvdGF0b3JTdGF0ZX07IHRoaXMgZnVuY3Rpb24gcHJvZHVjZXMgdGhlIGBleHAoLi4uKWAgZmFjdG9yIHRoYXQKCSAqIHN0YXRlIG11bHRpcGxpZXMgaW4gZWFjaCBmcmFtZS4KCSAqLwoJZnVuY3Rpb24gZnJhbWVSb3RhdGlvbihwZWFrSW5kZXgsIHBlYWtJbmRleFNoaWZ0ZWQsIGZmdFNpemUsIGhvcCkgewoJICAgIGNvbnN0IG9tZWdhRGVsdGEgPSAoMiAqIE1hdGguUEkgKiAocGVha0luZGV4U2hpZnRlZCAtIHBlYWtJbmRleCkpIC8gZmZ0U2l6ZTsKCSAgICBjb25zdCBhbmdsZSA9IG9tZWdhRGVsdGEgKiBob3A7CgkgICAgcmV0dXJuIHsgcmU6IE1hdGguY29zKGFuZ2xlKSwgaW06IE1hdGguc2luKGFuZ2xlKSB9OwoJfQoJLyoqCgkgKiBDcm9zcy1mcmFtZSBjdW11bGF0aXZlIHBoYXNlLWxvY2sgc3RhdGUgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbiAzLjUpLgoJICoKCSAqIFRoZSBwYXBlciByZXF1aXJlcyB0aGUgcGVyLXBlYWsgcm90YXRpb24gYmUgQUNDVU1VTEFURUQgZnJhbWUgdG8gZnJhbWU6CgkgKgoJICogICAgIFpfe3UrMX0gPSBaX3UgKiBleHAoaiAqIERlbHRhLW9tZWdhX3t1KzF9ICogUikKCSAqCgkgKiB3aXRoIERlbHRhLW9tZWdhIGFsbG93ZWQgdG8gdmFyeSBwZXIgZnJhbWUgKGF1dG9tYXRlZCAvIHRpbWUtdmFyeWluZyBwaXRjaCkuCgkgKiBBIG5haXZlIGBvbWVnYURlbHRhICogZWxhcHNlZFRpbWVgIHJvdGF0b3IgaXMgd3Jvbmc6IHdoZW4gdGhlIHNoaWZ0IGNoYW5nZXMsCgkgKiBpdCByZXRyb2FjdGl2ZWx5IHJlLXBoYXNlcyBldmVyeSBwcmlvciBmcmFtZSBhbmQgcHJvZHVjZXMgYSBkaXNjb250aW51aXR5LgoJICoKCSAqIFRoaXMgc3RhdGUga2VlcHMgb25lIGN1bXVsYXRpdmUgcm90YXRvciBaX3UgUEVSIFBFQUssIGtleWVkIGJ5IHRoZSBwZWFrJ3MKCSAqIHNvdXJjZSBiaW4gaW5kZXguIHtAbGluayBQZWFrUm90YXRvclN0YXRlLmFkdmFuY2V9IG11bHRpcGxpZXMgZWFjaCBwZWFrJ3MgWgoJICogYnkgdGhpcyBmcmFtZSdzIGV4cChqKkRlbHRhLW9tZWdhKlIpIChzbyBoaXN0b3J5IGlzIHByZXNlcnZlZCBhY3Jvc3MgcGl0Y2gKCSAqIGNoYW5nZXMpLCBhbmQge0BsaW5rIFBlYWtSb3RhdG9yU3RhdGUuZ2V0fSByZXR1cm5zIHRoZSBjdXJyZW50IGN1bXVsYXRpdmUKCSAqIHJvdGF0b3IgdG8gYXBwbHkgdG8gdGhhdCBwZWFrJ3MgcmVnaW9uIG9mIGluZmx1ZW5jZS4KCSAqLwoJY2xhc3MgUGVha1JvdGF0b3JTdGF0ZSB7CgkgICAgLyoqIHBlYWsgc291cmNlLWJpbiBpbmRleCAtPiBjdW11bGF0aXZlIHJvdGF0b3IgWl91ICh1bml0IG1vZHVsdXMpLiAqLwoJICAgIHJvdGF0b3JzID0gbmV3IE1hcCgpOwoJICAgIC8qKiBzY3JhdGNoIHNldCBvZiBiaW5zIHNlZW4gdGhpcyBmcmFtZSwgZm9yIHBydW5pbmcgdmFuaXNoZWQgcGVha3MuICovCgkgICAgc2VlbiA9IG5ldyBTZXQoKTsKCSAgICAvKioKCSAgICAgKiBBZHZhbmNlIGV2ZXJ5IGN1cnJlbnRseS1kZXRlY3RlZCBwZWFrJ3MgY3VtdWxhdGl2ZSByb3RhdG9yIGJ5IHRoaXMgZnJhbWUncwoJICAgICAqIGV4cChqKkRlbHRhLW9tZWdhKlIpLiBOZXcgcGVha3Mgc3RhcnQgYXQgWiA9IDEgKG5vIHJvdGF0aW9uKSB0aGVuIHRha2UgdGhpcwoJICAgICAqIGZyYW1lJ3MgaW5jcmVtZW50OyBwZWFrcyBub3QgcHJlc2VudCB0aGlzIGZyYW1lIGFyZSBkcm9wcGVkIHNvIHRoZWlyIHN0YWxlCgkgICAgICogcGhhc2UgZG9lcyBub3QgbGVhayBpbnRvIGEgbGF0ZXIgcmUtZGV0ZWN0aW9uLgoJICAgICAqCgkgICAgICogYHBpdGNoRmFjdG9yYCBhbmQgYGhvcGAgZGVmaW5lIHRoaXMgZnJhbWUncyBwZXItcGVhayBEZWx0YS1vbWVnYSB2aWEgdGhlCgkgICAgICogc2hpZnRlZCBiaW4gYHJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKWAuCgkgICAgICovCgkgICAgYWR2YW5jZShwZWFrSW5kZXhlcywgbmJQZWFrcywgZmZ0U2l6ZSwgcGl0Y2hGYWN0b3IsIGhvcCkgewoJICAgICAgICB0aGlzLnNlZW4uY2xlYXIoKTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYlBlYWtzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IHBlYWtJbmRleCA9IHBlYWtJbmRleGVzW2ldOwoJICAgICAgICAgICAgdGhpcy5zZWVuLmFkZChwZWFrSW5kZXgpOwoJICAgICAgICAgICAgY29uc3QgcGVha0luZGV4U2hpZnRlZCA9IE1hdGgucm91bmQocGVha0luZGV4ICogcGl0Y2hGYWN0b3IpOwoJICAgICAgICAgICAgY29uc3QgaW5jID0gZnJhbWVSb3RhdGlvbihwZWFrSW5kZXgsIHBlYWtJbmRleFNoaWZ0ZWQsIGZmdFNpemUsIGhvcCk7CgkgICAgICAgICAgICBjb25zdCBwcmV2ID0gdGhpcy5yb3RhdG9ycy5nZXQocGVha0luZGV4KSA/PyB7IHJlOiAxLCBpbTogMCB9OwoJICAgICAgICAgICAgLy8gWl97dSsxfSA9IFpfdSAqIGluYyAgKGNvbXBsZXggbXVsdGlwbHkpCgkgICAgICAgICAgICB0aGlzLnJvdGF0b3JzLnNldChwZWFrSW5kZXgsIHsKCSAgICAgICAgICAgICAgICByZTogcHJldi5yZSAqIGluYy5yZSAtIHByZXYuaW0gKiBpbmMuaW0sCgkgICAgICAgICAgICAgICAgaW06IHByZXYucmUgKiBpbmMuaW0gKyBwcmV2LmltICogaW5jLnJlLAoJICAgICAgICAgICAgfSk7CgkgICAgICAgIH0KCSAgICAgICAgLy8gUHJ1bmUgcGVha3MgdGhhdCBkaXNhcHBlYXJlZCB0aGlzIGZyYW1lLgoJICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiB0aGlzLnJvdGF0b3JzLmtleXMoKSkgewoJICAgICAgICAgICAgaWYgKCF0aGlzLnNlZW4uaGFzKGtleSkpCgkgICAgICAgICAgICAgICAgdGhpcy5yb3RhdG9ycy5kZWxldGUoa2V5KTsKCSAgICAgICAgfQoJICAgIH0KCSAgICAvKiogVGhlIGN1cnJlbnQgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSBmb3IgYSBwZWFrIChpZGVudGl0eSBpZiB1bnNlZW4pLiAqLwoJICAgIGdldChwZWFrSW5kZXgpIHsKCSAgICAgICAgcmV0dXJuIHRoaXMucm90YXRvcnMuZ2V0KHBlYWtJbmRleCkgPz8geyByZTogMSwgaW06IDAgfTsKCSAgICB9CgkgICAgLyoqIFJlc2V0IGFsbCBhY2N1bXVsYXRlZCBwaGFzZSAoZS5nLiBvbiBzdG9wIC8gcmUtc2VlaykuICovCgkgICAgcmVzZXQoKSB7CgkgICAgICAgIHRoaXMucm90YXRvcnMuY2xlYXIoKTsKCSAgICAgICAgdGhpcy5zZWVuLmNsZWFyKCk7CgkgICAgfQoJICAgIC8qKiBOdW1iZXIgb2YgcGVha3MgY3VycmVudGx5IHRyYWNrZWQgKHRlc3QvaW50cm9zcGVjdGlvbiBoZWxwZXIpLiAqLwoJICAgIGdldCBzaXplKCkgewoJICAgICAgICByZXR1cm4gdGhpcy5yb3RhdG9ycy5zaXplOwoJICAgIH0KCX0KCS8qKgoJICogSGFsZi13YXkgcmVnaW9uLW9mLWluZmx1ZW5jZSBib3VuZGFyaWVzIGZvciBwZWFrIGBpYCAoTGFyb2NoZS1Eb2xzb24gMTk5OQoJICogU2VjdGlvbiAzLjIg4oCUIGJvdW5kYXJ5IHNldCBtaWR3YXkgYmV0d2VlbiBhZGphY2VudCBwZWFrcykuIFJldHVybnMgdGhlCgkgKiBbc3RhcnRJbmRleCwgZW5kSW5kZXgpIGJpbiByYW5nZSBvd25lZCBieSB0aGlzIHBlYWsuCgkgKgoJICogVGhlIExBU1QgcGVhaydzIHJlZ2lvbiBtdXN0IGVuZCBhdCB0aGUgbm9uLXJlZHVuZGFudCBoYWxmLXNwZWN0cnVtIGxlbmd0aAoJICogYG1hZ25pdHVkZXNMZW5ndGhgICg9IGZmdFNpemUvMiArIDEpLCBOT1QgYXQgYGZmdFNpemVgLiBUaGUgYW5hbHlzZWQgc3BlY3RydW0KCSAqIG9ubHkgY2FycmllcyBiaW5zIFswLCBmZnRTaXplLzJdOyB0aGUgdXBwZXIgaGFsZiBpcyB0aGUgY29uanVnYXRlLXN5bW1ldHJpYwoJICogbWlycm9yIGZpbGxlZCBsYXRlciBieSBgY29tcGxldGVTcGVjdHJ1bWAuIFJlYWRpbmcgc291cmNlIGJpbnMgcGFzdCBOeXF1aXN0CgkgKiBmb2xkcyBzdGFsZSAvIG5vdC15ZXQtcG9wdWxhdGVkIG5lZ2F0aXZlLWZyZXF1ZW5jeSBkYXRhIGludG8gdGhlIG91dHB1dCwgc28KCSAqIHRoZSByZWdpb24gaXMgY2xhbXBlZCB0byB0aGUgYW5hbHlzZWQgcG9zaXRpdmUgc3BlY3RydW0uCgkgKi8KCWZ1bmN0aW9uIHJlZ2lvbk9mSW5mbHVlbmNlKHBlYWtJbmRleGVzLCBpLCBuYlBlYWtzLCBtYWduaXR1ZGVzTGVuZ3RoKSB7CgkgICAgY29uc3QgcGVha0luZGV4ID0gcGVha0luZGV4ZXNbaV07CgkgICAgY29uc3Qgc3RhcnRJbmRleCA9IGkgPiAwID8gcGVha0luZGV4IC0gTWF0aC5mbG9vcigocGVha0luZGV4IC0gcGVha0luZGV4ZXNbaSAtIDFdKSAvIDIpIDogMDsKCSAgICBjb25zdCBlbmRJbmRleCA9IGkgPCBuYlBlYWtzIC0gMSA/IHBlYWtJbmRleCArIE1hdGguY2VpbCgocGVha0luZGV4ZXNbaSArIDFdIC0gcGVha0luZGV4KSAvIDIpIDogbWFnbml0dWRlc0xlbmd0aDsKCSAgICByZXR1cm4geyBzdGFydEluZGV4LCBlbmRJbmRleCB9OwoJfQoJLyoqCgkgKiBQZWFrLXNoaWZ0IHdpdGggSWRlbnRpdHkgUGhhc2UtTG9ja2luZyAoTGFyb2NoZS1Eb2xzb24gMTk5OSwgU2VjdGlvbnMgMy40ICYKCSAqIDMuNSkuIEZvciBlYWNoIGRldGVjdGVkIHBlYWs6CgkgKiAgLSBjb21wdXRlIHRoZSBzaGlmdGVkIHBlYWsgYmluIChwZWFrSW5kZXggKiBwaXRjaEZhY3Rvciwgcm91bmRlZCDigJQgdGhlCgkgKiAgICBpbnRlZ2VyLWJpbiBjYXNlIG9mIFNlY3Rpb24gMy40LCBhIGxvc3NsZXNzIHJlZ2lvbiBjb3B5KSwKCSAqICAtIGNvbXB1dGUgT05FIHJvdGF0b3IgWl91ID0gZXhwKGoqRGVsdGEtb21lZ2EqdGltZUN1cnNvcikgZm9yIHRoZSBwZWFrCgkgKiAgICAoYHBlYWtSb3RhdG9yYCksCgkgKiAgLSByaWdpZGx5IHRyYW5zbGF0ZSB0aGUgcGVhaydzIHJlZ2lvbiBvZiBpbmZsdWVuY2UgdG8gdGhlIHNoaWZ0ZWQgbG9jYXRpb24sCgkgKiAgICBtdWx0aXBseWluZyBFVkVSWSBiaW4gaW4gdGhlIHJlZ2lvbiBieSB0aGF0IFNBTUUgWl91LgoJICogT3ZlcmxhcHBpbmcgc2hpZnRlZCByZWdpb25zIGFyZSBzdW1tZWQgKFNlY3Rpb24gMy40KS4KCSAqCgkgKiBgY29tcGxleGAgaXMgdGhlIGFuYWx5c2lzIHNwZWN0cnVtIChpbnRlcmxlYXZlZCByZS9pbSkuIGBzaGlmdGVkYCBpcyB0aGUKCSAqIG91dHB1dCBzcGVjdHJ1bSAoaW50ZXJsZWF2ZWQgcmUvaW0pIGFuZCBpcyB6ZXJvLWZpbGxlZCBoZXJlIGJlZm9yZQoJICogYWNjdW11bGF0aW9uLiBgbWFnbml0dWRlc0xlbmd0aGAgPSBmZnRTaXplLzIgKyAxICh0aGUgbm9uLXJlZHVuZGFudCBiaW4KCSAqIGNvdW50KTsgYmlucyBiZXlvbmQgaXQgYXJlIHNraXBwZWQgLyB0ZXJtaW5hdGUgdGhlIHBlYWsgbG9vcCwgbWF0Y2hpbmcgdGhlCgkgKiB3b3JrbGV0J3MgcHJlLWBjb21wbGV0ZVNwZWN0cnVtYCBoYWxmLXNwZWN0cnVtLgoJICovCglmdW5jdGlvbiBzaGlmdFBlYWtzKGNvbXBsZXgsIHNoaWZ0ZWQsIHBlYWtJbmRleGVzLCBuYlBlYWtzLCBmZnRTaXplLCBtYWduaXR1ZGVzTGVuZ3RoLCBwaXRjaEZhY3Rvciwgcm90YXRvcnMpIHsKCSAgICBzaGlmdGVkLmZpbGwoMCk7CgkgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuYlBlYWtzOyBpKyspIHsKCSAgICAgICAgY29uc3QgcGVha0luZGV4ID0gcGVha0luZGV4ZXNbaV07CgkgICAgICAgIGNvbnN0IHBlYWtJbmRleFNoaWZ0ZWQgPSBNYXRoLnJvdW5kKHBlYWtJbmRleCAqIHBpdGNoRmFjdG9yKTsKCSAgICAgICAgY29uc3QgeyBzdGFydEluZGV4LCBlbmRJbmRleCB9ID0gcmVnaW9uT2ZJbmZsdWVuY2UocGVha0luZGV4ZXMsIGksIG5iUGVha3MsIG1hZ25pdHVkZXNMZW5ndGgpOwoJICAgICAgICAvLyBMYXJvY2hlLURvbHNvbiAxOTk5IElkZW50aXR5IFBoYXNlLUxvY2tpbmc6IE9ORSBjdW11bGF0aXZlIHJvdGF0b3IgWl91CgkgICAgICAgIC8vIHBlciBwZWFrLCBhcHBsaWVkIHVuaWZvcm1seSB0byB0aGUgd2hvbGUgcmVnaW9uIG9mIGluZmx1ZW5jZSAob25lIGNvbXBsZXgKCSAgICAgICAgLy8gbXVsdGlwbHkgcGVyIGJpbiksIHByZXNlcnZpbmcgdGhlIGludHJhLXJlZ2lvbiBwaGFzZSByZWxhdGlvbnNoaXBzLiBaX3UKCSAgICAgICAgLy8gaXMgYWNjdW11bGF0ZWQgZnJhbWUtdG8tZnJhbWUgYnkgUGVha1JvdGF0b3JTdGF0ZSDigJQgbm90IHJlY29tcHV0ZWQgZnJvbQoJICAgICAgICAvLyBhYnNvbHV0ZSB0aW1lIOKAlCBzbyBhIGNoYW5naW5nIHBpdGNoRmFjdG9yIGRvZXMgbm90IHJldHJvYWN0aXZlbHkgcmVwaGFzZS4KCSAgICAgICAgY29uc3Qgcm90ID0gcm90YXRvcnMuZ2V0KHBlYWtJbmRleCk7CgkgICAgICAgIGZvciAobGV0IGogPSBzdGFydEluZGV4IC0gcGVha0luZGV4OyBqIDwgZW5kSW5kZXggLSBwZWFrSW5kZXg7IGorKykgewoJICAgICAgICAgICAgY29uc3QgYmluSW5kZXggPSBwZWFrSW5kZXggKyBqOwoJICAgICAgICAgICAgY29uc3QgYmluSW5kZXhTaGlmdGVkID0gcGVha0luZGV4U2hpZnRlZCArIGo7CgkgICAgICAgICAgICBpZiAoYmluSW5kZXhTaGlmdGVkID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICBicmVhazsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIC8vIFNvdXJjZSBiaW5zIGFyZSBvbmx5IHZhbGlkIGluc2lkZSB0aGUgYW5hbHlzZWQgcG9zaXRpdmUgc3BlY3RydW0uCgkgICAgICAgICAgICBpZiAoYmluSW5kZXggPCAwIHx8IGJpbkluZGV4ID49IG1hZ25pdHVkZXNMZW5ndGgpIHsKCSAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgICAgIGNvbnN0IGluZGV4UmVhbCA9IGJpbkluZGV4ICogMjsKCSAgICAgICAgICAgIGNvbnN0IGluZGV4SW1hZyA9IGluZGV4UmVhbCArIDE7CgkgICAgICAgICAgICBjb25zdCB2YWx1ZVJlYWwgPSBjb21wbGV4W2luZGV4UmVhbF07CgkgICAgICAgICAgICBjb25zdCB2YWx1ZUltYWcgPSBjb21wbGV4W2luZGV4SW1hZ107CgkgICAgICAgICAgICAvLyBDb21wbGV4IG11bHRpcGx5IGJ5IHRoZSBzaW5nbGUgcGVyLXBlYWsgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdS4KCSAgICAgICAgICAgIGNvbnN0IHZhbHVlU2hpZnRlZFJlYWwgPSB2YWx1ZVJlYWwgKiByb3QucmUgLSB2YWx1ZUltYWcgKiByb3QuaW07CgkgICAgICAgICAgICBjb25zdCB2YWx1ZVNoaWZ0ZWRJbWFnID0gdmFsdWVSZWFsICogcm90LmltICsgdmFsdWVJbWFnICogcm90LnJlOwoJICAgICAgICAgICAgaWYgKGJpbkluZGV4U2hpZnRlZCA8IDApIHsKCSAgICAgICAgICAgICAgICAvLyBMYXJvY2hlLURvbHNvbiAxOTk5IFNlY3Rpb24gMy40OiBhIHJlZ2lvbiBvZiBpbmZsdWVuY2Ugc3BpbGxpbmcgb250bwoJICAgICAgICAgICAgICAgIC8vIHRoZSBORUdBVElWRS1mcmVxdWVuY3kgYXhpcyBpcyByZWZsZWN0ZWQgYmFjayBpbnRvIHRoZSBwb3NpdGl2ZQoJICAgICAgICAgICAgICAgIC8vIGZyZXF1ZW5jaWVzIHdpdGggQ09NUExFWCBDT05KVUdBVElPTiwgYmVjYXVzZSB0aGUgb3JpZ2luYWwgc2lnbmFsIGlzCgkgICAgICAgICAgICAgICAgLy8gcmVhbCAoSGVybWl0aWFuIHN5bW1ldHJ5IFgoLXcpID0gY29uaihYKHcpKSkuIFdpdGhvdXQgdGhpcyB0aGUgZW5lcmd5CgkgICAgICAgICAgICAgICAgLy8gYmVsb3cgREMgaXMgZHJvcHBlZCBhbmQgZG93bndhcmQgc2hpZnRzIGxvc2UgdGhlaXIgbG93IGVuZC4KCSAgICAgICAgICAgICAgICBjb25zdCByZWZsZWN0ZWQgPSAtYmluSW5kZXhTaGlmdGVkOwoJICAgICAgICAgICAgICAgIGlmIChyZWZsZWN0ZWQgPj0gbWFnbml0dWRlc0xlbmd0aCkgewoJICAgICAgICAgICAgICAgICAgICBjb250aW51ZTsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICAgICAgY29uc3QgcmVmbFJlYWwgPSByZWZsZWN0ZWQgKiAyOwoJICAgICAgICAgICAgICAgIGNvbnN0IHJlZmxJbWFnID0gcmVmbFJlYWwgKyAxOwoJICAgICAgICAgICAgICAgIHNoaWZ0ZWRbcmVmbFJlYWxdICs9IHZhbHVlU2hpZnRlZFJlYWw7CgkgICAgICAgICAgICAgICAgc2hpZnRlZFtyZWZsSW1hZ10gKz0gLXZhbHVlU2hpZnRlZEltYWc7IC8vIGNvbmp1Z2F0ZQoJICAgICAgICAgICAgICAgIGNvbnRpbnVlOwoJICAgICAgICAgICAgfQoJICAgICAgICAgICAgY29uc3QgaW5kZXhTaGlmdGVkUmVhbCA9IGJpbkluZGV4U2hpZnRlZCAqIDI7CgkgICAgICAgICAgICBjb25zdCBpbmRleFNoaWZ0ZWRJbWFnID0gaW5kZXhTaGlmdGVkUmVhbCArIDE7CgkgICAgICAgICAgICBzaGlmdGVkW2luZGV4U2hpZnRlZFJlYWxdICs9IHZhbHVlU2hpZnRlZFJlYWw7CgkgICAgICAgICAgICBzaGlmdGVkW2luZGV4U2hpZnRlZEltYWddICs9IHZhbHVlU2hpZnRlZEltYWc7CgkgICAgICAgIH0KCSAgICB9Cgl9CgoJLyoKCSAqIFBoYXNlLXZvY29kZXIgQXVkaW9Xb3JrbGV0IHNoZWxsIOKAlCBwZWFrLWJhc2VkIHBpdGNoLXNoaWZ0ZXIgd2l0aCBJZGVudGl0eQoJICogUGhhc2UtTG9ja2luZywgSmVhbiBMYXJvY2hlICYgTWFyayBEb2xzb24sICJOZXcgUGhhc2UtVm9jb2RlciBUZWNobmlxdWVzIGZvcgoJICogUGl0Y2gtU2hpZnRpbmcsIEhhcm1vbml6aW5nIGFuZCBPdGhlciBFeG90aWMgRWZmZWN0cyIsIFByb2MuIDE5OTkgSUVFRSBXQVNQQUEuCgkgKgoJICogVGhpcyBmaWxlIG93bnMgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKEZGVCBmcmFtaW5nIG9uIHRvcCBvZiBPTEFQcm9jZXNzb3IsCgkgKiBwYXJhbWV0ZXJEZXNjcmlwdG9ycywgdGhlIHJlZ2lzdGVyUHJvY2Vzc29yIGNhbGwpLiBUaGUgcGVhayBkZXRlY3Rpb24sCgkgKiByZWdpb24tb2YtaW5mbHVlbmNlIHRyYW5zbGF0aW9uIGFuZCB0aGUgTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNQoJICogaWRlbnRpdHktcGhhc2UtbG9jayByb3RhdGlvbiBsaXZlIGluIHRoZSBjb250ZXh0LWZyZWUsIHVuaXQtdGVzdGVkCgkgKiBwaGFzZS12b2NvZGVyLWNvcmUudHMgKG1pcnJvcmluZyB0aGUgd2F2ZXNoYXBlciAvIGR5bmFtaWNzIGNvcmUvc2hlbGwgc3BsaXQpLgoJICovCgljb25zdCBCVUZGRVJFRF9CTE9DS19TSVpFID0gMjA0ODsKCWZ1bmN0aW9uIGdlbkhhbm5XaW5kb3cobGVuZ3RoKSB7CgkgICAgY29uc3Qgd2luID0gbmV3IEZsb2F0MzJBcnJheShsZW5ndGgpOwoJICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKCSAgICAgICAgd2luW2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIGxlbmd0aCkpOwoJICAgIH0KCSAgICByZXR1cm4gd2luOwoJfQoJY2xhc3MgUGhhc2VWb2NvZGVyUHJvY2Vzc29yIGV4dGVuZHMgT0xBUHJvY2Vzc29yIHsKCSAgICBmZnRTaXplOwoJICAgIHRpbWVDdXJzb3I7CgkgICAgLy8gTGFyb2NoZS1Eb2xzb24gMTk5OSBTZWN0aW9uIDMuNSBjdW11bGF0aXZlIHBoYXNlIFpfdSBpcyBQRVIgU0lHTkFMIFNUUkVBTToKCSAgICAvLyBlYWNoIGNoYW5uZWwgaXMgaXRzIG93biBzdHJlYW0sIHNvIGVhY2ggZ2V0cyBpdHMgb3duIFBlYWtSb3RhdG9yU3RhdGUuIEEKCSAgICAvLyBzaW5nbGUgc2hhcmVkIHN0YXRlIHdvdWxkIGxldCBjaGFubmVsIDAgYWR2YW5jZS91c2UgWl91IGFuZCB0aGVuIGNoYW5uZWwgMQoJICAgIC8vIGFkdmFuY2UgdGhlIFNBTUUgcGVhayB0byBaX3t1KzF9IGluIHRoZSBzYW1lIE9MQSBmcmFtZSAoaW50ZXItY2hhbm5lbCBwaGFzZQoJICAgIC8vIG1pc21hdGNoKSBhbmQgd291bGQgbGV0IG9uZSBjaGFubmVsJ3MgcGVhayBzZXQgcHJ1bmUgdGhlIG90aGVyJ3MuIEluZGV4ZWQKCSAgICAvLyBbaW5wdXRJbmRleF1bY2hhbm5lbEluZGV4XTsgZ3Jvd24gbGF6aWx5IGFzIGNoYW5uZWxzIGFwcGVhci4KCSAgICByb3RhdG9yczsKCSAgICBoYW5uV2luZG93OwoJICAgIGZmdDsKCSAgICAvLyBmZnQuanMgd2l0aCBhIEZsb2F0MzJBcnJheSBpbnB1dCByZXR1cm5zIGEgZmxhdCBpbnRlcmxlYXZlZCBGbG9hdDMyQXJyYXkKCSAgICAvLyBvZiBsZW5ndGggMipzaXplIChyZWFsL2ltYWcgcGFpcnMpLiBUaGUgbGlicmFyeSdzIHR5cGVzIGFyZSBgYW55W11gIHNvIHdlCgkgICAgLy8gbmFycm93IGF0IHRoZSBwZXJpbWV0ZXIg4oCUIHNlZSBhbGxvY2F0aW9uIHNpdGUgYmVsb3cuCgkgICAgZnJlcUNvbXBsZXhCdWZmZXI7CgkgICAgZnJlcUNvbXBsZXhCdWZmZXJTaGlmdGVkOwoJICAgIHRpbWVDb21wbGV4QnVmZmVyOwoJICAgIG1hZ25pdHVkZXM7CgkgICAgcGVha0luZGV4ZXM7CgkgICAgbmJQZWFrczsKCSAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewoJICAgICAgICByZXR1cm4gWwoJICAgICAgICAgICAgewoJICAgICAgICAgICAgICAgIG5hbWU6ICJwaXRjaEZhY3RvciIsCgkgICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiAxLjAsCgkgICAgICAgICAgICB9LAoJICAgICAgICBdOwoJICAgIH0KCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIC8vIE1lcmdlIGRlZmF1bHRzIHdpdGggY2FsbGVyLXN1cHBsaWVkIHByb2Nlc3Nvck9wdGlvbnMgKGNhbGxlciB3aW5zKS4KCSAgICAgICAgY29uc3QgYmFzZU9wdGlvbnMgPSBvcHRpb25zID8/IHt9OwoJICAgICAgICBjb25zdCBjYWxsZXJPcHRzID0gKGJhc2VPcHRpb25zLnByb2Nlc3Nvck9wdGlvbnMgPz8ge30pOwoJICAgICAgICBiYXNlT3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID0gewoJICAgICAgICAgICAgYmxvY2tTaXplOiBCVUZGRVJFRF9CTE9DS19TSVpFLAoJICAgICAgICAgICAgLi4uY2FsbGVyT3B0cywKCSAgICAgICAgfTsKCSAgICAgICAgc3VwZXIoYmFzZU9wdGlvbnMpOwoJICAgICAgICB0aGlzLmZmdFNpemUgPSB0aGlzLmJsb2NrU2l6ZTsKCSAgICAgICAgdGhpcy50aW1lQ3Vyc29yID0gMDsKCSAgICAgICAgLy8gUGVyLWNoYW5uZWwgcGVyLXBlYWsgY3VtdWxhdGl2ZSBwaGFzZS1sb2NrIHN0YXRlIChMYXJvY2hlLURvbHNvbiAxOTk5CgkgICAgICAgIC8vIFNlY3Rpb24gMy41KTogWl97dSsxfSA9IFpfdSAqIGV4cChqKkRlbHRhLW9tZWdhKlIpLCBhZHZhbmNlZCBvbmNlIHBlcgoJICAgICAgICAvLyBmcmFtZSwga2VwdCBpbmRlcGVuZGVudGx5IGZvciBlYWNoIGNoYW5uZWwgKHNpZ25hbCBzdHJlYW0pLiBHcm93biBsYXppbHkKCSAgICAgICAgLy8gcGVyIGNoYW5uZWwgaW4gcHJvY2Vzc09MQSgpIHNvIGl0IG1hdGNoZXMgdGhlIGxpdmUgY2hhbm5lbCBjb3VudC4KCSAgICAgICAgdGhpcy5yb3RhdG9ycyA9IFtdOwoJICAgICAgICB0aGlzLmhhbm5XaW5kb3cgPSBnZW5IYW5uV2luZG93KHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgLy8gcHJlcGFyZSBGRlQgYW5kIHByZS1hbGxvY2F0ZSBidWZmZXJzCgkgICAgICAgIHRoaXMuZmZ0ID0gbmV3IEZGVCh0aGlzLmZmdFNpemUpOwoJICAgICAgICAvLyBmZnQuanMgcmV0dXJucyBpdHMgY29tcGxleCBhcnJheSBhcyBgYW55W11gOyBydW50aW1lIGlzIGEgZmxhdCBGbG9hdDMyQXJyYXkuCgkgICAgICAgIHRoaXMuZnJlcUNvbXBsZXhCdWZmZXIgPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy5mcmVxQ29tcGxleEJ1ZmZlclNoaWZ0ZWQgPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICAgICAgdGhpcy50aW1lQ29tcGxleEJ1ZmZlciA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm1hZ25pdHVkZXMgPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuZmZ0U2l6ZSAvIDIgKyAxKTsKCSAgICAgICAgdGhpcy5wZWFrSW5kZXhlcyA9IG5ldyBJbnQzMkFycmF5KHRoaXMubWFnbml0dWRlcy5sZW5ndGgpOwoJICAgICAgICB0aGlzLm5iUGVha3MgPSAwOwoJICAgIH0KCSAgICBwcm9jZXNzT0xBKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewoJICAgICAgICBjb25zdCBwaXRjaEZhY3RvciA9IHBhcmFtZXRlcnMucGl0Y2hGYWN0b3JbcGFyYW1ldGVycy5waXRjaEZhY3Rvci5sZW5ndGggLSAxXTsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGNvbnN0IGlucHV0Um90YXRvcnMgPSAodGhpcy5yb3RhdG9yc1tpXSA/Pz0gW10pOwoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dHNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1tpXVtqXTsKCSAgICAgICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzW2ldW2pdOwoJICAgICAgICAgICAgICAgIC8vIEVhY2ggY2hhbm5lbCBrZWVwcyBpdHMgT1dOIGN1bXVsYXRpdmUgcm90YXRvciBzdGF0ZSAoTGFyb2NoZS1Eb2xzb24KCSAgICAgICAgICAgICAgICAvLyAxOTk5IFNlY3Rpb24gMy41IHBlci1zdHJlYW0gcGhhc2UpLiBTaGFyaW5nIG9uZSBhY3Jvc3MgY2hhbm5lbHMgd291bGQKCSAgICAgICAgICAgICAgICAvLyBkb3VibGUtYWR2YW5jZSBzaGFyZWQgcGVha3Mgd2l0aGluIGEgZnJhbWUgYW5kIGNyb3NzLXBydW5lIHBlYWsgc2V0cy4KCSAgICAgICAgICAgICAgICBjb25zdCBjaGFubmVsUm90YXRvcnMgPSAoaW5wdXRSb3RhdG9yc1tqXSA/Pz0gbmV3IFBlYWtSb3RhdG9yU3RhdGUoKSk7CgkgICAgICAgICAgICAgICAgdGhpcy5hcHBseUhhbm5XaW5kb3coaW5wdXQpOwoJICAgICAgICAgICAgICAgIHRoaXMuZmZ0LnJlYWxUcmFuc2Zvcm0odGhpcy5mcmVxQ29tcGxleEJ1ZmZlciwgaW5wdXQpOwoJICAgICAgICAgICAgICAgIC8vIFBlYWsgZGV0ZWN0ICsgcmVnaW9uLW9mLWluZmx1ZW5jZSB0cmFuc2xhdGUgKyBMYXJvY2hlLURvbHNvbiAxOTk5CgkgICAgICAgICAgICAgICAgLy8gU2VjdGlvbiAzLjUgaWRlbnRpdHktcGhhc2UtbG9jayByb3RhdGlvbiAob25lIFpfdSBwZXIgcGVhayBhcHBsaWVkCgkgICAgICAgICAgICAgICAgLy8gdW5pZm9ybWx5IHRvIGl0cyByZWdpb24pLiBBbGwgbWF0aCBpbiB0aGUgdW5pdC10ZXN0ZWQgY29yZS4KCSAgICAgICAgICAgICAgICBjb21wdXRlTWFnbml0dWRlcyh0aGlzLmZyZXFDb21wbGV4QnVmZmVyLCB0aGlzLm1hZ25pdHVkZXMpOwoJICAgICAgICAgICAgICAgIHRoaXMubmJQZWFrcyA9IGZpbmRQZWFrcyh0aGlzLm1hZ25pdHVkZXMsIHRoaXMucGVha0luZGV4ZXMpOwoJICAgICAgICAgICAgICAgIC8vIEFjY3VtdWxhdGUgZWFjaCBwZWFrJ3MgY3VtdWxhdGl2ZSByb3RhdG9yIFpfdSBieSB0aGlzIGZyYW1lJ3MKCSAgICAgICAgICAgICAgICAvLyBleHAoaipEZWx0YS1vbWVnYSpSKSBCRUZPUkUgYXBwbHlpbmcgaXQgKExhcm9jaGUtRG9sc29uIDE5OTkgU2VjdGlvbgoJICAgICAgICAgICAgICAgIC8vIDMuNSBjcm9zcy1mcmFtZSBjdW11bGF0aW9uKS4gaG9wU2l6ZSBpcyB0aGUgc3ludGhlc2lzIGhvcCBSLgoJICAgICAgICAgICAgICAgIGNoYW5uZWxSb3RhdG9ycy5hZHZhbmNlKHRoaXMucGVha0luZGV4ZXMsIHRoaXMubmJQZWFrcywgdGhpcy5mZnRTaXplLCBwaXRjaEZhY3RvciwgdGhpcy5ob3BTaXplKTsKCSAgICAgICAgICAgICAgICBzaGlmdFBlYWtzKHRoaXMuZnJlcUNvbXBsZXhCdWZmZXIsIHRoaXMuZnJlcUNvbXBsZXhCdWZmZXJTaGlmdGVkLCB0aGlzLnBlYWtJbmRleGVzLCB0aGlzLm5iUGVha3MsIHRoaXMuZmZ0U2l6ZSwgdGhpcy5tYWduaXR1ZGVzLmxlbmd0aCwgcGl0Y2hGYWN0b3IsIGNoYW5uZWxSb3RhdG9ycyk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuY29tcGxldGVTcGVjdHJ1bSh0aGlzLmZyZXFDb21wbGV4QnVmZmVyU2hpZnRlZCk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuaW52ZXJzZVRyYW5zZm9ybSh0aGlzLnRpbWVDb21wbGV4QnVmZmVyLCB0aGlzLmZyZXFDb21wbGV4QnVmZmVyU2hpZnRlZCk7CgkgICAgICAgICAgICAgICAgdGhpcy5mZnQuZnJvbUNvbXBsZXhBcnJheSh0aGlzLnRpbWVDb21wbGV4QnVmZmVyLCBvdXRwdXQpOwoJICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlIYW5uV2luZG93KG91dHB1dCk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgdGhpcy50aW1lQ3Vyc29yICs9IHRoaXMuaG9wU2l6ZTsKCSAgICB9CgkgICAgYXBwbHlIYW5uV2luZG93KGlucHV0KSB7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5ibG9ja1NpemU7IGkrKykgewoJICAgICAgICAgICAgaW5wdXRbaV0gKj0gdGhpcy5oYW5uV2luZG93W2ldOwoJICAgICAgICB9CgkgICAgfQoJfQoJcmVnaXN0ZXJQcm9jZXNzb3IoInBoYXNlLXZvY29kZXIiLCBQaGFzZVZvY29kZXJQcm9jZXNzb3IpOwoJY29uc29sZS5sb2coIlBoYXNlVm9jb2RlclByb2Nlc3NvciByZWdpc3RlcmVkIik7CgoJZXhwb3J0cy5QaGFzZVZvY29kZXJQcm9jZXNzb3IgPSBQaGFzZVZvY29kZXJQcm9jZXNzb3I7CgoJcmV0dXJuIGV4cG9ydHM7Cgp9KSh7fSk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXBoYXNlLXZvY29kZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHBoYXNlciA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIC8qCiAgICAgKiBQaGFzZXIgcHJvY2Vzc29yIGNvcmUg4oCUIGNvbnRleHQtZnJlZSBEU1AgbWF0aCBmb3IgYSBjbGFzc2ljIE1YUi9Vbml2aWJlLXN0eWxlCiAgICAgKiBwaGFzZSBzaGlmdGVyOiBhIGNhc2NhZGUgb2YgTiBpZGVudGljYWwgZmlyc3Qtb3JkZXIgYWxscGFzcyBzZWN0aW9ucyBhdCBhCiAgICAgKiBjb21tb24gTEZPLXN3ZXB0IGJyZWFrIGZyZXF1ZW5jeSwgc3VtbWVkIGFkZGl0aXZlbHkgd2l0aCB0aGUgZHJ5IHNpZ25hbC4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBhbGxwYXNzLXBoYXNlciBkZXNpZ24gZnJvbToKICAgICAqICAgSi4gTy4gU21pdGggSUlJLCAiQW4gQWxscGFzcyBBcHByb2FjaCB0byBEaWdpdGFsIFBoYXNpbmcgYW5kIEZsYW5naW5nIiwKICAgICAqICAgQ0NSTUEgUmVwb3J0IFNUQU4tTS0yMSAoMTk4MiksIHByZXNlbnRlZCBhdCBJQ01DLTg0OwogICAgICogICBhbmQgdGhlIGZpcnN0LW9yZGVyIHZpcnR1YWwtYW5hbG9nIHJlY2lwZSBpbiBKLiBPLiBTbWl0aCwgIlBoeXNpY2FsIEF1ZGlvCiAgICAgKiAgIFNpZ25hbCBQcm9jZXNzaW5nIiwgwqc4LjkgIkNsYXNzaWMgVmlydHVhbCBBbmFsb2cgUGhhc2UgU2hpZnRlcnMiLgogICAgICoKICAgICAqIFRvcG9sb2d5IChTbWl0aCBTVEFOLU0tMjEgVkc2L1ZHMTc7IFBBU1Agwqc4LjksIEZpZy4gOC4yMyk6CiAgICAgKiAgIHlbbl0gPSB4W25dICsgbWl4ICogY2FzY2FkZSh4W25dKQogICAgICogVGhlIGNhc2NhZGUgaXMgTiBmaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb25zIGluIHNlcmllcy4gQSBmaXJzdC1vcmRlciBhbGxwYXNzCiAgICAgKiBpcyBGTEFULW1hZ25pdHVkZSAofEh8ID0gMSBhdCBhbGwgZnJlcXVlbmNpZXMpLCBzbyB0aGUgRUZGRUNUIGNvbWVzIGVudGlyZWx5CiAgICAgKiBmcm9tIFNVTU1JTkcgdGhlIHBoYXNlLXNoaWZ0ZWQgY2FzY2FkZSBvdXRwdXQgd2l0aCB0aGUgZHJ5IHNpZ25hbDogYXQgZXZlcnkKICAgICAqIGZyZXF1ZW5jeSB3aGVyZSB0aGUgY2FzY2FkZSdzIGFjY3VtdWxhdGVkIHBoYXNlIHJlYWNoZXMgYW4gb2RkIG11bHRpcGxlIG9mCiAgICAgKiAxODAgZGVnIHRoZSBzdW0gY2FuY2VscywgcHJvZHVjaW5nIGEgbm90Y2guIFR3byBmaXJzdC1vcmRlciBzZWN0aW9ucyBjcmVhdGUKICAgICAqIG9uZSBub3RjaCAoUEFTUCDCpzguOTogImZvciBlYWNoIG5vdGNoIC4uLiBhZGQgdHdvIG5ldyBmaXJzdC1vcmRlciBhbGxwYXNzCiAgICAgKiBzZWN0aW9ucyIpLCBzbyBgc3RhZ2VzYCBzZWN0aW9ucyBnaXZlIHN0YWdlcy8yIG5vdGNoZXMuIFRoaXMgaXMgYW4gQURESVRJVkUKICAgICAqIHN1bSwgTk9UIGEgd2V0L2RyeSBjcm9zc2ZhZGUg4oCUIGEgY3Jvc3NmYWRlIG9mIGEgZmxhdC1tYWduaXR1ZGUgYWxscGFzcyB3b3VsZAogICAgICogbGVhdmUgdGhlIHNwZWN0cnVtIHVuY2hhbmdlZC4KICAgICAqCiAgICAgKiBGaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb24gKFBBU1Agwqc4LjkpLCBwZXItc2VjdGlvbiBzdGF0ZSB6MToKICAgICAqICAgSCh6KSA9IChhICsgel4tMSkgLyAoMSArIGEqel4tMSkKICAgICAqICAgdiAgPSBhKnMgKyB6MSA7ICB6MSA9IHMgLSBhKnYgICAgICAgIChvbmUtbXVsdGlwbHkgZGlyZWN0IGZvcm0pCiAgICAgKgogICAgICogQnJlYWstZnJlcXVlbmN5IC0+IGNvZWZmaWNpZW50IChleGFjdCBiaWxpbmVhci90YW4gbWFwLCBQQVNQIEVxLiA4LjIwKToKICAgICAqICAgcF9kID0gKDEgLSB0YW4ocGkqZmIvZnMpKSAvICgxICsgdGFuKHBpKmZiL2ZzKSkgICAgKGZiID0gYnJlYWsgZnJlcSwgSHopCiAgICAgKiBQQVNQIHdyaXRlcyBpdHMgYWxscGFzcyBhcyAocF9kIC0gel4tMSkvKDEgLSBwX2Qqel4tMSk7IGZvciBUSElTIHRlbXBsYXRlCiAgICAgKiAoYSArIHpeLTEpLygxICsgYSp6Xi0xKSB0aGUgbWF0Y2hpbmcgY29lZmZpY2llbnQgaXMgYSA9IC1wX2QgKFBBU1Agwqc4LjksIHRoZQogICAgICogc2lnbi1jb252ZW50aW9uIG5vdGUpLCBzbyB0aGUgY2FzY2FkZSdzIG5vdGNoZXMgbGFuZCBBVCB0aGUgYnJlYWsgZnJlcXVlbmN5CiAgICAgKiByYXRoZXIgdGhhbiBhdCBpdHMgTnlxdWlzdCBjb21wbGVtZW50LiBGb3IgZmIgaW4gKDAsIGZzLzIpIHRoaXMgZ2l2ZXMgfGF8IDwgMQogICAgICogKGEgc3RhYmxlIGFsbHBhc3MgcG9sZSksIGFuZCBTbWl0aCdzIHN0cnVjdHVyZS1nYWluIGJvdW5kIGtlZXBzIHRoZSBzdW1tZWQKICAgICAqIG91dHB1dCBzdHJpY3RseSBpbiBbMCwgMl0gZm9yIGFueSBub3RjaCBzZXR0aW5nIGdpdmVuIHN0YWJsZSBzZWN0aW9ucwogICAgICogKFNUQU4tTS0yMSBwYWdlLTAzKS4KICAgICAqCiAgICAgKiBQZXIgc2FtcGxlOgogICAgICogICBmYiAgID0gZnJlcXVlbmN5ICogMl4oZGVwdGggKiBsZm8pICAgICAgIC8vIG11bHRpcGxpY2F0aXZlL2xvZyBzd2VlcCAobGZvIGluIFstMSwxXSkKICAgICAqICAgcCAgICA9IGJpbGluZWFyKGZiKSAgICAgICAgICAgICAgICAgICAgICAgIC8vICAgcHJlc2VydmVzIG5vdGNoIGZyZXF1ZW5jeSBSQVRJT1MKICAgICAqICAgcyAgICA9IHggKyBmZWVkYmFjayAqIGxhc3RDYXNjYWRlT3V0ICAgICAgIC8vIHJlZ2VuZXJhdGlvbi9yZXNvbmFuY2UgKGZlZWRiYWNrPTAgPT4gcz14KQogICAgICogICB2ICAgID0gY2FzY2FkZSBvZiBgc3RhZ2VzYCBhbGxwYXNzIHNlY3Rpb25zIGFwcGxpZWQgdG8gcwogICAgICogICB5ICAgID0geCArIG1peCAqIHYgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQURESVRJVkUgKFNtaXRoIHkgPSB4ICsgZyphbGxwYXNzKQogICAgICogICBsYXN0Q2FzY2FkZU91dCA9IHYKICAgICAqIExGTzogc2ludXNvaWRhbCwgYmlwb2xhciBbLTEsIDFdOyBwaGFzZSBhZHZhbmNlcyBhdCBgcmF0ZWAgSHouIFN0ZXJlbyB1c2VzIGEKICAgICAqIHF1YWRyYXR1cmUgTEZPICg5MCBkZWcgcGVyIGNoYW5uZWwsIERhdHRvcnJvIDE5OTcgcC43NzYpLCByZWFsaXplZCBieSBzZWVkaW5nCiAgICAgKiBlYWNoIGNoYW5uZWwncyBjb3JlIHdpdGggbGZvUGhhc2UwID0gY2ggKiBwaS8yIGF0IGNvbnN0cnVjdGlvbiAoc2VlIHBoYXNlci50cykuCiAgICAgKgogICAgICogVGhpcyBmaWxlIGhvbGRzIE9OTFkgcHVyZSBudW1lcmljIG1hdGggKHBsYWluIG51bWJlcnMgKyBGbG9hdDMyQXJyYXkpLiBJdCBoYXMKICAgICAqIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMsIHNvIGl0IGlzIHVuaXQtdGVzdGFibGUgZGlyZWN0bHk7IHRoZQogICAgICogd29ya2xldCBzaGVsbCBpbiBwaGFzZXIudHMgZGVsZWdhdGVzIHRvIGl0LgogICAgICovCiAgICAvKioKICAgICAqIERlZmF1bHQgQXVkaW9QYXJhbSB2YWx1ZXMgZm9yIHRoZSBwaGFzZXIgd29ya2xldCwgaW4gdGhlIHdvcmtsZXQncyBvd24gdW5pdHMuCiAgICAgKiBTSU5HTEUgU09VUkNFIE9GIFRSVVRIIOKAlCB0aGUgd29ya2xldCdzIGBwYXJhbWV0ZXJEZXNjcmlwdG9yc2AgKHBoYXNlci50cykKICAgICAqIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdCBuZWVkIHRoZSBzaGlwcGVkCiAgICAgKiBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgY2xhc3NpYyA0LXNlY3Rpb24gKDItbm90Y2gpIE1YUi1zdHlsZSBwaGFzZXIsIG1pZC1zcGVjdHJ1bSBjZW50ZXIsCiAgICAgKiBzbG93IHN3ZWVwIOKAlCB0aGUgY3JlYXRlUGhhc2VyIGZhY3RvcnkgKGNhY29waG9ueS50cykgc3ByZWFkcyBvcHRpb24gb3ZlcnJpZGVzCiAgICAgKiBvdmVyIHRoZXNlLgogICAgICovCiAgICBjb25zdCBQSEFTRVJfREVGQVVMVFMgPSB7CiAgICAgICAgZnJlcXVlbmN5OiA1MDAsCiAgICAgICAgcmF0ZTogMC41LAogICAgICAgIGRlcHRoOiAxLjUsCiAgICAgICAgc3RhZ2VzOiA0LAogICAgICAgIGZlZWRiYWNrOiAwLAogICAgICAgIG1peDogMC41LAogICAgfTsKICAgIC8qKiBNYXhpbXVtIG51bWJlciBvZiBhbGxwYXNzIHNlY3Rpb25zIHRoZSBzZWN0aW9uLXN0YXRlIGFycmF5IGlzIHNpemVkIGZvci4gKi8KICAgIGNvbnN0IE1BWF9TVEFHRVMgPSAxMjsKICAgIC8qKiBTdGFiaWxpdHkvc2FmZXR5IGJvdW5kIG9uIHxmZWVkYmFja3wgZm9yIHRoZSByZWdlbmVyYXRpb24gbG9vcC4gKi8KICAgIGNvbnN0IE1BWF9QSEFTRVJfRkVFREJBQ0sgPSAwLjk1OwogICAgLyoqCiAgICAgKiBCcmVhay1mcmVxdWVuY3kgLT4gZmlyc3Qtb3JkZXItYWxscGFzcyBjb2VmZmljaWVudCB2aWEgdGhlIGV4YWN0IGJpbGluZWFyL3RhbgogICAgICogbWFwIChQQVNQIEVxLiA4LjIwKToKICAgICAqICAgcCA9ICgxIC0gdGFuKHBpKmZiL2ZzKSkgLyAoMSArIHRhbihwaSpmYi9mcykpCiAgICAgKiBGb3IgZmIgaW4gKDAsIGZzLzIpIHRoZSByZXN1bHQgbGllcyBpbiAoLTEsIDEpIChhIHN0YWJsZSBhbGxwYXNzIHBvbGUpOyBhcwogICAgICogZmIgLT4gMCwgcCAtPiAxLCBhbmQgYXMgZmIgLT4gZnMvMiwgcCAtPiAtMS4gRXhwb3NlZCBwdXJlIGZvciBpc29sYXRlZCB0ZXN0aW5nLgogICAgICovCiAgICBmdW5jdGlvbiBicmVha0ZyZXFUb0FsbHBhc3NDb2VmZihicmVha0ZyZXFIeiwgc2FtcGxlUmF0ZSkgewogICAgICAgIGNvbnN0IHQgPSBNYXRoLnRhbigoTWF0aC5QSSAqIGJyZWFrRnJlcUh6KSAvIHNhbXBsZVJhdGUpOwogICAgICAgIHJldHVybiAoMSAtIHQpIC8gKDEgKyB0KTsKICAgIH0KICAgIC8qKgogICAgICogU3RhdGVmdWwgcGhhc2VyIHByb2Nlc3NvciBmb3IgT05FIGNoYW5uZWwuIE93bnMgdGhlIHBlci1zZWN0aW9uIGFsbHBhc3Mgc3RhdGVzCiAgICAgKiAoejEgZm9yIGVhY2ggb2YgdXAgdG8gTUFYX1NUQUdFUyBzZWN0aW9ucyksIHRoZSBmZWVkYmFjayBtZW1vcnkgYW5kIHRoZSBMRk8KICAgICAqIHBoYXNlLCBhbmQgaXMgcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvIHRoZSBhbGxwYXNzIGhpc3RvcmllcywgcmVnZW5lcmF0aW9uIGFuZAogICAgICogTEZPIGNvbnRpbnVpdHkgYWxsIHN1cnZpdmUgYWNyb3NzIHByb2Nlc3MoKSBjYWxscyDigJQgY29uc3RydWN0aW5nIGEgZnJlc2gKICAgICAqIGluc3RhbmNlIHBlciBibG9jayB3b3VsZCBjbGVhciB0aGUgc2VjdGlvbiBzdGF0ZXMgYW5kIHJlc2V0IHRoZSBMRk8sIHByb2R1Y2luZwogICAgICogYXVkaWJsZSBjbGlja3MgKHNhbWUgaGF6YXJkIGRvY3VtZW50ZWQgZm9yIHRoZSBtb2R1bGF0ZWQtZGVsYXkgY29yZSkuCiAgICAgKi8KICAgIGNsYXNzIFBoYXNlclByb2Nlc3NvciB7CiAgICAgICAgc2FtcGxlUmF0ZTsKICAgICAgICAvKiogUGVyLXNlY3Rpb24gYWxscGFzcyBkZWxheS1lbGVtZW50IHN0YXRlcyB6MSAob25lIHNsb3QgcGVyIHNlY3Rpb24pLiAqLwogICAgICAgIHoxOwogICAgICAgIC8qKiBMYXN0IGNhc2NhZGUgb3V0cHV0LCBmZWQgYmFjayBpbnRvIHRoZSBpbnB1dCAocmVnZW5lcmF0aW9uKS4gKi8KICAgICAgICBsYXN0Q2FzY2FkZU91dCA9IDA7CiAgICAgICAgLyoqIEN1cnJlbnQgTEZPIHBoYXNlIChyYWRpYW5zKS4gKi8KICAgICAgICBsZm9QaGFzZTsKICAgICAgICBjb25zdHJ1Y3RvcihzYW1wbGVSYXRlLCBsZm9QaGFzZTAgPSAwKSB7CiAgICAgICAgICAgIHRoaXMuc2FtcGxlUmF0ZSA9IHNhbXBsZVJhdGU7CiAgICAgICAgICAgIHRoaXMuejEgPSBuZXcgRmxvYXQzMkFycmF5KE1BWF9TVEFHRVMpOwogICAgICAgICAgICB0aGlzLmxmb1BoYXNlID0gbGZvUGhhc2UwOwogICAgICAgIH0KICAgICAgICAvKiogQ2xlYXIgYWxsIGFsbHBhc3Mgc2VjdGlvbiBzdGF0ZXMsIHRoZSBmZWVkYmFjayBtZW1vcnkgYW5kIHRoZSBMRk8gcGhhc2UuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMuejEuZmlsbCgwKTsKICAgICAgICAgICAgdGhpcy5sYXN0Q2FzY2FkZU91dCA9IDA7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2suIGBpbnB1dGAgYW5kIGBvdXRwdXRgIG1heSBhbGlhcyAoaW4tcGxhY2UgaXMgZmluZSkuCiAgICAgICAgICoKICAgICAgICAgKiBQZXIgc2FtcGxlIChTbWl0aCBTVEFOLU0tMjEgLyBQQVNQIMKnOC45KToKICAgICAgICAgKiAgIDEuIGxmbyAgPSBzaW4obGZvUGhhc2UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGJpcG9sYXIgWy0xLCAxXSkKICAgICAgICAgKiAgIDIuIGZiICAgPSBmcmVxdWVuY3kgKiAyXihkZXB0aCAqIGxmbykgICAgICAgICAgICAgICAgKG11bHRpcGxpY2F0aXZlIHN3ZWVwKQogICAgICAgICAqICAgMy4gYSAgICA9IC1iaWxpbmVhcihmYikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoRXEuIDguMjAsIGEgPSAtcF9kKQogICAgICAgICAqICAgNC4gcyAgICA9IHggKyBmZWVkYmFjayAqIGxhc3RDYXNjYWRlT3V0ICAgICAgICAgICAgICAocmVnZW5lcmF0aW9uKQogICAgICAgICAqICAgNS4gdiAgICA9IGNhc2NhZGUgb2YgYHN0YWdlc2AgYWxscGFzcyBzZWN0aW9ucyBvbiBzICAob25lIHoxIGVhY2gpCiAgICAgICAgICogICA2LiB5ICAgID0geCArIG1peCAqIHYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChBRERJVElWRSBzdW0pCiAgICAgICAgICogICA3LiBsYXN0Q2FzY2FkZU91dCA9IHYKICAgICAgICAgKiBUaGUgTEZPIHBoYXNlIGFkdmFuY2VzIGJ5IDIqcGkqcmF0ZS9zYW1wbGVSYXRlIGVhY2ggc2FtcGxlLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIC8vIGstcmF0ZS1zdHlsZSBjb2VmZmljaWVudHMgY29tcHV0ZWQgb25jZSBwZXIgYmxvY2suCiAgICAgICAgICAgIGNvbnN0IGZyZXF1ZW5jeSA9IHBhcmFtcy5mcmVxdWVuY3k7CiAgICAgICAgICAgIGNvbnN0IGRlcHRoID0gcGFyYW1zLmRlcHRoOwogICAgICAgICAgICBjb25zdCBmZWVkYmFjayA9IE1hdGgubWF4KC1NQVhfUEhBU0VSX0ZFRURCQUNLLCBNYXRoLm1pbihNQVhfUEhBU0VSX0ZFRURCQUNLLCBwYXJhbXMuZmVlZGJhY2spKTsKICAgICAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1zLm1peDsKICAgICAgICAgICAgY29uc3Qgc3RhZ2VzID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oTUFYX1NUQUdFUywgTWF0aC5yb3VuZChwYXJhbXMuc3RhZ2VzKSkpOwogICAgICAgICAgICBjb25zdCBwaGFzZUluYyA9ICgyICogTWF0aC5QSSAqIHBhcmFtcy5yYXRlKSAvIHRoaXMuc2FtcGxlUmF0ZTsKICAgICAgICAgICAgY29uc3QgbnlxdWlzdCA9IHRoaXMuc2FtcGxlUmF0ZSAvIDI7CiAgICAgICAgICAgIGNvbnN0IG4gPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgeCA9IGlucHV0W2ldOwogICAgICAgICAgICAgICAgLy8gU2ludXNvaWRhbCBiaXBvbGFyIExGTyAtPiBtdWx0aXBsaWNhdGl2ZSBicmVhay1mcmVxdWVuY3kgc3dlZXAuIENsYW1wIHRoZQogICAgICAgICAgICAgICAgLy8gc3dlcHQgZnJlcXVlbmN5IHRvICgwLCBOeXF1aXN0KSBzbyB0aGUgYmlsaW5lYXIgbWFwIHN0YXlzIHdlbGwtZGVmaW5lZC4KICAgICAgICAgICAgICAgIGNvbnN0IGxmbyA9IE1hdGguc2luKHRoaXMubGZvUGhhc2UpOwogICAgICAgICAgICAgICAgbGV0IGZiID0gZnJlcXVlbmN5ICogMiAqKiAoZGVwdGggKiBsZm8pOwogICAgICAgICAgICAgICAgaWYgKGZiIDwgMSkKICAgICAgICAgICAgICAgICAgICBmYiA9IDE7CiAgICAgICAgICAgICAgICBpZiAoZmIgPiBueXF1aXN0IC0gMSkKICAgICAgICAgICAgICAgICAgICBmYiA9IG55cXVpc3QgLSAxOwogICAgICAgICAgICAgICAgLy8gQmlsaW5lYXIgcG9sZSBwX2QgKFBBU1AgRXEuIDguMjApLiBUaGUgc2VjdGlvbiB0ZW1wbGF0ZSBoZXJlIGlzCiAgICAgICAgICAgICAgICAvLyAoYSArIHpeLTEpLygxICsgYSp6Xi0xKSwgc28gYSA9IC1wX2QgbWFrZXMgdGhlIGNhc2NhZGUgbm90Y2ggQVQgZmIKICAgICAgICAgICAgICAgIC8vIChub3QgYXQgaXRzIE55cXVpc3QgY29tcGxlbWVudCkuIFNlZSBhbGxwYXNzU3RlcCdzIHNpZ24gbm90ZS4KICAgICAgICAgICAgICAgIGNvbnN0IGEgPSAtYnJlYWtGcmVxVG9BbGxwYXNzQ29lZmYoZmIsIHRoaXMuc2FtcGxlUmF0ZSk7CiAgICAgICAgICAgICAgICAvLyBSZWdlbmVyYXRpb246IGZlZWQgdGhlIHByZXZpb3VzIGNhc2NhZGUgb3V0cHV0IGJhY2sgaW50byB0aGUgaW5wdXQuCiAgICAgICAgICAgICAgICBsZXQgdiA9IHggKyBmZWVkYmFjayAqIHRoaXMubGFzdENhc2NhZGVPdXQ7CiAgICAgICAgICAgICAgICAvLyBDYXNjYWRlIG9mIGBzdGFnZXNgIGlkZW50aWNhbCBmaXJzdC1vcmRlciBhbGxwYXNzIHNlY3Rpb25zLgogICAgICAgICAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCBzdGFnZXM7IGsrKykgewogICAgICAgICAgICAgICAgICAgIGNvbnN0IHByZXYgPSB0aGlzLnoxW2tdOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG91dCA9IGEgKiB2ICsgcHJldjsKICAgICAgICAgICAgICAgICAgICB0aGlzLnoxW2tdID0gdiAtIGEgKiBvdXQ7CiAgICAgICAgICAgICAgICAgICAgdiA9IG91dDsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMubGFzdENhc2NhZGVPdXQgPSB2OwogICAgICAgICAgICAgICAgLy8gQWRkaXRpdmUgb3V0cHV0IChTbWl0aCB5ID0geCArIGcqYWxscGFzcyk6IHRoZSBub3RjaGVzIGNvbWUgZnJvbSB0aGUgc3VtLgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0geCArIG1peCAqIHY7CiAgICAgICAgICAgICAgICAvLyBBZHZhbmNlIHRoZSBMRk8gcGhhc2UuCiAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlICs9IHBoYXNlSW5jOwogICAgICAgICAgICAgICAgaWYgKHRoaXMubGZvUGhhc2UgPj0gMiAqIE1hdGguUEkpCiAgICAgICAgICAgICAgICAgICAgdGhpcy5sZm9QaGFzZSAtPSAyICogTWF0aC5QSTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvKgogICAgICogUGhhc2VyIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMIERTUAogICAgICogbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFBoYXNlclByb2Nlc3NvciBpbiBwaGFzZXItY29yZS50cy4gTWlycm9ycyB0aGUKICAgICAqIG1vZHVsYXRlZC1kZWxheS50cyAvIGR5bmFtaWNzLnRzIGNvcmUvc2hlbGwgc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlCiAgICAgKiB3b3JrbGV0IHBsdW1iaW5nIChwYXJhbWV0ZXJEZXNjcmlwdG9ycywgcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IKICAgICAqIGNhbGwpOyB0aGUgYWxnb3JpdGhtIGxpdmVzIGluIHRoZSB1bml0LXRlc3RlZCBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogY2xhc3NpYyBNWFIvVW5pdmliZS1zdHlsZSBjYXNjYWRlIG9mIGZpcnN0LW9yZGVyIGFsbHBhc3Mgc2VjdGlvbnMKICAgICAqIGF0IGEgY29tbW9uIExGTy1zd2VwdCBicmVhayBmcmVxdWVuY3ksIHN1bW1lZCBhZGRpdGl2ZWx5IHdpdGggdGhlIGRyeSBzaWduYWwKICAgICAqIChTbWl0aCBTVEFOLU0tMjE7IFBBU1Agwqc4LjkpLiBTZWUgcGhhc2VyLWNvcmUudHMgaGVhZGVyIGZvciB0aGUgc2VjdGlvbgogICAgICogdHJhbnNmZXIgZnVuY3Rpb24sIHRoZSBiaWxpbmVhciBicmVhay1mcmVxdWVuY3kgbWFwLCB0aGUgYWRkaXRpdmUtbm90Y2gKICAgICAqIHJhdGlvbmFsZSBhbmQgdGhlIG11bHRpcGxpY2F0aXZlIExGTyBzd2VlcC4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpwaGFzZXJdIjsKICAgIGNsYXNzIFBoYXNlcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIC8vIE9uZSBzdGF0ZWZ1bCBjb3JlIHBlciBjaGFubmVsIHNvIGVhY2ggY2hhbm5lbCBrZWVwcyBpdHMgb3duIGFsbHBhc3Mgc2VjdGlvbgogICAgICAgIC8vIHN0YXRlcywgZmVlZGJhY2sgbWVtb3J5IGFuZCBMRk8gcGhhc2UgYWNyb3NzIHByb2Nlc3MoKSBibG9ja3MuIEVhY2gKICAgICAgICAvLyBjaGFubmVsJ3MgTEZPIGlzIHNlZWRlZCBpbiBxdWFkcmF0dXJlIChjaCAqIHBpLzIpIHNvIGEgc3RlcmVvIHBhaXIgZ2V0cyB0aGUKICAgICAgICAvLyBkeW5hbWljIHN0ZXJlbyBmaWVsZCBEYXR0b3JybyBkZXNjcmliZXMgKHAuNzc2KS4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBQSEFTRVJfREVGQVVMVFMgKHBoYXNlci1jb3JlLnRzKSDigJQgdGhlIHNpbmdsZQogICAgICAgICAgICAvLyBzb3VyY2Ugb2YgdHJ1dGggc2hhcmVkIHdpdGggdGhlIGNvcmUgdGVzdHMuIFJhbmdlcyBsaXZlIGhlcmUuIGBzdGFnZXNgIGlzCiAgICAgICAgICAgIC8vIGEgY291bnQgKHJvdW5kZWQgdG8gYW4gaW50IGluIHRoZSBjb3JlKTsgfGZlZWRiYWNrfCA8PSAwLjk1IGtlZXBzIHRoZQogICAgICAgICAgICAvLyByZWdlbmVyYXRpb24gbG9vcCBib3VuZGVkLgogICAgICAgICAgICByZXR1cm4gWwogICAgICAgICAgICAgICAgWyJmcmVxdWVuY3kiLCBQSEFTRVJfREVGQVVMVFMuZnJlcXVlbmN5LCAyMCwgMTAwMDAsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsicmF0ZSIsIFBIQVNFUl9ERUZBVUxUUy5yYXRlLCAwLCAyMCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJkZXB0aCIsIFBIQVNFUl9ERUZBVUxUUy5kZXB0aCwgMCwgNCwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJzdGFnZXMiLCBQSEFTRVJfREVGQVVMVFMuc3RhZ2VzLCAyLCAxMiwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJmZWVkYmFjayIsIFBIQVNFUl9ERUZBVUxUUy5mZWVkYmFjaywgLTAuOTUsIDAuOTUsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsibWl4IiwgUEhBU0VSX0RFRkFVTFRTLm1peCwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICBdLm1hcCgoW25hbWUsIGRlZmF1bHRWYWx1ZSwgbWluVmFsdWUsIG1heFZhbHVlLCBhdXRvbWF0aW9uUmF0ZV0pID0+ICh7CiAgICAgICAgICAgICAgICBuYW1lOiBuYW1lLAogICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUsCiAgICAgICAgICAgICAgICBtaW5WYWx1ZTogbWluVmFsdWUsCiAgICAgICAgICAgICAgICBtYXhWYWx1ZTogbWF4VmFsdWUsCiAgICAgICAgICAgICAgICBhdXRvbWF0aW9uUmF0ZTogYXV0b21hdGlvblJhdGUsCiAgICAgICAgICAgIH0pKTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF07CiAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICAgICAgICAgIGlmICghaW5wdXQgfHwgaW5wdXQubGVuZ3RoID09PSAwIHx8ICFvdXRwdXQgfHwgb3V0cHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gay1yYXRlIHBhcmFtczogdGFrZSB0aGUgZmlyc3QgKGNvbnN0YW50LW92ZXItYmxvY2spIHZhbHVlLgogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICBmcmVxdWVuY3k6IHBhcmFtZXRlcnMuZnJlcXVlbmN5WzBdLAogICAgICAgICAgICAgICAgcmF0ZTogcGFyYW1ldGVycy5yYXRlWzBdLAogICAgICAgICAgICAgICAgZGVwdGg6IHBhcmFtZXRlcnMuZGVwdGhbMF0sCiAgICAgICAgICAgICAgICBzdGFnZXM6IHBhcmFtZXRlcnMuc3RhZ2VzWzBdLAogICAgICAgICAgICAgICAgZmVlZGJhY2s6IHBhcmFtZXRlcnMuZmVlZGJhY2tbMF0sCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gU2VlZCB0aGUgTEZPIDkwIGRlZyBhcGFydCBwZXIgY2hhbm5lbCBmb3IgdGhlIHF1YWRyYXR1cmUgc3RlcmVvIGZpZWxkCiAgICAgICAgICAgICAgICAgICAgLy8gKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICAgICAgICAgICAgICB0aGlzLmNvcmVzW2NoXSA9IG5ldyBQaGFzZXJQcm9jZXNzb3Ioc2FtcGxlUmF0ZSwgKGNoICogTWF0aC5QSSkgLyAyKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJwaGFzZXIiLCBQaGFzZXJXb3JrbGV0UHJvY2Vzc29yKTsKICAgICAgICBjb25zb2xlLmluZm8oYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBjb21wbGV0ZWApOwogICAgfQogICAgY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgY29uc29sZS5lcnJvcihgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGZhaWxlZGAsIGVycm9yKTsKICAgICAgICB0aHJvdyBlcnJvcjsKICAgIH0KCiAgICBleHBvcnRzLlBoYXNlcldvcmtsZXRQcm9jZXNzb3IgPSBQaGFzZXJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1waGFzZXItYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHNwZWN0cmFsRnJlZXplID0gKGZ1bmN0aW9uIChleHBvcnRzKSB7CgkndXNlIHN0cmljdCc7CgoJZnVuY3Rpb24gZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMgKHgpIHsKCQlyZXR1cm4geCAmJiB4Ll9fZXNNb2R1bGUgJiYgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdkZWZhdWx0JykgPyB4WydkZWZhdWx0J10gOiB4OwoJfQoKCXZhciBmZnQ7Cgl2YXIgaGFzUmVxdWlyZWRGZnQ7CgoJZnVuY3Rpb24gcmVxdWlyZUZmdCAoKSB7CgkJaWYgKGhhc1JlcXVpcmVkRmZ0KSByZXR1cm4gZmZ0OwoJCWhhc1JlcXVpcmVkRmZ0ID0gMTsKCgkJZnVuY3Rpb24gRkZUKHNpemUpIHsKCQkgIHRoaXMuc2l6ZSA9IHNpemUgfCAwOwoJCSAgaWYgKHRoaXMuc2l6ZSA8PSAxIHx8ICh0aGlzLnNpemUgJiAodGhpcy5zaXplIC0gMSkpICE9PSAwKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZGVCBzaXplIG11c3QgYmUgYSBwb3dlciBvZiB0d28gYW5kIGJpZ2dlciB0aGFuIDEnKTsKCgkJICB0aGlzLl9jc2l6ZSA9IHNpemUgPDwgMTsKCgkJICAvLyBOT1RFOiBVc2Ugb2YgYHZhcmAgaXMgaW50ZW50aW9uYWwgZm9yIG9sZCBWOCB2ZXJzaW9ucwoJCSAgdmFyIHRhYmxlID0gbmV3IEFycmF5KHRoaXMuc2l6ZSAqIDIpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCB0YWJsZS5sZW5ndGg7IGkgKz0gMikgewoJCSAgICBjb25zdCBhbmdsZSA9IE1hdGguUEkgKiBpIC8gdGhpcy5zaXplOwoJCSAgICB0YWJsZVtpXSA9IE1hdGguY29zKGFuZ2xlKTsKCQkgICAgdGFibGVbaSArIDFdID0gLU1hdGguc2luKGFuZ2xlKTsKCQkgIH0KCQkgIHRoaXMudGFibGUgPSB0YWJsZTsKCgkJICAvLyBGaW5kIHNpemUncyBwb3dlciBvZiB0d28KCQkgIHZhciBwb3dlciA9IDA7CgkJICBmb3IgKHZhciB0ID0gMTsgdGhpcy5zaXplID4gdDsgdCA8PD0gMSkKCQkgICAgcG93ZXIrKzsKCgkJICAvLyBDYWxjdWxhdGUgaW5pdGlhbCBzdGVwJ3Mgd2lkdGg6CgkJICAvLyAgICogSWYgd2UgYXJlIGZ1bGwgcmFkaXgtNCAtIGl0IGlzIDJ4IHNtYWxsZXIgdG8gZ2l2ZSBpbml0YWwgbGVuPTgKCQkgIC8vICAgKiBPdGhlcndpc2UgaXQgaXMgdGhlIHNhbWUgYXMgYHBvd2VyYCB0byBnaXZlIGxlbj00CgkJICB0aGlzLl93aWR0aCA9IHBvd2VyICUgMiA9PT0gMCA/IHBvd2VyIC0gMSA6IHBvd2VyOwoKCQkgIC8vIFByZS1jb21wdXRlIGJpdC1yZXZlcnNhbCBwYXR0ZXJucwoJCSAgdGhpcy5fYml0cmV2ID0gbmV3IEFycmF5KDEgPDwgdGhpcy5fd2lkdGgpOwoJCSAgZm9yICh2YXIgaiA9IDA7IGogPCB0aGlzLl9iaXRyZXYubGVuZ3RoOyBqKyspIHsKCQkgICAgdGhpcy5fYml0cmV2W2pdID0gMDsKCQkgICAgZm9yICh2YXIgc2hpZnQgPSAwOyBzaGlmdCA8IHRoaXMuX3dpZHRoOyBzaGlmdCArPSAyKSB7CgkJICAgICAgdmFyIHJldlNoaWZ0ID0gdGhpcy5fd2lkdGggLSBzaGlmdCAtIDI7CgkJICAgICAgdGhpcy5fYml0cmV2W2pdIHw9ICgoaiA+Pj4gc2hpZnQpICYgMykgPDwgcmV2U2hpZnQ7CgkJICAgIH0KCQkgIH0KCgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJICB0aGlzLl9pbnYgPSAwOwoJCX0KCQlmZnQgPSBGRlQ7CgoJCUZGVC5wcm90b3R5cGUuZnJvbUNvbXBsZXhBcnJheSA9IGZ1bmN0aW9uIGZyb21Db21wbGV4QXJyYXkoY29tcGxleCwgc3RvcmFnZSkgewoJCSAgdmFyIHJlcyA9IHN0b3JhZ2UgfHwgbmV3IEFycmF5KGNvbXBsZXgubGVuZ3RoID4+PiAxKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgY29tcGxleC5sZW5ndGg7IGkgKz0gMikKCQkgICAgcmVzW2kgPj4+IDFdID0gY29tcGxleFtpXTsKCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5jcmVhdGVDb21wbGV4QXJyYXkgPSBmdW5jdGlvbiBjcmVhdGVDb21wbGV4QXJyYXkoKSB7CgkJICBjb25zdCByZXMgPSBuZXcgQXJyYXkodGhpcy5fY3NpemUpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXMubGVuZ3RoOyBpKyspCgkJICAgIHJlc1tpXSA9IDA7CgkJICByZXR1cm4gcmVzOwoJCX07CgoJCUZGVC5wcm90b3R5cGUudG9Db21wbGV4QXJyYXkgPSBmdW5jdGlvbiB0b0NvbXBsZXhBcnJheShpbnB1dCwgc3RvcmFnZSkgewoJCSAgdmFyIHJlcyA9IHN0b3JhZ2UgfHwgdGhpcy5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCQkgIGZvciAodmFyIGkgPSAwOyBpIDwgcmVzLmxlbmd0aDsgaSArPSAyKSB7CgkJICAgIHJlc1tpXSA9IGlucHV0W2kgPj4+IDFdOwoJCSAgICByZXNbaSArIDFdID0gMDsKCQkgIH0KCQkgIHJldHVybiByZXM7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5jb21wbGV0ZVNwZWN0cnVtID0gZnVuY3Rpb24gY29tcGxldGVTcGVjdHJ1bShzcGVjdHJ1bSkgewoJCSAgdmFyIHNpemUgPSB0aGlzLl9jc2l6ZTsKCQkgIHZhciBoYWxmID0gc2l6ZSA+Pj4gMTsKCQkgIGZvciAodmFyIGkgPSAyOyBpIDwgaGFsZjsgaSArPSAyKSB7CgkJICAgIHNwZWN0cnVtW3NpemUgLSBpXSA9IHNwZWN0cnVtW2ldOwoJCSAgICBzcGVjdHJ1bVtzaXplIC0gaSArIDFdID0gLXNwZWN0cnVtW2kgKyAxXTsKCQkgIH0KCQl9OwoKCQlGRlQucHJvdG90eXBlLnRyYW5zZm9ybSA9IGZ1bmN0aW9uIHRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAwOwoJCSAgdGhpcy5fdHJhbnNmb3JtNCgpOwoJCSAgdGhpcy5fb3V0ID0gbnVsbDsKCQkgIHRoaXMuX2RhdGEgPSBudWxsOwoJCX07CgoJCUZGVC5wcm90b3R5cGUucmVhbFRyYW5zZm9ybSA9IGZ1bmN0aW9uIHJlYWxUcmFuc2Zvcm0ob3V0LCBkYXRhKSB7CgkJICBpZiAob3V0ID09PSBkYXRhKQoJCSAgICB0aHJvdyBuZXcgRXJyb3IoJ0lucHV0IGFuZCBvdXRwdXQgYnVmZmVycyBtdXN0IGJlIGRpZmZlcmVudCcpOwoKCQkgIHRoaXMuX291dCA9IG91dDsKCQkgIHRoaXMuX2RhdGEgPSBkYXRhOwoJCSAgdGhpcy5faW52ID0gMDsKCQkgIHRoaXMuX3JlYWxUcmFuc2Zvcm00KCk7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJRkZULnByb3RvdHlwZS5pbnZlcnNlVHJhbnNmb3JtID0gZnVuY3Rpb24gaW52ZXJzZVRyYW5zZm9ybShvdXQsIGRhdGEpIHsKCQkgIGlmIChvdXQgPT09IGRhdGEpCgkJICAgIHRocm93IG5ldyBFcnJvcignSW5wdXQgYW5kIG91dHB1dCBidWZmZXJzIG11c3QgYmUgZGlmZmVyZW50Jyk7CgoJCSAgdGhpcy5fb3V0ID0gb3V0OwoJCSAgdGhpcy5fZGF0YSA9IGRhdGE7CgkJICB0aGlzLl9pbnYgPSAxOwoJCSAgdGhpcy5fdHJhbnNmb3JtNCgpOwoJCSAgZm9yICh2YXIgaSA9IDA7IGkgPCBvdXQubGVuZ3RoOyBpKyspCgkJICAgIG91dFtpXSAvPSB0aGlzLnNpemU7CgkJICB0aGlzLl9vdXQgPSBudWxsOwoJCSAgdGhpcy5fZGF0YSA9IG51bGw7CgkJfTsKCgkJLy8gcmFkaXgtNCBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogVXNlcyBvZiBgdmFyYCBhcmUgaW50ZW50aW9uYWwgZm9yIG9sZGVyIFY4IHZlcnNpb24gdGhhdCBkbyBub3QKCQkvLyBzdXBwb3J0IGJvdGggYGxldCBjb21wb3VuZCBhc3NpZ25tZW50c2AgYW5kIGBjb25zdCBwaGlgCgkJRkZULnByb3RvdHlwZS5fdHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF90cmFuc2Zvcm00KCkgewoJCSAgdmFyIG91dCA9IHRoaXMuX291dDsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgoJCSAgLy8gSW5pdGlhbCBzdGVwIChwZXJtdXRlIGFuZCB0cmFuc2Zvcm0pCgkJICB2YXIgd2lkdGggPSB0aGlzLl93aWR0aDsKCQkgIHZhciBzdGVwID0gMSA8PCB3aWR0aDsKCQkgIHZhciBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgoJCSAgdmFyIG91dE9mZjsKCQkgIHZhciB0OwoJCSAgdmFyIGJpdHJldiA9IHRoaXMuX2JpdHJldjsKCQkgIGlmIChsZW4gPT09IDQpIHsKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVRyYW5zZm9ybTIob3V0T2ZmLCBvZmYsIHN0ZXApOwoJCSAgICB9CgkJICB9IGVsc2UgewoJCSAgICAvLyBsZW4gPT09IDgKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVRyYW5zZm9ybTQob3V0T2ZmLCBvZmYsIHN0ZXApOwoJCSAgICB9CgkJICB9CgoJCSAgLy8gTG9vcCB0aHJvdWdoIHN0ZXBzIGluIGRlY3JlYXNpbmcgb3JkZXIKCQkgIHZhciBpbnYgPSB0aGlzLl9pbnYgPyAtMSA6IDE7CgkJICB2YXIgdGFibGUgPSB0aGlzLnRhYmxlOwoJCSAgZm9yIChzdGVwID4+PSAyOyBzdGVwID49IDI7IHN0ZXAgPj49IDIpIHsKCQkgICAgbGVuID0gKHNpemUgLyBzdGVwKSA8PCAxOwoJCSAgICB2YXIgcXVhcnRlckxlbiA9IGxlbiA+Pj4gMjsKCgkJICAgIC8vIExvb3AgdGhyb3VnaCBvZmZzZXRzIGluIHRoZSBkYXRhCgkJICAgIGZvciAob3V0T2ZmID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbikgewoJCSAgICAgIC8vIEZ1bGwgY2FzZQoJCSAgICAgIHZhciBsaW1pdCA9IG91dE9mZiArIHF1YXJ0ZXJMZW47CgkJICAgICAgZm9yICh2YXIgaSA9IG91dE9mZiwgayA9IDA7IGkgPCBsaW1pdDsgaSArPSAyLCBrICs9IHN0ZXApIHsKCQkgICAgICAgIGNvbnN0IEEgPSBpOwoJCSAgICAgICAgY29uc3QgQiA9IEEgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgY29uc3QgQyA9IEIgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgY29uc3QgRCA9IEMgKyBxdWFydGVyTGVuOwoKCQkgICAgICAgIC8vIE9yaWdpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgQXIgPSBvdXRbQV07CgkJICAgICAgICBjb25zdCBBaSA9IG91dFtBICsgMV07CgkJICAgICAgICBjb25zdCBCciA9IG91dFtCXTsKCQkgICAgICAgIGNvbnN0IEJpID0gb3V0W0IgKyAxXTsKCQkgICAgICAgIGNvbnN0IENyID0gb3V0W0NdOwoJCSAgICAgICAgY29uc3QgQ2kgPSBvdXRbQyArIDFdOwoJCSAgICAgICAgY29uc3QgRHIgPSBvdXRbRF07CgkJICAgICAgICBjb25zdCBEaSA9IG91dFtEICsgMV07CgoJCSAgICAgICAgLy8gTWlkZGxlIHZhbHVlcwoJCSAgICAgICAgY29uc3QgTUFyID0gQXI7CgkJICAgICAgICBjb25zdCBNQWkgPSBBaTsKCgkJICAgICAgICBjb25zdCB0YWJsZUJyID0gdGFibGVba107CgkJICAgICAgICBjb25zdCB0YWJsZUJpID0gaW52ICogdGFibGVbayArIDFdOwoJCSAgICAgICAgY29uc3QgTUJyID0gQnIgKiB0YWJsZUJyIC0gQmkgKiB0YWJsZUJpOwoJCSAgICAgICAgY29uc3QgTUJpID0gQnIgKiB0YWJsZUJpICsgQmkgKiB0YWJsZUJyOwoKCQkgICAgICAgIGNvbnN0IHRhYmxlQ3IgPSB0YWJsZVsyICoga107CgkJICAgICAgICBjb25zdCB0YWJsZUNpID0gaW52ICogdGFibGVbMiAqIGsgKyAxXTsKCQkgICAgICAgIGNvbnN0IE1DciA9IENyICogdGFibGVDciAtIENpICogdGFibGVDaTsKCQkgICAgICAgIGNvbnN0IE1DaSA9IENyICogdGFibGVDaSArIENpICogdGFibGVDcjsKCgkJICAgICAgICBjb25zdCB0YWJsZURyID0gdGFibGVbMyAqIGtdOwoJCSAgICAgICAgY29uc3QgdGFibGVEaSA9IGludiAqIHRhYmxlWzMgKiBrICsgMV07CgkJICAgICAgICBjb25zdCBNRHIgPSBEciAqIHRhYmxlRHIgLSBEaSAqIHRhYmxlRGk7CgkJICAgICAgICBjb25zdCBNRGkgPSBEciAqIHRhYmxlRGkgKyBEaSAqIHRhYmxlRHI7CgoJCSAgICAgICAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgICAgICAgY29uc3QgVDByID0gTUFyICsgTUNyOwoJCSAgICAgICAgY29uc3QgVDBpID0gTUFpICsgTUNpOwoJCSAgICAgICAgY29uc3QgVDFyID0gTUFyIC0gTUNyOwoJCSAgICAgICAgY29uc3QgVDFpID0gTUFpIC0gTUNpOwoJCSAgICAgICAgY29uc3QgVDJyID0gTUJyICsgTURyOwoJCSAgICAgICAgY29uc3QgVDJpID0gTUJpICsgTURpOwoJCSAgICAgICAgY29uc3QgVDNyID0gaW52ICogKE1CciAtIE1Ecik7CgkJICAgICAgICBjb25zdCBUM2kgPSBpbnYgKiAoTUJpIC0gTURpKTsKCgkJICAgICAgICAvLyBGaW5hbCB2YWx1ZXMKCQkgICAgICAgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCQkgICAgICAgIGNvbnN0IEZBaSA9IFQwaSArIFQyaTsKCgkJICAgICAgICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgkJICAgICAgICBjb25zdCBGQ2kgPSBUMGkgLSBUMmk7CgoJCSAgICAgICAgY29uc3QgRkJyID0gVDFyICsgVDNpOwoJCSAgICAgICAgY29uc3QgRkJpID0gVDFpIC0gVDNyOwoKCQkgICAgICAgIGNvbnN0IEZEciA9IFQxciAtIFQzaTsKCQkgICAgICAgIGNvbnN0IEZEaSA9IFQxaSArIFQzcjsKCgkJICAgICAgICBvdXRbQV0gPSBGQXI7CgkJICAgICAgICBvdXRbQSArIDFdID0gRkFpOwoJCSAgICAgICAgb3V0W0JdID0gRkJyOwoJCSAgICAgICAgb3V0W0IgKyAxXSA9IEZCaTsKCQkgICAgICAgIG91dFtDXSA9IEZDcjsKCQkgICAgICAgIG91dFtDICsgMV0gPSBGQ2k7CgkJICAgICAgICBvdXRbRF0gPSBGRHI7CgkJICAgICAgICBvdXRbRCArIDFdID0gRkRpOwoJCSAgICAgIH0KCQkgICAgfQoJCSAgfQoJCX07CgoJCS8vIHJhZGl4LTIgaW1wbGVtZW50YXRpb24KCQkvLwoJCS8vIE5PVEU6IE9ubHkgY2FsbGVkIGZvciBsZW49NAoJCUZGVC5wcm90b3R5cGUuX3NpbmdsZVRyYW5zZm9ybTIgPSBmdW5jdGlvbiBfc2luZ2xlVHJhbnNmb3JtMihvdXRPZmYsIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcCkgewoJCSAgY29uc3Qgb3V0ID0gdGhpcy5fb3V0OwoJCSAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGE7CgoJCSAgY29uc3QgZXZlblIgPSBkYXRhW29mZl07CgkJICBjb25zdCBldmVuSSA9IGRhdGFbb2ZmICsgMV07CgkJICBjb25zdCBvZGRSID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IG9kZEkgPSBkYXRhW29mZiArIHN0ZXAgKyAxXTsKCgkJICBjb25zdCBsZWZ0UiA9IGV2ZW5SICsgb2RkUjsKCQkgIGNvbnN0IGxlZnRJID0gZXZlbkkgKyBvZGRJOwoJCSAgY29uc3QgcmlnaHRSID0gZXZlblIgLSBvZGRSOwoJCSAgY29uc3QgcmlnaHRJID0gZXZlbkkgLSBvZGRJOwoKCQkgIG91dFtvdXRPZmZdID0gbGVmdFI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSBsZWZ0STsKCQkgIG91dFtvdXRPZmYgKyAyXSA9IHJpZ2h0UjsKCQkgIG91dFtvdXRPZmYgKyAzXSA9IHJpZ2h0STsKCQl9OwoKCQkvLyByYWRpeC00CgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTgKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3NpbmdsZVRyYW5zZm9ybTQob3V0T2ZmLCBvZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoJCSAgY29uc3QgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgY29uc3Qgc3RlcDIgPSBzdGVwICogMjsKCQkgIGNvbnN0IHN0ZXAzID0gc3RlcCAqIDM7CgoJCSAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICBjb25zdCBBciA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IEFpID0gZGF0YVtvZmYgKyAxXTsKCQkgIGNvbnN0IEJyID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IEJpID0gZGF0YVtvZmYgKyBzdGVwICsgMV07CgkJICBjb25zdCBDciA9IGRhdGFbb2ZmICsgc3RlcDJdOwoJCSAgY29uc3QgQ2kgPSBkYXRhW29mZiArIHN0ZXAyICsgMV07CgkJICBjb25zdCBEciA9IGRhdGFbb2ZmICsgc3RlcDNdOwoJCSAgY29uc3QgRGkgPSBkYXRhW29mZiArIHN0ZXAzICsgMV07CgoJCSAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgY29uc3QgVDByID0gQXIgKyBDcjsKCQkgIGNvbnN0IFQwaSA9IEFpICsgQ2k7CgkJICBjb25zdCBUMXIgPSBBciAtIENyOwoJCSAgY29uc3QgVDFpID0gQWkgLSBDaTsKCQkgIGNvbnN0IFQyciA9IEJyICsgRHI7CgkJICBjb25zdCBUMmkgPSBCaSArIERpOwoJCSAgY29uc3QgVDNyID0gaW52ICogKEJyIC0gRHIpOwoJCSAgY29uc3QgVDNpID0gaW52ICogKEJpIC0gRGkpOwoKCQkgIC8vIEZpbmFsIHZhbHVlcwoJCSAgY29uc3QgRkFyID0gVDByICsgVDJyOwoJCSAgY29uc3QgRkFpID0gVDBpICsgVDJpOwoKCQkgIGNvbnN0IEZCciA9IFQxciArIFQzaTsKCQkgIGNvbnN0IEZCaSA9IFQxaSAtIFQzcjsKCgkJICBjb25zdCBGQ3IgPSBUMHIgLSBUMnI7CgkJICBjb25zdCBGQ2kgPSBUMGkgLSBUMmk7CgoJCSAgY29uc3QgRkRyID0gVDFyIC0gVDNpOwoJCSAgY29uc3QgRkRpID0gVDFpICsgVDNyOwoKCQkgIG91dFtvdXRPZmZdID0gRkFyOwoJCSAgb3V0W291dE9mZiArIDFdID0gRkFpOwoJCSAgb3V0W291dE9mZiArIDJdID0gRkJyOwoJCSAgb3V0W291dE9mZiArIDNdID0gRkJpOwoJCSAgb3V0W291dE9mZiArIDRdID0gRkNyOwoJCSAgb3V0W291dE9mZiArIDVdID0gRkNpOwoJCSAgb3V0W291dE9mZiArIDZdID0gRkRyOwoJCSAgb3V0W291dE9mZiArIDddID0gRkRpOwoJCX07CgoJCS8vIFJlYWwgaW5wdXQgcmFkaXgtNCBpbXBsZW1lbnRhdGlvbgoJCUZGVC5wcm90b3R5cGUuX3JlYWxUcmFuc2Zvcm00ID0gZnVuY3Rpb24gX3JlYWxUcmFuc2Zvcm00KCkgewoJCSAgdmFyIG91dCA9IHRoaXMuX291dDsKCQkgIHZhciBzaXplID0gdGhpcy5fY3NpemU7CgoJCSAgLy8gSW5pdGlhbCBzdGVwIChwZXJtdXRlIGFuZCB0cmFuc2Zvcm0pCgkJICB2YXIgd2lkdGggPSB0aGlzLl93aWR0aDsKCQkgIHZhciBzdGVwID0gMSA8PCB3aWR0aDsKCQkgIHZhciBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgoJCSAgdmFyIG91dE9mZjsKCQkgIHZhciB0OwoJCSAgdmFyIGJpdHJldiA9IHRoaXMuX2JpdHJldjsKCQkgIGlmIChsZW4gPT09IDQpIHsKCQkgICAgZm9yIChvdXRPZmYgPSAwLCB0ID0gMDsgb3V0T2ZmIDwgc2l6ZTsgb3V0T2ZmICs9IGxlbiwgdCsrKSB7CgkJICAgICAgY29uc3Qgb2ZmID0gYml0cmV2W3RdOwoJCSAgICAgIHRoaXMuX3NpbmdsZVJlYWxUcmFuc2Zvcm0yKG91dE9mZiwgb2ZmID4+PiAxLCBzdGVwID4+PiAxKTsKCQkgICAgfQoJCSAgfSBlbHNlIHsKCQkgICAgLy8gbGVuID09PSA4CgkJICAgIGZvciAob3V0T2ZmID0gMCwgdCA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4sIHQrKykgewoJCSAgICAgIGNvbnN0IG9mZiA9IGJpdHJldlt0XTsKCQkgICAgICB0aGlzLl9zaW5nbGVSZWFsVHJhbnNmb3JtNChvdXRPZmYsIG9mZiA+Pj4gMSwgc3RlcCA+Pj4gMSk7CgkJICAgIH0KCQkgIH0KCgkJICAvLyBMb29wIHRocm91Z2ggc3RlcHMgaW4gZGVjcmVhc2luZyBvcmRlcgoJCSAgdmFyIGludiA9IHRoaXMuX2ludiA/IC0xIDogMTsKCQkgIHZhciB0YWJsZSA9IHRoaXMudGFibGU7CgkJICBmb3IgKHN0ZXAgPj49IDI7IHN0ZXAgPj0gMjsgc3RlcCA+Pj0gMikgewoJCSAgICBsZW4gPSAoc2l6ZSAvIHN0ZXApIDw8IDE7CgkJICAgIHZhciBoYWxmTGVuID0gbGVuID4+PiAxOwoJCSAgICB2YXIgcXVhcnRlckxlbiA9IGhhbGZMZW4gPj4+IDE7CgkJICAgIHZhciBocXVhcnRlckxlbiA9IHF1YXJ0ZXJMZW4gPj4+IDE7CgoJCSAgICAvLyBMb29wIHRocm91Z2ggb2Zmc2V0cyBpbiB0aGUgZGF0YQoJCSAgICBmb3IgKG91dE9mZiA9IDA7IG91dE9mZiA8IHNpemU7IG91dE9mZiArPSBsZW4pIHsKCQkgICAgICBmb3IgKHZhciBpID0gMCwgayA9IDA7IGkgPD0gaHF1YXJ0ZXJMZW47IGkgKz0gMiwgayArPSBzdGVwKSB7CgkJICAgICAgICB2YXIgQSA9IG91dE9mZiArIGk7CgkJICAgICAgICB2YXIgQiA9IEEgKyBxdWFydGVyTGVuOwoJCSAgICAgICAgdmFyIEMgPSBCICsgcXVhcnRlckxlbjsKCQkgICAgICAgIHZhciBEID0gQyArIHF1YXJ0ZXJMZW47CgoJCSAgICAgICAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICAgICAgICB2YXIgQXIgPSBvdXRbQV07CgkJICAgICAgICB2YXIgQWkgPSBvdXRbQSArIDFdOwoJCSAgICAgICAgdmFyIEJyID0gb3V0W0JdOwoJCSAgICAgICAgdmFyIEJpID0gb3V0W0IgKyAxXTsKCQkgICAgICAgIHZhciBDciA9IG91dFtDXTsKCQkgICAgICAgIHZhciBDaSA9IG91dFtDICsgMV07CgkJICAgICAgICB2YXIgRHIgPSBvdXRbRF07CgkJICAgICAgICB2YXIgRGkgPSBvdXRbRCArIDFdOwoKCQkgICAgICAgIC8vIE1pZGRsZSB2YWx1ZXMKCQkgICAgICAgIHZhciBNQXIgPSBBcjsKCQkgICAgICAgIHZhciBNQWkgPSBBaTsKCgkJICAgICAgICB2YXIgdGFibGVCciA9IHRhYmxlW2tdOwoJCSAgICAgICAgdmFyIHRhYmxlQmkgPSBpbnYgKiB0YWJsZVtrICsgMV07CgkJICAgICAgICB2YXIgTUJyID0gQnIgKiB0YWJsZUJyIC0gQmkgKiB0YWJsZUJpOwoJCSAgICAgICAgdmFyIE1CaSA9IEJyICogdGFibGVCaSArIEJpICogdGFibGVCcjsKCgkJICAgICAgICB2YXIgdGFibGVDciA9IHRhYmxlWzIgKiBrXTsKCQkgICAgICAgIHZhciB0YWJsZUNpID0gaW52ICogdGFibGVbMiAqIGsgKyAxXTsKCQkgICAgICAgIHZhciBNQ3IgPSBDciAqIHRhYmxlQ3IgLSBDaSAqIHRhYmxlQ2k7CgkJICAgICAgICB2YXIgTUNpID0gQ3IgKiB0YWJsZUNpICsgQ2kgKiB0YWJsZUNyOwoKCQkgICAgICAgIHZhciB0YWJsZURyID0gdGFibGVbMyAqIGtdOwoJCSAgICAgICAgdmFyIHRhYmxlRGkgPSBpbnYgKiB0YWJsZVszICogayArIDFdOwoJCSAgICAgICAgdmFyIE1EciA9IERyICogdGFibGVEciAtIERpICogdGFibGVEaTsKCQkgICAgICAgIHZhciBNRGkgPSBEciAqIHRhYmxlRGkgKyBEaSAqIHRhYmxlRHI7CgoJCSAgICAgICAgLy8gUHJlLUZpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIFQwciA9IE1BciArIE1DcjsKCQkgICAgICAgIHZhciBUMGkgPSBNQWkgKyBNQ2k7CgkJICAgICAgICB2YXIgVDFyID0gTUFyIC0gTUNyOwoJCSAgICAgICAgdmFyIFQxaSA9IE1BaSAtIE1DaTsKCQkgICAgICAgIHZhciBUMnIgPSBNQnIgKyBNRHI7CgkJICAgICAgICB2YXIgVDJpID0gTUJpICsgTURpOwoJCSAgICAgICAgdmFyIFQzciA9IGludiAqIChNQnIgLSBNRHIpOwoJCSAgICAgICAgdmFyIFQzaSA9IGludiAqIChNQmkgLSBNRGkpOwoKCQkgICAgICAgIC8vIEZpbmFsIHZhbHVlcwoJCSAgICAgICAgdmFyIEZBciA9IFQwciArIFQycjsKCQkgICAgICAgIHZhciBGQWkgPSBUMGkgKyBUMmk7CgoJCSAgICAgICAgdmFyIEZCciA9IFQxciArIFQzaTsKCQkgICAgICAgIHZhciBGQmkgPSBUMWkgLSBUM3I7CgoJCSAgICAgICAgb3V0W0FdID0gRkFyOwoJCSAgICAgICAgb3V0W0EgKyAxXSA9IEZBaTsKCQkgICAgICAgIG91dFtCXSA9IEZCcjsKCQkgICAgICAgIG91dFtCICsgMV0gPSBGQmk7CgoJCSAgICAgICAgLy8gT3V0cHV0IGZpbmFsIG1pZGRsZSBwb2ludAoJCSAgICAgICAgaWYgKGkgPT09IDApIHsKCQkgICAgICAgICAgdmFyIEZDciA9IFQwciAtIFQycjsKCQkgICAgICAgICAgdmFyIEZDaSA9IFQwaSAtIFQyaTsKCQkgICAgICAgICAgb3V0W0NdID0gRkNyOwoJCSAgICAgICAgICBvdXRbQyArIDFdID0gRkNpOwoJCSAgICAgICAgICBjb250aW51ZTsKCQkgICAgICAgIH0KCgkJICAgICAgICAvLyBEbyBub3Qgb3ZlcndyaXRlIG91cnNlbHZlcwoJCSAgICAgICAgaWYgKGkgPT09IGhxdWFydGVyTGVuKQoJCSAgICAgICAgICBjb250aW51ZTsKCgkJICAgICAgICAvLyBJbiB0aGUgZmxpcHBlZCBjYXNlOgoJCSAgICAgICAgLy8gTUFpID0gLU1BaQoJCSAgICAgICAgLy8gTUJyPS1NQmksIE1CaT0tTUJyCgkJICAgICAgICAvLyBNQ3I9LU1DcgoJCSAgICAgICAgLy8gTURyPU1EaSwgTURpPU1EcgoJCSAgICAgICAgdmFyIFNUMHIgPSBUMXI7CgkJICAgICAgICB2YXIgU1QwaSA9IC1UMWk7CgkJICAgICAgICB2YXIgU1QxciA9IFQwcjsKCQkgICAgICAgIHZhciBTVDFpID0gLVQwaTsKCQkgICAgICAgIHZhciBTVDJyID0gLWludiAqIFQzaTsKCQkgICAgICAgIHZhciBTVDJpID0gLWludiAqIFQzcjsKCQkgICAgICAgIHZhciBTVDNyID0gLWludiAqIFQyaTsKCQkgICAgICAgIHZhciBTVDNpID0gLWludiAqIFQycjsKCgkJICAgICAgICB2YXIgU0ZBciA9IFNUMHIgKyBTVDJyOwoJCSAgICAgICAgdmFyIFNGQWkgPSBTVDBpICsgU1QyaTsKCgkJICAgICAgICB2YXIgU0ZCciA9IFNUMXIgKyBTVDNpOwoJCSAgICAgICAgdmFyIFNGQmkgPSBTVDFpIC0gU1QzcjsKCgkJICAgICAgICB2YXIgU0EgPSBvdXRPZmYgKyBxdWFydGVyTGVuIC0gaTsKCQkgICAgICAgIHZhciBTQiA9IG91dE9mZiArIGhhbGZMZW4gLSBpOwoKCQkgICAgICAgIG91dFtTQV0gPSBTRkFyOwoJCSAgICAgICAgb3V0W1NBICsgMV0gPSBTRkFpOwoJCSAgICAgICAgb3V0W1NCXSA9IFNGQnI7CgkJICAgICAgICBvdXRbU0IgKyAxXSA9IFNGQmk7CgkJICAgICAgfQoJCSAgICB9CgkJICB9CgkJfTsKCgkJLy8gcmFkaXgtMiBpbXBsZW1lbnRhdGlvbgoJCS8vCgkJLy8gTk9URTogT25seSBjYWxsZWQgZm9yIGxlbj00CgkJRkZULnByb3RvdHlwZS5fc2luZ2xlUmVhbFRyYW5zZm9ybTIgPSBmdW5jdGlvbiBfc2luZ2xlUmVhbFRyYW5zZm9ybTIob3V0T2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9mZiwKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwKSB7CgkJICBjb25zdCBvdXQgPSB0aGlzLl9vdXQ7CgkJICBjb25zdCBkYXRhID0gdGhpcy5fZGF0YTsKCgkJICBjb25zdCBldmVuUiA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IG9kZFIgPSBkYXRhW29mZiArIHN0ZXBdOwoKCQkgIGNvbnN0IGxlZnRSID0gZXZlblIgKyBvZGRSOwoJCSAgY29uc3QgcmlnaHRSID0gZXZlblIgLSBvZGRSOwoKCQkgIG91dFtvdXRPZmZdID0gbGVmdFI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDJdID0gcmlnaHRSOwoJCSAgb3V0W291dE9mZiArIDNdID0gMDsKCQl9OwoKCQkvLyByYWRpeC00CgkJLy8KCQkvLyBOT1RFOiBPbmx5IGNhbGxlZCBmb3IgbGVuPTgKCQlGRlQucHJvdG90eXBlLl9zaW5nbGVSZWFsVHJhbnNmb3JtNCA9IGZ1bmN0aW9uIF9zaW5nbGVSZWFsVHJhbnNmb3JtNChvdXRPZmYsCgkJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2ZmLAoJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXApIHsKCQkgIGNvbnN0IG91dCA9IHRoaXMuX291dDsKCQkgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhOwoJCSAgY29uc3QgaW52ID0gdGhpcy5faW52ID8gLTEgOiAxOwoJCSAgY29uc3Qgc3RlcDIgPSBzdGVwICogMjsKCQkgIGNvbnN0IHN0ZXAzID0gc3RlcCAqIDM7CgoJCSAgLy8gT3JpZ2luYWwgdmFsdWVzCgkJICBjb25zdCBBciA9IGRhdGFbb2ZmXTsKCQkgIGNvbnN0IEJyID0gZGF0YVtvZmYgKyBzdGVwXTsKCQkgIGNvbnN0IENyID0gZGF0YVtvZmYgKyBzdGVwMl07CgkJICBjb25zdCBEciA9IGRhdGFbb2ZmICsgc3RlcDNdOwoKCQkgIC8vIFByZS1GaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IFQwciA9IEFyICsgQ3I7CgkJICBjb25zdCBUMXIgPSBBciAtIENyOwoJCSAgY29uc3QgVDJyID0gQnIgKyBEcjsKCQkgIGNvbnN0IFQzciA9IGludiAqIChCciAtIERyKTsKCgkJICAvLyBGaW5hbCB2YWx1ZXMKCQkgIGNvbnN0IEZBciA9IFQwciArIFQycjsKCgkJICBjb25zdCBGQnIgPSBUMXI7CgkJICBjb25zdCBGQmkgPSAtVDNyOwoKCQkgIGNvbnN0IEZDciA9IFQwciAtIFQycjsKCgkJICBjb25zdCBGRHIgPSBUMXI7CgkJICBjb25zdCBGRGkgPSBUM3I7CgoJCSAgb3V0W291dE9mZl0gPSBGQXI7CgkJICBvdXRbb3V0T2ZmICsgMV0gPSAwOwoJCSAgb3V0W291dE9mZiArIDJdID0gRkJyOwoJCSAgb3V0W291dE9mZiArIDNdID0gRkJpOwoJCSAgb3V0W291dE9mZiArIDRdID0gRkNyOwoJCSAgb3V0W291dE9mZiArIDVdID0gMDsKCQkgIG91dFtvdXRPZmYgKyA2XSA9IEZEcjsKCQkgIG91dFtvdXRPZmYgKyA3XSA9IEZEaTsKCQl9OwoJCXJldHVybiBmZnQ7Cgl9CgoJdmFyIGZmdEV4cG9ydHMgPSByZXF1aXJlRmZ0KCk7Cgl2YXIgRkZUID0gLypAX19QVVJFX18qL2dldERlZmF1bHRFeHBvcnRGcm9tQ2pzKGZmdEV4cG9ydHMpOwoKCWNvbnN0IFdFQkFVRElPX0JMT0NLX1NJWkUgPSAxMjg7Cgljb25zdCBERUZBVUxUX0JMT0NLX1NJWkUgPSAxMDI0OyAvLyBEZWZhdWx0IGJsb2NrIHNpemUgaWYgbm90IHByb3ZpZGVkIGluIG9wdGlvbnMKCS8qKiBPdmVybGFwLUFkZCBOb2RlICovCgljbGFzcyBPTEFQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewoJICAgIG5iSW5wdXRzOwoJICAgIG5iT3V0cHV0czsKCSAgICBibG9ja1NpemU7CgkgICAgaG9wU2l6ZTsKCSAgICBuYk92ZXJsYXBzOwoJICAgIGlucHV0QnVmZmVycyA9IFtdOwoJICAgIGlucHV0QnVmZmVyc0hlYWQgPSBbXTsKCSAgICBpbnB1dEJ1ZmZlcnNUb1NlbmQgPSBbXTsKCSAgICBvdXRwdXRCdWZmZXJzID0gW107CgkgICAgb3V0cHV0QnVmZmVyc1RvUmV0cmlldmUgPSBbXTsKCSAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CgkgICAgICAgIHN1cGVyKG9wdGlvbnMpOwoJICAgICAgICB0aGlzLm5iSW5wdXRzID0gb3B0aW9ucz8ubnVtYmVyT2ZJbnB1dHMgfHwgMTsKCSAgICAgICAgdGhpcy5uYk91dHB1dHMgPSBvcHRpb25zPy5udW1iZXJPZk91dHB1dHMgfHwgMTsKCSAgICAgICAgLy8gcHJvY2Vzc29yT3B0aW9ucyBpcyB0eXBlZCBgdW5rbm93bmAgYXQgdGhlIGFtYmllbnQgYm91bmRhcnk7IG5hcnJvdyBoZXJlLgoJICAgICAgICBjb25zdCBwcm9jT3B0cyA9IChvcHRpb25zPy5wcm9jZXNzb3JPcHRpb25zID8/IHt9KTsKCSAgICAgICAgdGhpcy5ibG9ja1NpemUgPSBwcm9jT3B0cy5ibG9ja1NpemUgfHwgREVGQVVMVF9CTE9DS19TSVpFOwoJICAgICAgICB0aGlzLmhvcFNpemUgPSBXRUJBVURJT19CTE9DS19TSVpFOwoJICAgICAgICB0aGlzLm5iT3ZlcmxhcHMgPSBNYXRoLmZsb29yKHRoaXMuYmxvY2tTaXplIC8gdGhpcy5ob3BTaXplKTsKCSAgICAgICAgdGhpcy5pbml0aWFsaXplQnVmZmVycygpOwoJICAgIH0KCSAgICBpbml0aWFsaXplQnVmZmVycygpIHsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnMgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZCA9IG5ldyBBcnJheSh0aGlzLm5iSW5wdXRzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmQgPSBuZXcgQXJyYXkodGhpcy5uYklucHV0cyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVycyA9IG5ldyBBcnJheSh0aGlzLm5iT3V0cHV0cyk7CgkgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1RvUmV0cmlldmUgPSBuZXcgQXJyYXkodGhpcy5uYk91dHB1dHMpOwoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaSwgMSk7CgkgICAgICAgIH0KCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMoaSwgMSk7CgkgICAgICAgIH0KCSAgICB9CgkgICAgYWxsb2NhdGVJbnB1dENoYW5uZWxzKGlucHV0SW5kZXgsIG5iQ2hhbm5lbHMpIHsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzSGVhZFtpbnB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNUb1NlbmRbaW5wdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJDaGFubmVsczsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUgKyBXRUJBVURJT19CTE9DS19TSVpFKTsKCSAgICAgICAgICAgIHRoaXMuaW5wdXRCdWZmZXJzW2lucHV0SW5kZXhdW2ldLmZpbGwoMCk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc0hlYWRbaW5wdXRJbmRleF1baV0gPSB0aGlzLmlucHV0QnVmZmVyc1tpbnB1dEluZGV4XVtpXS5zdWJhcnJheSgwLCB0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpbnB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB9CgkgICAgfQoJICAgIGFsbG9jYXRlT3V0cHV0Q2hhbm5lbHMob3V0cHV0SW5kZXgsIG5iQ2hhbm5lbHMpIHsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzW291dHB1dEluZGV4XSA9IG5ldyBBcnJheShuYkNoYW5uZWxzKTsKCSAgICAgICAgdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtvdXRwdXRJbmRleF0gPSBuZXcgQXJyYXkobmJDaGFubmVscyk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJDaGFubmVsczsgaSsrKSB7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdW2ldID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbb3V0cHV0SW5kZXhdW2ldLmZpbGwoMCk7CgkgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlW291dHB1dEluZGV4XVtpXSA9IG5ldyBGbG9hdDMyQXJyYXkodGhpcy5ibG9ja1NpemUpOwoJICAgICAgICB9CgkgICAgfQoJICAgIHJlYWxsb2NhdGVDaGFubmVsc0lmTmVlZGVkKGlucHV0cywgb3V0cHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgY29uc3QgbmJDaGFubmVscyA9IGlucHV0c1tpXS5sZW5ndGg7CgkgICAgICAgICAgICBpZiAobmJDaGFubmVscyAhPT0gdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoKSB7CgkgICAgICAgICAgICAgICAgdGhpcy5hbGxvY2F0ZUlucHV0Q2hhbm5lbHMoaSwgbmJDaGFubmVscyk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBjb25zdCBuYkNoYW5uZWxzID0gb3V0cHV0c1tpXS5sZW5ndGg7CgkgICAgICAgICAgICBpZiAobmJDaGFubmVscyAhPT0gdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aCkgewoJICAgICAgICAgICAgICAgIHRoaXMuYWxsb2NhdGVPdXRwdXRDaGFubmVscyhpLCBuYkNoYW5uZWxzKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICByZWFkSW5wdXRzKGlucHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJJbnB1dHM7IGkrKykgewoJICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGNvbnN0IHdlYkF1ZGlvQmxvY2sgPSBpbnB1dHNbaV1bal07CgkgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEJ1ZmZlcnNbaV1bal0uc2V0KHdlYkF1ZGlvQmxvY2ssIHRoaXMuYmxvY2tTaXplKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICB3cml0ZU91dHB1dHMob3V0cHV0cykgewoJICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubmJPdXRwdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5vdXRwdXRCdWZmZXJzW2ldLmxlbmd0aDsgaisrKSB7CgkgICAgICAgICAgICAgICAgY29uc3Qgd2ViQXVkaW9CbG9jayA9IG91dHB1dHNbaV1bal07CgkgICAgICAgICAgICAgICAgd2ViQXVkaW9CbG9jay5zZXQodGhpcy5vdXRwdXRCdWZmZXJzW2ldW2pdLnN1YmFycmF5KDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBzaGlmdElucHV0QnVmZmVycygpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1tpXVtqXS5jb3B5V2l0aGluKDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgfQoJICAgIHNoaWZ0T3V0cHV0QnVmZmVycygpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5jb3B5V2l0aGluKDAsIFdFQkFVRElPX0JMT0NLX1NJWkUpOwoJICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QnVmZmVyc1tpXVtqXS5maWxsKDAsIHRoaXMuYmxvY2tTaXplIC0gV0VCQVVESU9fQkxPQ0tfU0laRSk7CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcHJlcGFyZUlucHV0QnVmZmVyc1RvU2VuZCgpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iSW5wdXRzOyBpKyspIHsKCSAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5pbnB1dEJ1ZmZlcnNbaV0ubGVuZ3RoOyBqKyspIHsKCSAgICAgICAgICAgICAgICB0aGlzLmlucHV0QnVmZmVyc1RvU2VuZFtpXVtqXS5zZXQodGhpcy5pbnB1dEJ1ZmZlcnNIZWFkW2ldW2pdKTsKCSAgICAgICAgICAgIH0KCSAgICAgICAgfQoJICAgIH0KCSAgICBoYW5kbGVPdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSgpIHsKCSAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5iT3V0cHV0czsgaSsrKSB7CgkgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMub3V0cHV0QnVmZmVyc1tpXS5sZW5ndGg7IGorKykgewoJICAgICAgICAgICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgdGhpcy5ibG9ja1NpemU7IGsrKykgewoJICAgICAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJ1ZmZlcnNbaV1bal1ba10gKz0gdGhpcy5vdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZVtpXVtqXVtrXSAvIHRoaXMubmJPdmVybGFwczsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICB9CgkgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKCSAgICAgICAgdGhpcy5yZWFsbG9jYXRlQ2hhbm5lbHNJZk5lZWRlZChpbnB1dHMsIG91dHB1dHMpOwoJICAgICAgICB0aGlzLnJlYWRJbnB1dHMoaW5wdXRzKTsKCSAgICAgICAgdGhpcy5zaGlmdElucHV0QnVmZmVycygpOwoJICAgICAgICB0aGlzLnByZXBhcmVJbnB1dEJ1ZmZlcnNUb1NlbmQoKTsKCSAgICAgICAgdGhpcy5wcm9jZXNzT0xBKHRoaXMuaW5wdXRCdWZmZXJzVG9TZW5kLCB0aGlzLm91dHB1dEJ1ZmZlcnNUb1JldHJpZXZlLCBwYXJhbWV0ZXJzKTsKCSAgICAgICAgdGhpcy5oYW5kbGVPdXRwdXRCdWZmZXJzVG9SZXRyaWV2ZSgpOwoJICAgICAgICB0aGlzLndyaXRlT3V0cHV0cyhvdXRwdXRzKTsKCSAgICAgICAgdGhpcy5zaGlmdE91dHB1dEJ1ZmZlcnMoKTsKCSAgICAgICAgcmV0dXJuIHRydWU7CgkgICAgfQoJfQoKCWNvbnN0IHdyYXBQaGFzZSA9IChwaGFzZSkgPT4gTWF0aC5hdGFuMihNYXRoLnNpbihwaGFzZSksIE1hdGguY29zKHBoYXNlKSk7CgkvKiogU3RhdGVmdWwgc3BlY3RyYWwtZnJhbWUgZnJlZXplciB3aXRoIHBoYXNlIGNvbnRpbnVhdGlvbiBhbmQgMy1mcmFtZSBtYWduaXR1ZGUgc21lYXIuICovCgljbGFzcyBTcGVjdHJhbEZyZWV6ZVN0YXRlIHsKCSAgICBiaW5Db3VudDsKCSAgICBmZnRTaXplOwoJICAgIGhvcFNpemU7CgkgICAgcHJldmlvdXM7CgkgICAgaGVsZE1hZ25pdHVkZTsKCSAgICBoZWxkUGhhc2U7CgkgICAgcGhhc2VTdGVwOwoJICAgIG1hZ25pdHVkZUhpc3Rvcnk7CgkgICAgaGlzdG9yeUluZGV4ID0gMDsKCSAgICBoaXN0b3J5Q291bnQgPSAwOwoJICAgIHdhc0Zyb3plbiA9IGZhbHNlOwoJICAgIGhhc1ByZXZpb3VzID0gZmFsc2U7CgkgICAgY29uc3RydWN0b3IoYmluQ291bnQsIGZmdFNpemUsIGhvcFNpemUpIHsKCSAgICAgICAgdGhpcy5iaW5Db3VudCA9IGJpbkNvdW50OwoJICAgICAgICB0aGlzLmZmdFNpemUgPSBmZnRTaXplOwoJICAgICAgICB0aGlzLmhvcFNpemUgPSBob3BTaXplOwoJICAgICAgICB0aGlzLnByZXZpb3VzID0gbmV3IEZsb2F0MzJBcnJheShmZnRTaXplICogMik7CgkgICAgICAgIHRoaXMuaGVsZE1hZ25pdHVkZSA9IG5ldyBGbG9hdDMyQXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLmhlbGRQaGFzZSA9IG5ldyBGbG9hdDY0QXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLnBoYXNlU3RlcCA9IG5ldyBGbG9hdDY0QXJyYXkoYmluQ291bnQpOwoJICAgICAgICB0aGlzLm1hZ25pdHVkZUhpc3RvcnkgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiAzIH0sICgpID0+IG5ldyBGbG9hdDMyQXJyYXkoYmluQ291bnQpKTsKCSAgICB9CgkgICAgcHJvY2VzcyhpbnB1dCwgb3V0cHV0LCBmcm96ZW4sIHNtZWFyLCBtaXgpIHsKCSAgICAgICAgY29uc3QgY3VycmVudE1hZ25pdHVkZXMgPSB0aGlzLm1hZ25pdHVkZUhpc3RvcnlbdGhpcy5oaXN0b3J5SW5kZXhdOwoJICAgICAgICBmb3IgKGxldCBiaW4gPSAwOyBiaW4gPCB0aGlzLmJpbkNvdW50OyBiaW4rKykgewoJICAgICAgICAgICAgY29uc3QgaSA9IGJpbiAqIDI7CgkgICAgICAgICAgICBjdXJyZW50TWFnbml0dWRlc1tiaW5dID0gTWF0aC5oeXBvdChpbnB1dFtpXSwgaW5wdXRbaSArIDFdKTsKCSAgICAgICAgfQoJICAgICAgICB0aGlzLmhpc3RvcnlJbmRleCA9ICh0aGlzLmhpc3RvcnlJbmRleCArIDEpICUgdGhpcy5tYWduaXR1ZGVIaXN0b3J5Lmxlbmd0aDsKCSAgICAgICAgdGhpcy5oaXN0b3J5Q291bnQgPSBNYXRoLm1pbih0aGlzLm1hZ25pdHVkZUhpc3RvcnkubGVuZ3RoLCB0aGlzLmhpc3RvcnlDb3VudCArIDEpOwoJICAgICAgICBpZiAoZnJvemVuICYmICF0aGlzLndhc0Zyb3plbikgewoJICAgICAgICAgICAgY29uc3Qgc21lYXJBbW91bnQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBzbWVhcikpOwoJICAgICAgICAgICAgZm9yIChsZXQgYmluID0gMDsgYmluIDwgdGhpcy5iaW5Db3VudDsgYmluKyspIHsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICBsZXQgYXZlcmFnZSA9IDA7CgkgICAgICAgICAgICAgICAgZm9yIChsZXQgaCA9IDA7IGggPCB0aGlzLmhpc3RvcnlDb3VudDsgaCsrKQoJICAgICAgICAgICAgICAgICAgICBhdmVyYWdlICs9IHRoaXMubWFnbml0dWRlSGlzdG9yeVtoXVtiaW5dOwoJICAgICAgICAgICAgICAgIGF2ZXJhZ2UgLz0gdGhpcy5oaXN0b3J5Q291bnQ7CgkgICAgICAgICAgICAgICAgdGhpcy5oZWxkTWFnbml0dWRlW2Jpbl0gPSBjdXJyZW50TWFnbml0dWRlc1tiaW5dICogKDEgLSBzbWVhckFtb3VudCkgKyBhdmVyYWdlICogc21lYXJBbW91bnQ7CgkgICAgICAgICAgICAgICAgdGhpcy5oZWxkUGhhc2VbYmluXSA9IE1hdGguYXRhbjIoaW5wdXRbaSArIDFdLCBpbnB1dFtpXSk7CgkgICAgICAgICAgICAgICAgaWYgKHRoaXMuaGFzUHJldmlvdXMpIHsKCSAgICAgICAgICAgICAgICAgICAgY29uc3QgY3Jvc3NSZWFsID0gaW5wdXRbaV0gKiB0aGlzLnByZXZpb3VzW2ldICsgaW5wdXRbaSArIDFdICogdGhpcy5wcmV2aW91c1tpICsgMV07CgkgICAgICAgICAgICAgICAgICAgIGNvbnN0IGNyb3NzSW1hZyA9IGlucHV0W2kgKyAxXSAqIHRoaXMucHJldmlvdXNbaV0gLSBpbnB1dFtpXSAqIHRoaXMucHJldmlvdXNbaSArIDFdOwoJICAgICAgICAgICAgICAgICAgICB0aGlzLnBoYXNlU3RlcFtiaW5dID0gTWF0aC5hdGFuMihjcm9zc0ltYWcsIGNyb3NzUmVhbCk7CgkgICAgICAgICAgICAgICAgfQoJICAgICAgICAgICAgICAgIGVsc2UgewoJICAgICAgICAgICAgICAgICAgICB0aGlzLnBoYXNlU3RlcFtiaW5dID0gKDIgKiBNYXRoLlBJICogYmluICogdGhpcy5ob3BTaXplKSAvIHRoaXMuZmZ0U2l6ZTsKCSAgICAgICAgICAgICAgICB9CgkgICAgICAgICAgICB9CgkgICAgICAgIH0KCSAgICAgICAgY29uc3Qgd2V0ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgbWl4KSk7CgkgICAgICAgIGlmIChmcm96ZW4pIHsKCSAgICAgICAgICAgIGZvciAobGV0IGJpbiA9IDA7IGJpbiA8IHRoaXMuYmluQ291bnQ7IGJpbisrKSB7CgkgICAgICAgICAgICAgICAgaWYgKHRoaXMud2FzRnJvemVuKQoJICAgICAgICAgICAgICAgICAgICB0aGlzLmhlbGRQaGFzZVtiaW5dID0gd3JhcFBoYXNlKHRoaXMuaGVsZFBoYXNlW2Jpbl0gKyB0aGlzLnBoYXNlU3RlcFtiaW5dKTsKCSAgICAgICAgICAgICAgICBjb25zdCBpID0gYmluICogMjsKCSAgICAgICAgICAgICAgICBjb25zdCBoZWxkUmVhbCA9IHRoaXMuaGVsZE1hZ25pdHVkZVtiaW5dICogTWF0aC5jb3ModGhpcy5oZWxkUGhhc2VbYmluXSk7CgkgICAgICAgICAgICAgICAgY29uc3QgaGVsZEltYWcgPSB0aGlzLmhlbGRNYWduaXR1ZGVbYmluXSAqIE1hdGguc2luKHRoaXMuaGVsZFBoYXNlW2Jpbl0pOwoJICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IGlucHV0W2ldICogKDEgLSB3ZXQpICsgaGVsZFJlYWwgKiB3ZXQ7CgkgICAgICAgICAgICAgICAgb3V0cHV0W2kgKyAxXSA9IGlucHV0W2kgKyAxXSAqICgxIC0gd2V0KSArIGhlbGRJbWFnICogd2V0OwoJICAgICAgICAgICAgfQoJICAgICAgICB9CgkgICAgICAgIGVsc2UgewoJICAgICAgICAgICAgb3V0cHV0LnNldChpbnB1dCk7CgkgICAgICAgIH0KCSAgICAgICAgdGhpcy5wcmV2aW91cy5zZXQoaW5wdXQpOwoJICAgICAgICB0aGlzLmhhc1ByZXZpb3VzID0gdHJ1ZTsKCSAgICAgICAgdGhpcy53YXNGcm96ZW4gPSBmcm96ZW47CgkgICAgfQoJfQoKCWNvbnN0IEJMT0NLX1NJWkUgPSAyMDQ4OwoJY2xhc3MgU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgT0xBUHJvY2Vzc29yIHsKCSAgICBmZnQ7CgkgICAgd2luZG93OwoJICAgIGlucHV0U3BlY3RydW07CgkgICAgb3V0cHV0U3BlY3RydW07CgkgICAgdGltZUNvbXBsZXg7CgkgICAgc3RhdGVzID0gW107CgkgICAgc3RhdGljIGdldCBwYXJhbWV0ZXJEZXNjcmlwdG9ycygpIHsKCSAgICAgICAgcmV0dXJuIFsKCSAgICAgICAgICAgIHsgbmFtZTogImZyZWV6ZSIsIGRlZmF1bHRWYWx1ZTogMCwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAxLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKCSAgICAgICAgICAgIHsgbmFtZTogInNtZWFyIiwgZGVmYXVsdFZhbHVlOiAwLjUsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCgkgICAgICAgICAgICB7IG5hbWU6ICJtaXgiLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCgkgICAgICAgIF07CgkgICAgfQoJICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsKCSAgICAgICAgY29uc3Qgd29ya2xldE9wdGlvbnMgPSBvcHRpb25zID8/IHt9OwoJICAgICAgICB3b3JrbGV0T3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID0geyBibG9ja1NpemU6IEJMT0NLX1NJWkUsIC4uLih3b3JrbGV0T3B0aW9ucy5wcm9jZXNzb3JPcHRpb25zID8/IHt9KSB9OwoJICAgICAgICBzdXBlcih3b3JrbGV0T3B0aW9ucyk7CgkgICAgICAgIHRoaXMuZmZ0ID0gbmV3IEZGVCh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIHRoaXMud2luZG93ID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmJsb2NrU2l6ZSk7CgkgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5ibG9ja1NpemU7IGkrKykKCSAgICAgICAgICAgIHRoaXMud2luZG93W2ldID0gMC41ICogKDEgLSBNYXRoLmNvcygoMiAqIE1hdGguUEkgKiBpKSAvIHRoaXMuYmxvY2tTaXplKSk7CgkgICAgICAgIHRoaXMuaW5wdXRTcGVjdHJ1bSA9IHRoaXMuZmZ0LmNyZWF0ZUNvbXBsZXhBcnJheSgpOwoJICAgICAgICB0aGlzLm91dHB1dFNwZWN0cnVtID0gdGhpcy5mZnQuY3JlYXRlQ29tcGxleEFycmF5KCk7CgkgICAgICAgIHRoaXMudGltZUNvbXBsZXggPSB0aGlzLmZmdC5jcmVhdGVDb21wbGV4QXJyYXkoKTsKCSAgICB9CgkgICAgcHJvY2Vzc09MQShpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKCSAgICAgICAgY29uc3QgZnJvemVuID0gcGFyYW1ldGVycy5mcmVlemVbcGFyYW1ldGVycy5mcmVlemUubGVuZ3RoIC0gMV0gPj0gMC41OwoJICAgICAgICBjb25zdCBzbWVhciA9IHBhcmFtZXRlcnMuc21lYXJbcGFyYW1ldGVycy5zbWVhci5sZW5ndGggLSAxXTsKCSAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1ldGVycy5taXhbcGFyYW1ldGVycy5taXgubGVuZ3RoIC0gMV07CgkgICAgICAgIGNvbnN0IGJpbnMgPSB0aGlzLmJsb2NrU2l6ZSAvIDIgKyAxOwoJICAgICAgICBmb3IgKGxldCBjaGFubmVsID0gMDsgY2hhbm5lbCA8IGlucHV0c1swXS5sZW5ndGg7IGNoYW5uZWwrKykgewoJICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF1bY2hhbm5lbF07CgkgICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdW2NoYW5uZWxdOwoJICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dC5sZW5ndGg7IGkrKykKCSAgICAgICAgICAgICAgICBpbnB1dFtpXSAqPSB0aGlzLndpbmRvd1tpXTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LnJlYWxUcmFuc2Zvcm0odGhpcy5pbnB1dFNwZWN0cnVtLCBpbnB1dCk7CgkgICAgICAgICAgICBjb25zdCBzdGF0ZSA9ICh0aGlzLnN0YXRlc1tjaGFubmVsXSA/Pz0gbmV3IFNwZWN0cmFsRnJlZXplU3RhdGUoYmlucywgdGhpcy5ibG9ja1NpemUsIHRoaXMuaG9wU2l6ZSkpOwoJICAgICAgICAgICAgc3RhdGUucHJvY2Vzcyh0aGlzLmlucHV0U3BlY3RydW0sIHRoaXMub3V0cHV0U3BlY3RydW0sIGZyb3plbiwgc21lYXIsIG1peCk7CgkgICAgICAgICAgICB0aGlzLmZmdC5jb21wbGV0ZVNwZWN0cnVtKHRoaXMub3V0cHV0U3BlY3RydW0pOwoJICAgICAgICAgICAgdGhpcy5mZnQuaW52ZXJzZVRyYW5zZm9ybSh0aGlzLnRpbWVDb21wbGV4LCB0aGlzLm91dHB1dFNwZWN0cnVtKTsKCSAgICAgICAgICAgIHRoaXMuZmZ0LmZyb21Db21wbGV4QXJyYXkodGhpcy50aW1lQ29tcGxleCwgb3V0cHV0KTsKCSAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb3V0cHV0Lmxlbmd0aDsgaSsrKQoJICAgICAgICAgICAgICAgIG91dHB1dFtpXSAqPSB0aGlzLndpbmRvd1tpXTsKCSAgICAgICAgfQoJICAgIH0KCX0KCXJlZ2lzdGVyUHJvY2Vzc29yKCJzcGVjdHJhbC1mcmVlemUiLCBTcGVjdHJhbEZyZWV6ZVdvcmtsZXRQcm9jZXNzb3IpOwoKCWV4cG9ydHMuU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yID0gU3BlY3RyYWxGcmVlemVXb3JrbGV0UHJvY2Vzc29yOwoKCXJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zcGVjdHJhbC1mcmVlemUtYnVuZGxlLmpzLm1hcAo=\"","export default \"data:text/javascript;base64,dmFyIHN0ZXJlb1RvQmZvcm1hdCA9IChmdW5jdGlvbiAoZXhwb3J0cykgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNvbnN0IEJVVFRFUldPUlRIX1EgPSBNYXRoLlNRUlQxXzI7CiAgICBjb25zdCBMT1dfQ1VUT0ZGX0haID0gMjUwOwogICAgY29uc3QgSElHSF9DVVRPRkZfSFogPSAyMDAwOwogICAgLy8gVGllciAxIHBlcmNlcHR1YWwgdHVuaW5nOgogICAgLy8gLSBLZWVwIGxvdy1iYW5kIGVuZXJneSBtb3N0bHkgb21uaWRpcmVjdGlvbmFsLgogICAgLy8gLSBSZWR1Y2UgVyByZWxhdGl2ZSB0byB0aGUgb3JpZ2luYWwgc3FydCgxLzIpIG1hcHBpbmcuCiAgICAvLyAtIExldCB1cHBlciBiYW5kcyBjYXJyeSBtb3JlIG9mIHRoZSBob3Jpem9udGFsIGN1ZS4KICAgIGNvbnN0IFdfR0FJTl9MT1cgPSAwLjU7CiAgICBjb25zdCBXX0dBSU5fTUlEID0gMC40NTsKICAgIGNvbnN0IFdfR0FJTl9ISUdIID0gMC4zNTsKICAgIGNvbnN0IFhfR0FJTl9MT1cgPSAwOwogICAgY29uc3QgWF9HQUlOX01JRCA9IDAuMzU7CiAgICBjb25zdCBYX0dBSU5fSElHSCA9IDAuNTU7CiAgICBjb25zdCBZX0dBSU5fTE9XID0gMDsKICAgIGNvbnN0IFlfR0FJTl9NSUQgPSAwLjU7CiAgICBjb25zdCBZX0dBSU5fSElHSCA9IDAuODU7CiAgICBjb25zdCBTSURFX0RPTUlOQU5DRV9FUFNJTE9OID0gMWUtOTsKICAgIC8vIENvaGVyZW5jZSBzbW9vdGhpbmcuIEV4cG9uZW50aWFsIGZpcnN0LW9yZGVyIGxvdy1wYXNzIG9uIHBlci1iYW5kIHBvd2VyCiAgICAvLyBhbmQgY3Jvc3MtcG93ZXIuIEFscGhhIGNsb3NlciB0byAxIG1lYW5zIHNsb3dlciB0cmFja2luZzsgdGhpcyB2YWx1ZSBnaXZlcwogICAgLy8gYSB0aW1lIGNvbnN0YW50IG9mIGFib3V0IDIwIG1zIGF0IDQ4IGtIeiwgd2hpY2ggbGluZXMgdXAgd2l0aCBCQ0MtcGFwZXIKICAgIC8vIHJlY29tbWVuZGF0aW9ucyBmb3IgcGVyY2VwdHVhbC1jdWUgc21vb3RoaW5nLgogICAgY29uc3QgQ09IRVJFTkNFX1NNT09USElOR19BTFBIQSA9IDAuOTk5OwogICAgLy8gTnVtZXJpY2FsIGZsb29yIG9uIHBlci1iYW5kIHBvd2VyIHNvIHRoZSBJQ0MgZGVub21pbmF0b3IgbmV2ZXIgYmxvd3MgdXAKICAgIC8vIGR1cmluZyBzaWxlbmNlLgogICAgY29uc3QgQ09IRVJFTkNFX1BPV0VSX0VQU0lMT04gPSAxZS0xMjsKICAgIGNsYXNzIEJpcXVhZEZpbHRlciB7CiAgICAgICAgYjAgPSAwOwogICAgICAgIGIxID0gMDsKICAgICAgICBiMiA9IDA7CiAgICAgICAgYTEgPSAwOwogICAgICAgIGEyID0gMDsKICAgICAgICB4MSA9IDA7CiAgICAgICAgeDIgPSAwOwogICAgICAgIHkxID0gMDsKICAgICAgICB5MiA9IDA7CiAgICAgICAgY29uc3RydWN0b3IodHlwZSwgc2FtcGxlUmF0ZSwgY3V0b2ZmSHosIHEgPSBCVVRURVJXT1JUSF9RKSB7CiAgICAgICAgICAgIHRoaXMuY29uZmlndXJlKHR5cGUsIHNhbXBsZVJhdGUsIGN1dG9mZkh6LCBxKTsKICAgICAgICB9CiAgICAgICAgY29uZmlndXJlKHR5cGUsIHNhbXBsZVJhdGUsIGN1dG9mZkh6LCBxKSB7CiAgICAgICAgICAgIGNvbnN0IGNsYW1wZWRDdXRvZmYgPSBNYXRoLm1heCgxLCBNYXRoLm1pbihjdXRvZmZIeiwgc2FtcGxlUmF0ZSAqIDAuNDUpKTsKICAgICAgICAgICAgY29uc3Qgb21lZ2EgPSAoMiAqIE1hdGguUEkgKiBjbGFtcGVkQ3V0b2ZmKSAvIHNhbXBsZVJhdGU7CiAgICAgICAgICAgIGNvbnN0IHNpbiA9IE1hdGguc2luKG9tZWdhKTsKICAgICAgICAgICAgY29uc3QgY29zID0gTWF0aC5jb3Mob21lZ2EpOwogICAgICAgICAgICBjb25zdCBhbHBoYSA9IHNpbiAvICgyICogcSk7CiAgICAgICAgICAgIGxldCBiMDsKICAgICAgICAgICAgbGV0IGIxOwogICAgICAgICAgICBsZXQgYjI7CiAgICAgICAgICAgIGlmICh0eXBlID09PSAibG93cGFzcyIpIHsKICAgICAgICAgICAgICAgIGIwID0gKDEgLSBjb3MpIC8gMjsKICAgICAgICAgICAgICAgIGIxID0gMSAtIGNvczsKICAgICAgICAgICAgICAgIGIyID0gKDEgLSBjb3MpIC8gMjsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIGIwID0gKDEgKyBjb3MpIC8gMjsKICAgICAgICAgICAgICAgIGIxID0gLSgxICsgY29zKTsKICAgICAgICAgICAgICAgIGIyID0gKDEgKyBjb3MpIC8gMjsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBhMCA9IDEgKyBhbHBoYTsKICAgICAgICAgICAgY29uc3QgYTEgPSAtMiAqIGNvczsKICAgICAgICAgICAgY29uc3QgYTIgPSAxIC0gYWxwaGE7CiAgICAgICAgICAgIHRoaXMuYjAgPSBiMCAvIGEwOwogICAgICAgICAgICB0aGlzLmIxID0gYjEgLyBhMDsKICAgICAgICAgICAgdGhpcy5iMiA9IGIyIC8gYTA7CiAgICAgICAgICAgIHRoaXMuYTEgPSBhMSAvIGEwOwogICAgICAgICAgICB0aGlzLmEyID0gYTIgLyBhMDsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dCkgewogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSB0aGlzLmIwICogaW5wdXQgKyB0aGlzLmIxICogdGhpcy54MSArIHRoaXMuYjIgKiB0aGlzLngyIC0gdGhpcy5hMSAqIHRoaXMueTEgLSB0aGlzLmEyICogdGhpcy55MjsKICAgICAgICAgICAgdGhpcy54MiA9IHRoaXMueDE7CiAgICAgICAgICAgIHRoaXMueDEgPSBpbnB1dDsKICAgICAgICAgICAgdGhpcy55MiA9IHRoaXMueTE7CiAgICAgICAgICAgIHRoaXMueTEgPSBvdXRwdXQ7CiAgICAgICAgICAgIHJldHVybiBvdXRwdXQ7CiAgICAgICAgfQogICAgfQogICAgLyoqCiAgICAgKiBSdW5uaW5nIHBlci1iYW5kIGludGVyLWNoYW5uZWwgY29oZXJlbmNlIGVzdGltYXRvci4KICAgICAqCiAgICAgKiBNYWludGFpbnMgZXhwb25lbnRpYWxseSBzbW9vdGhlZCBlc3RpbWF0ZXMgb2YgTCpMLCBSKlIsIGFuZCBMKlIsIHRoZW4KICAgICAqIGRlcml2ZXMgYSBub3JtYWxpemVkIGNvaGVyZW5jZSB2YWx1ZSBgfDxMUj58IC8gc3FydCg8TMKyPiAqIDxSwrI+KWAgaW4KICAgICAqIHRoZSByYW5nZSBbMCwgMV0uIEhpZ2ggY29oZXJlbmNlIGluZGljYXRlcyBhIHN0YWJsZSBpbi1waGFzZSAvIHBhbm5lZAogICAgICogc291cmNlOyBsb3cgY29oZXJlbmNlIGluZGljYXRlcyBkZWNvcnJlbGF0ZWQgZGlmZnVzZSBjb250ZW50IGxpa2Ugcm9vbQogICAgICogcmV2ZXJiLgogICAgICoKICAgICAqIFRoZSBzaWduIG9mIDxMUj4gaXMgcHJlc2VydmVkIGludGVybmFsbHkgc28gY2FsbGVycyBjYW4gZGlmZmVyZW50aWF0ZQogICAgICogcG9zaXRpdmUgKGluLXBoYXNlIC8gY2VudGVyKSBmcm9tIG5lZ2F0aXZlIChhbnRpLXBoYXNlKSBjb3JyZWxhdGlvbiwKICAgICAqIGJ1dCB0aGUgcHVibGljIGBjb2hlcmVuY2UoKWAgaGVscGVyIHJldHVybnMgbWFnbml0dWRlIG9ubHkuCiAgICAgKi8KICAgIGNsYXNzIENyb3NzQ29ycmVsYXRvciB7CiAgICAgICAgc21vb3RoZWRMTCA9IDA7CiAgICAgICAgc21vb3RoZWRSUiA9IDA7CiAgICAgICAgc21vb3RoZWRMUiA9IDA7CiAgICAgICAgYWxwaGE7CiAgICAgICAgb25lTWludXNBbHBoYTsKICAgICAgICBjb25zdHJ1Y3RvcihhbHBoYSA9IENPSEVSRU5DRV9TTU9PVEhJTkdfQUxQSEEpIHsKICAgICAgICAgICAgdGhpcy5hbHBoYSA9IGFscGhhOwogICAgICAgICAgICB0aGlzLm9uZU1pbnVzQWxwaGEgPSAxIC0gYWxwaGE7CiAgICAgICAgfQogICAgICAgIHVwZGF0ZShsZWZ0LCByaWdodCkgewogICAgICAgICAgICB0aGlzLnNtb290aGVkTEwgPSB0aGlzLmFscGhhICogdGhpcy5zbW9vdGhlZExMICsgdGhpcy5vbmVNaW51c0FscGhhICogbGVmdCAqIGxlZnQ7CiAgICAgICAgICAgIHRoaXMuc21vb3RoZWRSUiA9IHRoaXMuYWxwaGEgKiB0aGlzLnNtb290aGVkUlIgKyB0aGlzLm9uZU1pbnVzQWxwaGEgKiByaWdodCAqIHJpZ2h0OwogICAgICAgICAgICB0aGlzLnNtb290aGVkTFIgPSB0aGlzLmFscGhhICogdGhpcy5zbW9vdGhlZExSICsgdGhpcy5vbmVNaW51c0FscGhhICogbGVmdCAqIHJpZ2h0OwogICAgICAgIH0KICAgICAgICAvKiogTWFnbml0dWRlIG9mIG5vcm1hbGl6ZWQgY3Jvc3MtY29ycmVsYXRpb24gaW4gWzAsIDFdLiAqLwogICAgICAgIGNvaGVyZW5jZSgpIHsKICAgICAgICAgICAgY29uc3QgZGVub20gPSBNYXRoLnNxcnQodGhpcy5zbW9vdGhlZExMICogdGhpcy5zbW9vdGhlZFJSKSArIENPSEVSRU5DRV9QT1dFUl9FUFNJTE9OOwogICAgICAgICAgICBjb25zdCBjID0gTWF0aC5hYnModGhpcy5zbW9vdGhlZExSKSAvIGRlbm9tOwogICAgICAgICAgICByZXR1cm4gYyA+IDEgPyAxIDogYzsKICAgICAgICB9CiAgICAgICAgLyoqCiAgICAgICAgICogU2lnbmVkIG5vcm1hbGl6ZWQgY3Jvc3MtY29ycmVsYXRpb24gaW4gWy0xLCArMV0uCiAgICAgICAgICoKICAgICAgICAgKiAtICsxOiBwZXJmZWN0bHkgaW4tcGhhc2UgKEwgYW5kIFIgbW92ZSB0b2dldGhlciwgbW9ubyAvIGNlbnRlciBwYW4pCiAgICAgICAgICogLSAwOiB1bmNvcnJlbGF0ZWQgKGRpZmZ1c2UgYW1iaWVudCwgaGFyZC1wYW5uZWQgc2luZ2xlIHNvdXJjZSkKICAgICAgICAgKiAtIC0xOiBwZXJmZWN0bHkgYW50aS1waGFzZSAoTCBhbmQgUiBtb3ZlIG9wcG9zaXRlbHksIHN0ZXJlbyB3aWR0aAogICAgICAgICAqICAgdHJpY2tzIG9yIG91dC1vZi1waGFzZSBjb250ZW50KQogICAgICAgICAqCiAgICAgICAgICogRnJvbnQvYmFjayByb3V0aW5nIHdhbnRzIHBvc2l0aXZlIGNvcnJlbGF0aW9uIChyZWFsIGNlbnRlciBtYXRlcmlhbCkuCiAgICAgICAgICogTGVmdC9yaWdodCByb3V0aW5nIHdhbnRzIHdoYXRldmVyIGlzbid0IGluLXBoYXNlIChhbnl0aGluZyB0aGF0IGhhcwogICAgICAgICAqIGEgc3RhYmxlIGRpZmZlcmVuY2UgYmV0d2VlbiBjaGFubmVscywgaW5jbHVkaW5nIGhhcmQgcGFucykuCiAgICAgICAgICovCiAgICAgICAgc2lnbmVkQ29oZXJlbmNlKCkgewogICAgICAgICAgICBjb25zdCBkZW5vbSA9IE1hdGguc3FydCh0aGlzLnNtb290aGVkTEwgKiB0aGlzLnNtb290aGVkUlIpICsgQ09IRVJFTkNFX1BPV0VSX0VQU0lMT047CiAgICAgICAgICAgIGNvbnN0IGMgPSB0aGlzLnNtb290aGVkTFIgLyBkZW5vbTsKICAgICAgICAgICAgaWYgKGMgPiAxKQogICAgICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgICAgIGlmIChjIDwgLTEpCiAgICAgICAgICAgICAgICByZXR1cm4gLTE7CiAgICAgICAgICAgIHJldHVybiBjOwogICAgICAgIH0KICAgIH0KICAgIGNsYXNzIFN0ZXJlb1RvRm9hVXBtaXhlciB7CiAgICAgICAgbG93TGVmdDsKICAgICAgICBsb3dSaWdodDsKICAgICAgICBoaWdoTGVmdDsKICAgICAgICBoaWdoUmlnaHQ7CiAgICAgICAgbG93Q29oZXJlbmNlOwogICAgICAgIG1pZENvaGVyZW5jZTsKICAgICAgICBoaWdoQ29oZXJlbmNlOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5sb3dMZWZ0ID0gbmV3IEJpcXVhZEZpbHRlcigibG93cGFzcyIsIHNhbXBsZVJhdGUsIExPV19DVVRPRkZfSFopOwogICAgICAgICAgICB0aGlzLmxvd1JpZ2h0ID0gbmV3IEJpcXVhZEZpbHRlcigibG93cGFzcyIsIHNhbXBsZVJhdGUsIExPV19DVVRPRkZfSFopOwogICAgICAgICAgICB0aGlzLmhpZ2hMZWZ0ID0gbmV3IEJpcXVhZEZpbHRlcigiaGlnaHBhc3MiLCBzYW1wbGVSYXRlLCBISUdIX0NVVE9GRl9IWik7CiAgICAgICAgICAgIHRoaXMuaGlnaFJpZ2h0ID0gbmV3IEJpcXVhZEZpbHRlcigiaGlnaHBhc3MiLCBzYW1wbGVSYXRlLCBISUdIX0NVVE9GRl9IWik7CiAgICAgICAgICAgIHRoaXMubG93Q29oZXJlbmNlID0gbmV3IENyb3NzQ29ycmVsYXRvcigpOwogICAgICAgICAgICB0aGlzLm1pZENvaGVyZW5jZSA9IG5ldyBDcm9zc0NvcnJlbGF0b3IoKTsKICAgICAgICAgICAgdGhpcy5oaWdoQ29oZXJlbmNlID0gbmV3IENyb3NzQ29ycmVsYXRvcigpOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGxlZnQsIHJpZ2h0LCB3LCB5LCB6LCB4KSB7CiAgICAgICAgICAgIGNvbnN0IGZyYW1lQ291bnQgPSBNYXRoLm1pbihsZWZ0Lmxlbmd0aCwgcmlnaHQubGVuZ3RoLCB3Lmxlbmd0aCwgeC5sZW5ndGgsIHkubGVuZ3RoLCB6Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGZyYW1lID0gMDsgZnJhbWUgPCBmcmFtZUNvdW50OyBmcmFtZSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBsZWZ0U2FtcGxlID0gbGVmdFtmcmFtZV07CiAgICAgICAgICAgICAgICBjb25zdCByaWdodFNhbXBsZSA9IHJpZ2h0W2ZyYW1lXTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd0xlZnQgPSB0aGlzLmxvd0xlZnQucHJvY2VzcyhsZWZ0U2FtcGxlKTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd1JpZ2h0ID0gdGhpcy5sb3dSaWdodC5wcm9jZXNzKHJpZ2h0U2FtcGxlKTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hMZWZ0ID0gdGhpcy5oaWdoTGVmdC5wcm9jZXNzKGxlZnRTYW1wbGUpOwogICAgICAgICAgICAgICAgY29uc3QgaGlnaFJpZ2h0ID0gdGhpcy5oaWdoUmlnaHQucHJvY2VzcyhyaWdodFNhbXBsZSk7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRMZWZ0ID0gbGVmdFNhbXBsZSAtIGxvd0xlZnQgLSBoaWdoTGVmdDsKICAgICAgICAgICAgICAgIGNvbnN0IG1pZFJpZ2h0ID0gcmlnaHRTYW1wbGUgLSBsb3dSaWdodCAtIGhpZ2hSaWdodDsKICAgICAgICAgICAgICAgIHRoaXMubG93Q29oZXJlbmNlLnVwZGF0ZShsb3dMZWZ0LCBsb3dSaWdodCk7CiAgICAgICAgICAgICAgICB0aGlzLm1pZENvaGVyZW5jZS51cGRhdGUobWlkTGVmdCwgbWlkUmlnaHQpOwogICAgICAgICAgICAgICAgdGhpcy5oaWdoQ29oZXJlbmNlLnVwZGF0ZShoaWdoTGVmdCwgaGlnaFJpZ2h0KTsKICAgICAgICAgICAgICAgIGNvbnN0IGxvd01pZCA9IChsb3dMZWZ0ICsgbG93UmlnaHQpICogMC41OwogICAgICAgICAgICAgICAgY29uc3QgbG93U2lkZSA9IChsb3dMZWZ0IC0gbG93UmlnaHQpICogMC41OwogICAgICAgICAgICAgICAgY29uc3QgbWlkTWlkID0gKG1pZExlZnQgKyBtaWRSaWdodCkgKiAwLjU7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRTaWRlID0gKG1pZExlZnQgLSBtaWRSaWdodCkgKiAwLjU7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoTWlkID0gKGhpZ2hMZWZ0ICsgaGlnaFJpZ2h0KSAqIDAuNTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hTaWRlID0gKGhpZ2hMZWZ0IC0gaGlnaFJpZ2h0KSAqIDAuNTsKICAgICAgICAgICAgICAgIC8vIENvaGVyZW5jZSBnYXRpbmcgdXNlcyBzaWduZWQgY3Jvc3MtY29ycmVsYXRpb24gcGVyIGJhbmQ6CiAgICAgICAgICAgICAgICAvLyAgIM+BIOKJiCArMTogaW4tcGhhc2UgLyBjZW50ZXIgbWF0ZXJpYWwg4oaSIGZyb250YWwgWCBjdWUsIG5vIFkKICAgICAgICAgICAgICAgIC8vICAgz4Eg4omIICAwOiBoYXJkLXBhbm5lZCBzaW5nbGUgc291cmNlIG9yIGRpZmZ1c2UgYW1iaWVudCDihpIgWSBvcGVuLCBYIHN1cHByZXNzZWQKICAgICAgICAgICAgICAgIC8vICAgz4Eg4omIIC0xOiBhbnRpLXBoYXNlIC8gaW52ZXJ0ZWQtcGFpciBzdGVyZW8gdHJpY2tlcnkg4oaSIFkgb3BlbiwgWCBzdXBwcmVzc2VkCiAgICAgICAgICAgICAgICAvLwogICAgICAgICAgICAgICAgLy8gWCBnYWluIGZhY3RvciA9IG1heCgwLCDPgSkuIE9ubHkgcG9zaXRpdmUgaW4tcGhhc2UgY29udGVudCBzdGVlcnMKICAgICAgICAgICAgICAgIC8vIGZvcndhcmQ7IGRlY29ycmVsYXRlZCBhbmQgYW50aS1waGFzZSBjb250ZW50IHN0YXlzIG91dCBvZiBYLgogICAgICAgICAgICAgICAgLy8KICAgICAgICAgICAgICAgIC8vIFkgZ2FpbiBmYWN0b3IgPSAxIC0gbWF4KDAsIM+BKS4gQ2VudGVyIG1hdGVyaWFsIChoaWdoICvPgSkKICAgICAgICAgICAgICAgIC8vIHN1cHByZXNzZXMgWTsgZXZlcnl0aGluZyBlbHNlIChoYXJkIHBhbnMsIGFudGktcGhhc2UsIGFtYmllbnQpCiAgICAgICAgICAgICAgICAvLyBrZWVwcyB0aGUgc2lkZSBjdWUuCiAgICAgICAgICAgICAgICAvLwogICAgICAgICAgICAgICAgLy8gSGlnaC1mcmVxdWVuY3kgdm9jYWxzIHdlcmUgc3RpbGwgcGVlbGluZyBzaWRld2F5cyBiZWNhdXNlICJub3QKICAgICAgICAgICAgICAgIC8vIHBlcmZlY3RseSBjZW50ZXJlZCIgd2FzIGVub3VnaCB0byBrZWVwIGEgbG90IG9mIHRyZWJsZSBpbiBZLgogICAgICAgICAgICAgICAgLy8gTWFrZSB0aGUgaGlnaCBiYW5kIG1vcmUgc2VsZWN0aXZlOiBvbmx5IHN0ZWVyIGhhcmQgc2lkZXdheXMgd2hlbgogICAgICAgICAgICAgICAgLy8gdGhlIGJhbmQgaXMgYm90aCBub24tY2VudGVyZWQgYW5kIGFjdHVhbGx5IHNpZGUtZG9taW5hbnQuCiAgICAgICAgICAgICAgICBjb25zdCBsb3dSaG8gPSBNYXRoLm1heCgwLCB0aGlzLmxvd0NvaGVyZW5jZS5zaWduZWRDb2hlcmVuY2UoKSk7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRSaG8gPSBNYXRoLm1heCgwLCB0aGlzLm1pZENvaGVyZW5jZS5zaWduZWRDb2hlcmVuY2UoKSk7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoUmhvID0gTWF0aC5tYXgoMCwgdGhpcy5oaWdoQ29oZXJlbmNlLnNpZ25lZENvaGVyZW5jZSgpKTsKICAgICAgICAgICAgICAgIGNvbnN0IGhpZ2hTaWRlRG9taW5hbmNlID0gTWF0aC5hYnMoaGlnaFNpZGUpIC8gKE1hdGguYWJzKGhpZ2hNaWQpICsgTWF0aC5hYnMoaGlnaFNpZGUpICsgU0lERV9ET01JTkFOQ0VfRVBTSUxPTik7CiAgICAgICAgICAgICAgICBjb25zdCBoaWdoWVdlaWdodCA9ICgxIC0gaGlnaFJobykgKiBoaWdoU2lkZURvbWluYW5jZSAqIGhpZ2hTaWRlRG9taW5hbmNlOwogICAgICAgICAgICAgICAgd1tmcmFtZV0gPSBsb3dNaWQgKiBXX0dBSU5fTE9XICsgbWlkTWlkICogV19HQUlOX01JRCArIGhpZ2hNaWQgKiBXX0dBSU5fSElHSDsKICAgICAgICAgICAgICAgIHlbZnJhbWVdID0KICAgICAgICAgICAgICAgICAgICBsb3dTaWRlICogWV9HQUlOX0xPVyAqICgxIC0gbG93UmhvKSArCiAgICAgICAgICAgICAgICAgICAgICAgIG1pZFNpZGUgKiBZX0dBSU5fTUlEICogKDEgLSBtaWRSaG8pICsKICAgICAgICAgICAgICAgICAgICAgICAgaGlnaFNpZGUgKiBZX0dBSU5fSElHSCAqIGhpZ2hZV2VpZ2h0OwogICAgICAgICAgICAgICAgeltmcmFtZV0gPSAwOwogICAgICAgICAgICAgICAgeFtmcmFtZV0gPSBsb3dNaWQgKiBYX0dBSU5fTE9XICogbG93UmhvICsgbWlkTWlkICogWF9HQUlOX01JRCAqIG1pZFJobyArIGhpZ2hNaWQgKiBYX0dBSU5fSElHSCAqIGhpZ2hSaG87CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDpzdGVyZW8tdG8tYmZvcm1hdF0iOwogICAgY2xhc3MgU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBmcmFtZXNQcm9jZXNzZWQgPSAwOwogICAgICAgIGZyYW1lc1Nob3J0SW5wdXQgPSAwOwogICAgICAgIGxhc3RSZXBvcnRGcmFtZSA9IDA7CiAgICAgICAgdXBtaXhlciA9IG5ldyBTdGVyZW9Ub0ZvYVVwbWl4ZXIoc2FtcGxlUmF0ZSk7CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIF9wYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA8IDIgfHwgIW91dHB1dCB8fCBvdXRwdXQubGVuZ3RoIDwgNCkgewogICAgICAgICAgICAgICAgdGhpcy5mcmFtZXNTaG9ydElucHV0Kys7CiAgICAgICAgICAgICAgICB0aGlzLmZyYW1lc1Byb2Nlc3NlZCsrOwogICAgICAgICAgICAgICAgdGhpcy5tYXliZVJlcG9ydChpbnB1dD8ubGVuZ3RoID8/IDAsICJzaG9ydC1pbnB1dCIsIGlucHV0LCBvdXRwdXQpOwogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy51cG1peGVyLnByb2Nlc3MoaW5wdXRbMF0sIGlucHV0WzFdLCBvdXRwdXRbMF0sIG91dHB1dFsxXSwgb3V0cHV0WzJdLCBvdXRwdXRbM10pOwogICAgICAgICAgICB0aGlzLmZyYW1lc1Byb2Nlc3NlZCsrOwogICAgICAgICAgICB0aGlzLm1heWJlUmVwb3J0KGlucHV0Lmxlbmd0aCwgIm9rIiwgaW5wdXQsIG91dHB1dCk7CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgICAgICBtYXliZVJlcG9ydChjaGFubmVscywga2luZCwgaW5wdXQsIG91dHB1dCkgewogICAgICAgICAgICBpZiAodGhpcy5mcmFtZXNQcm9jZXNzZWQgLSB0aGlzLmxhc3RSZXBvcnRGcmFtZSA8IDc1MCkKICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgdGhpcy5sYXN0UmVwb3J0RnJhbWUgPSB0aGlzLmZyYW1lc1Byb2Nlc3NlZDsKICAgICAgICAgICAgbGV0IHN0YXRzID0gIiI7CiAgICAgICAgICAgIGlmIChpbnB1dCAmJiBpbnB1dC5sZW5ndGggPj0gMiAmJiBvdXRwdXQgJiYgb3V0cHV0Lmxlbmd0aCA+PSA0KSB7CiAgICAgICAgICAgICAgICBjb25zdCBwZWFrID0gKGEpID0+IHsKICAgICAgICAgICAgICAgICAgICBsZXQgbSA9IDA7CiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHYgPSBNYXRoLmFicyhhW2ldKTsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHYgPiBtKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbSA9IHY7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybiBtOwogICAgICAgICAgICAgICAgfTsKICAgICAgICAgICAgICAgIHN0YXRzID0KICAgICAgICAgICAgICAgICAgICBgIHBlYWtMPSR7cGVhayhpbnB1dFswXSkudG9GaXhlZCg0KX1gICsKICAgICAgICAgICAgICAgICAgICAgICAgYCBwZWFrUj0ke3BlYWsoaW5wdXRbMV0pLnRvRml4ZWQoNCl9YCArCiAgICAgICAgICAgICAgICAgICAgICAgIGAgcGVha1c9JHtwZWFrKG91dHB1dFswXSkudG9GaXhlZCg0KX1gICsKICAgICAgICAgICAgICAgICAgICAgICAgYCBwZWFrWT0ke3BlYWsob3V0cHV0WzFdKS50b0ZpeGVkKDQpfWAgKwogICAgICAgICAgICAgICAgICAgICAgICBgIHBlYWtaPSR7cGVhayhvdXRwdXRbMl0pLnRvRml4ZWQoNCl9YCArCiAgICAgICAgICAgICAgICAgICAgICAgIGAgcGVha1g9JHtwZWFrKG91dHB1dFszXSkudG9GaXhlZCg0KX1gOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHByb2Nlc3MgdGljayBmcmFtZXNUb3RhbD0ke3RoaXMuZnJhbWVzUHJvY2Vzc2VkfSBzaG9ydElucHV0PSR7dGhpcy5mcmFtZXNTaG9ydElucHV0fSBjaGFubmVsc1RoaXNUaWNrPSR7Y2hhbm5lbHN9IGtpbmQ9JHtraW5kfSR7c3RhdHN9YCk7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3Igc3RhcnRgKTsKICAgICAgICByZWdpc3RlclByb2Nlc3Nvcigic3RlcmVvLXRvLWJmb3JtYXQiLCBTdGVyZW9Ub0JGb3JtYXRQcm9jZXNzb3IpOwogICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGNvbXBsZXRlYCk7CiAgICB9CiAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICBjb25zb2xlLmVycm9yKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgZmFpbGVkYCwgZXJyb3IpOwogICAgICAgIHRocm93IGVycm9yOwogICAgfQoKICAgIGV4cG9ydHMuU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yID0gU3RlcmVvVG9CRm9ybWF0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zdGVyZW8tdG8tYmZvcm1hdC1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHN0ZXJlb1dpZGVuZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICBmdW5jdGlvbiBzZWVkZWRSYW5kb20oc2VlZCkgewogICAgICAgIGxldCBzdGF0ZSA9IHNlZWQgPj4+IDA7CiAgICAgICAgcmV0dXJuICgpID0+IHsKICAgICAgICAgICAgc3RhdGUgPSAoMTY2NDUyNSAqIHN0YXRlICsgMTAxMzkwNDIyMykgPj4+IDA7CiAgICAgICAgICAgIHJldHVybiBzdGF0ZSAvIDB4MV8wMDAwXzAwMDA7CiAgICAgICAgfTsKICAgIH0KICAgIC8qKiBFeHBvbmVudGlhbGx5IGRlY2F5aW5nIHNwYXJzZSB2ZWx2ZXQtbm9pc2UgRklSIChTY2hsZWNodCBldCBhbC4sIERBRngtMTgpLiAqLwogICAgZnVuY3Rpb24gYnVpbGREZWNvcnJlbGF0b3JUYXBzKHNhbXBsZVJhdGUsIHNlZWQpIHsKICAgICAgICBjb25zdCByYW5kb20gPSBzZWVkZWRSYW5kb20oc2VlZCk7CiAgICAgICAgY29uc3QgbGVuZ3RoID0gTWF0aC5tYXgoMzIsIE1hdGgucm91bmQoc2FtcGxlUmF0ZSAqIDAuMDIpKTsKICAgICAgICBjb25zdCBzcGFjaW5nID0gTWF0aC5tYXgoMywgTWF0aC5yb3VuZChzYW1wbGVSYXRlIC8gMjQwMCkpOwogICAgICAgIGNvbnN0IHRhcHMgPSBbXTsKICAgICAgICBsZXQgZW5lcmd5ID0gMDsKICAgICAgICBmb3IgKGxldCBzdGFydCA9IHNwYWNpbmc7IHN0YXJ0IDwgbGVuZ3RoOyBzdGFydCArPSBzcGFjaW5nKSB7CiAgICAgICAgICAgIGNvbnN0IGRlbGF5ID0gTWF0aC5taW4obGVuZ3RoIC0gMSwgc3RhcnQgKyBNYXRoLmZsb29yKHJhbmRvbSgpICogc3BhY2luZykpOwogICAgICAgICAgICBjb25zdCBzaWduID0gcmFuZG9tKCkgPCAwLjUgPyAtMSA6IDE7CiAgICAgICAgICAgIGNvbnN0IGdhaW4gPSBzaWduICogTWF0aC5leHAoKC0zICogZGVsYXkpIC8gbGVuZ3RoKTsKICAgICAgICAgICAgdGFwcy5wdXNoKHsgZGVsYXksIGdhaW4gfSk7CiAgICAgICAgICAgIGVuZXJneSArPSBnYWluICogZ2FpbjsKICAgICAgICB9CiAgICAgICAgY29uc3Qgbm9ybSA9IGVuZXJneSA+IDAgPyAxIC8gTWF0aC5zcXJ0KGVuZXJneSkgOiAxOwogICAgICAgIGZvciAoY29uc3QgdGFwIG9mIHRhcHMpCiAgICAgICAgICAgIHRhcC5nYWluICo9IG5vcm07CiAgICAgICAgcmV0dXJuIHRhcHM7CiAgICB9CiAgICBjbGFzcyBTcGFyc2VGaXIgewogICAgICAgIHRhcHM7CiAgICAgICAgYnVmZmVyOwogICAgICAgIG1hc2s7CiAgICAgICAgd3JpdGUgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHRhcHMpIHsKICAgICAgICAgICAgdGhpcy50YXBzID0gdGFwczsKICAgICAgICAgICAgY29uc3QgbWF4RGVsYXkgPSB0YXBzLnJlZHVjZSgobWF4LCB0YXApID0+IE1hdGgubWF4KG1heCwgdGFwLmRlbGF5KSwgMSk7CiAgICAgICAgICAgIGNvbnN0IHNpemUgPSAyICoqIE1hdGguY2VpbChNYXRoLmxvZzIobWF4RGVsYXkgKyAyKSk7CiAgICAgICAgICAgIHRoaXMuYnVmZmVyID0gbmV3IEZsb2F0MzJBcnJheShzaXplKTsKICAgICAgICAgICAgdGhpcy5tYXNrID0gc2l6ZSAtIDE7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXQpIHsKICAgICAgICAgICAgdGhpcy5idWZmZXJbdGhpcy53cml0ZV0gPSBpbnB1dDsKICAgICAgICAgICAgbGV0IG91dHB1dCA9IDA7CiAgICAgICAgICAgIGZvciAoY29uc3QgdGFwIG9mIHRoaXMudGFwcykKICAgICAgICAgICAgICAgIG91dHB1dCArPSB0YXAuZ2FpbiAqIHRoaXMuYnVmZmVyWyh0aGlzLndyaXRlIC0gdGFwLmRlbGF5KSAmIHRoaXMubWFza107CiAgICAgICAgICAgIHRoaXMud3JpdGUgPSAodGhpcy53cml0ZSArIDEpICYgdGhpcy5tYXNrOwogICAgICAgICAgICByZXR1cm4gb3V0cHV0OwogICAgICAgIH0KICAgIH0KICAgIC8qKiBNb25vL3N0ZXJlbyBpbiB0byBzdGVyZW8gb3V0IHZlbHZldC1ub2lzZSB3aWRlbmVyIHdpdGggdHJhbnNpZW50IGJ5cGFzcy4gKi8KICAgIGNsYXNzIFN0ZXJlb1dpZGVuZXJDb3JlIHsKICAgICAgICBsZWZ0RmlyOwogICAgICAgIHJpZ2h0RmlyOwogICAgICAgIGVudmVsb3BlID0gMDsKICAgICAgICB0cmFuc2llbnQgPSAwOwogICAgICAgIHRyYW5zaWVudERlY2F5OwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUpIHsKICAgICAgICAgICAgdGhpcy5sZWZ0RmlyID0gbmV3IFNwYXJzZUZpcihidWlsZERlY29ycmVsYXRvclRhcHMoc2FtcGxlUmF0ZSwgMHg1MWYxNWUpKTsKICAgICAgICAgICAgdGhpcy5yaWdodEZpciA9IG5ldyBTcGFyc2VGaXIoYnVpbGREZWNvcnJlbGF0b3JUYXBzKHNhbXBsZVJhdGUsIDB4YzBmZmVlKSk7CiAgICAgICAgICAgIHRoaXMudHJhbnNpZW50RGVjYXkgPSBNYXRoLmV4cCgtMSAvIChzYW1wbGVSYXRlICogMC4wMTIpKTsKICAgICAgICB9CiAgICAgICAgcHJvY2Vzc1NhbXBsZShsZWZ0LCByaWdodCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IGxldmVsID0gTWF0aC5tYXgoTWF0aC5hYnMobGVmdCksIE1hdGguYWJzKHJpZ2h0KSk7CiAgICAgICAgICAgIGlmIChsZXZlbCA+IHRoaXMuZW52ZWxvcGUgKiAzICsgMC4wNCkKICAgICAgICAgICAgICAgIHRoaXMudHJhbnNpZW50ID0gMTsKICAgICAgICAgICAgdGhpcy5lbnZlbG9wZSA9IE1hdGgubWF4KGxldmVsLCB0aGlzLmVudmVsb3BlICogMC45OTUpOwogICAgICAgICAgICB0aGlzLnRyYW5zaWVudCAqPSB0aGlzLnRyYW5zaWVudERlY2F5OwogICAgICAgICAgICBjb25zdCBwcm90ZWN0aW9uID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgcGFyYW1zLnRyYW5zaWVudFByb3RlY3Rpb24pKTsKICAgICAgICAgICAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCBwYXJhbXMud2lkdGgpKSAqICgxIC0gcHJvdGVjdGlvbiAqIHRoaXMudHJhbnNpZW50KTsKICAgICAgICAgICAgY29uc3QgZGVjb3JyZWxhdGlvbiA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEsIHBhcmFtcy5kZWNvcnJlbGF0aW9uKSk7CiAgICAgICAgICAgIGNvbnN0IGZpckxlZnQgPSB0aGlzLmxlZnRGaXIucHJvY2VzcyhsZWZ0KTsKICAgICAgICAgICAgY29uc3QgZmlyUmlnaHQgPSB0aGlzLnJpZ2h0RmlyLnByb2Nlc3MocmlnaHQpOwogICAgICAgICAgICBjb25zdCBkZWNvckxlZnQgPSBsZWZ0ICogKDEgLSBkZWNvcnJlbGF0aW9uKSArIGZpckxlZnQgKiBkZWNvcnJlbGF0aW9uOwogICAgICAgICAgICBjb25zdCBkZWNvclJpZ2h0ID0gcmlnaHQgKiAoMSAtIGRlY29ycmVsYXRpb24pICsgZmlyUmlnaHQgKiBkZWNvcnJlbGF0aW9uOwogICAgICAgICAgICBjb25zdCBkcnlHYWluID0gTWF0aC5jb3MoKE1hdGguUEkgLyAyKSAqIHdpZHRoKTsKICAgICAgICAgICAgY29uc3Qgd2V0R2FpbiA9IE1hdGguc2luKChNYXRoLlBJIC8gMikgKiB3aWR0aCk7CiAgICAgICAgICAgIHJldHVybiBbbGVmdCAqIGRyeUdhaW4gKyBkZWNvckxlZnQgKiB3ZXRHYWluLCByaWdodCAqIGRyeUdhaW4gKyBkZWNvclJpZ2h0ICogd2V0R2Fpbl07CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIFN0ZXJlb1dpZGVuZXJXb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICBjb3JlID0gbmV3IFN0ZXJlb1dpZGVuZXJDb3JlKHNhbXBsZVJhdGUpOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJ3aWR0aCIsIGRlZmF1bHRWYWx1ZTogMC42NSwgbWluVmFsdWU6IDAsIG1heFZhbHVlOiAxLCBhdXRvbWF0aW9uUmF0ZTogImstcmF0ZSIgfSwKICAgICAgICAgICAgICAgIHsgbmFtZTogImRlY29ycmVsYXRpb24iLCBkZWZhdWx0VmFsdWU6IDEsIG1pblZhbHVlOiAwLCBtYXhWYWx1ZTogMSwgYXV0b21hdGlvblJhdGU6ICJrLXJhdGUiIH0sCiAgICAgICAgICAgICAgICB7IG5hbWU6ICJ0cmFuc2llbnRQcm90ZWN0aW9uIiwgZGVmYXVsdFZhbHVlOiAwLjc1LCBtaW5WYWx1ZTogMCwgbWF4VmFsdWU6IDEsIGF1dG9tYXRpb25SYXRlOiAiay1yYXRlIiB9LAogICAgICAgICAgICBdOwogICAgICAgIH0KICAgICAgICBwcm9jZXNzKGlucHV0cywgb3V0cHV0cywgcGFyYW1ldGVycykgewogICAgICAgICAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXSA/PyBbXTsKICAgICAgICAgICAgaWYgKG91dHB1dC5sZW5ndGggPCAyKQogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIGNvbnN0IGxlZnRJbnB1dCA9IGlucHV0WzBdOwogICAgICAgICAgICBjb25zdCByaWdodElucHV0ID0gaW5wdXRbMV0gPz8gbGVmdElucHV0OwogICAgICAgICAgICBjb25zdCBsZWZ0T3V0cHV0ID0gb3V0cHV0WzBdOwogICAgICAgICAgICBjb25zdCByaWdodE91dHB1dCA9IG91dHB1dFsxXTsKICAgICAgICAgICAgaWYgKCFsZWZ0SW5wdXQgfHwgIXJpZ2h0SW5wdXQpIHsKICAgICAgICAgICAgICAgIGxlZnRPdXRwdXQuZmlsbCgwKTsKICAgICAgICAgICAgICAgIHJpZ2h0T3V0cHV0LmZpbGwoMCk7CiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICB3aWR0aDogcGFyYW1ldGVycy53aWR0aFtwYXJhbWV0ZXJzLndpZHRoLmxlbmd0aCAtIDFdLAogICAgICAgICAgICAgICAgZGVjb3JyZWxhdGlvbjogcGFyYW1ldGVycy5kZWNvcnJlbGF0aW9uW3BhcmFtZXRlcnMuZGVjb3JyZWxhdGlvbi5sZW5ndGggLSAxXSwKICAgICAgICAgICAgICAgIHRyYW5zaWVudFByb3RlY3Rpb246IHBhcmFtZXRlcnMudHJhbnNpZW50UHJvdGVjdGlvbltwYXJhbWV0ZXJzLnRyYW5zaWVudFByb3RlY3Rpb24ubGVuZ3RoIC0gMV0sCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVmdE91dHB1dC5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgW2xlZnQsIHJpZ2h0XSA9IHRoaXMuY29yZS5wcm9jZXNzU2FtcGxlKGxlZnRJbnB1dFtpXSwgcmlnaHRJbnB1dFtpXSwgcGFyYW1zKTsKICAgICAgICAgICAgICAgIGxlZnRPdXRwdXRbaV0gPSBsZWZ0OwogICAgICAgICAgICAgICAgcmlnaHRPdXRwdXRbaV0gPSByaWdodDsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICByZWdpc3RlclByb2Nlc3Nvcigic3RlcmVvLXdpZGVuZXIiLCBTdGVyZW9XaWRlbmVyV29ya2xldFByb2Nlc3Nvcik7CgogICAgZXhwb3J0cy5TdGVyZW9XaWRlbmVyV29ya2xldFByb2Nlc3NvciA9IFN0ZXJlb1dpZGVuZXJXb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD1zdGVyZW8td2lkZW5lci1idW5kbGUuanMubWFwCg==\"","export default \"data:text/javascript;base64,dmFyIHRyZW1vbG8gPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKgogICAgICogVHJlbW9sbyBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciBMRk8tZHJpdmVuIGFtcGxpdHVkZQogICAgICogbW9kdWxhdGlvbiAoYSBWQ0Egd2hvc2UgZ2FpbiBpcyBzd3VuZyBieSBhIGxvdy1mcmVxdWVuY3kgb3NjaWxsYXRvcikuCiAgICAgKgogICAgICogVHJlbW9sbyBpcyB0aGUgbW9zdCBFTEVNRU5UQVJZIG1vZHVsYXRpb24gZWZmZWN0IOKAlCBwZXItc2FtcGxlIGFtcGxpdHVkZQogICAgICogbW9kdWxhdGlvbiDigJQgYW5kIGhhcyBubyBkZWRpY2F0ZWQgcGVlci1yZXZpZXdlZCBEU1AgcGFwZXIuIFRoaXMgaW1wbGVtZW50YXRpb24KICAgICAqIGlzIGFuY2hvcmVkIGhvbmVzdGx5IHRvOgogICAgICogICAtIFN0YW5kYXJkIGFtcGxpdHVkZS1tb2R1bGF0aW9uIHRoZW9yeSBmb3IgdGhlIGdhaW4gbGF3IGFuZCBpdHMgc2lkZWJhbmQKICAgICAqICAgICBzdHJ1Y3R1cmU6IGEgY2FycmllciBhdCBmX2MgbXVsdGlwbGllZCBieSAoMSArIG0qY29zKDIqcGkqZl9tKnQpKSBnYWlucyB0d28KICAgICAqICAgICBzaWRlYmFuZHMgYXQgZl9jICsvLSBmX20gb2YgYW1wbGl0dWRlIG0vMi4KICAgICAqICAgLSBKLiBEYXR0b3JybywgIkVmZmVjdCBEZXNpZ24sIFBhcnQgMiIgKEpBRVMgMTk5NyksIHAuNzc2LCBmb3IgdGhlIHF1YWRyYXR1cmUKICAgICAqICAgICAoOTAtZGVnIHBlci1jaGFubmVsKSBzdGVyZW8gTEZPIHVzZWQgZm9yIHN0ZXJlbyB0cmVtb2xvIC8gYXV0by1wYW4uCiAgICAgKiAgIC0gQy4gTWl0Y2hlbHRyZWUsIEMuIEouIFN0ZWlubWV0eiwgTS4gQ29tdW5pdGEsIEouIEQuIFJlaXNzLCAiTW9kdWxhdGlvbgogICAgICogICAgIEV4dHJhY3Rpb24gZm9yIExGTy1kcml2ZW4gQXVkaW8gRWZmZWN0cyIgKERBRngyMyksIFNlYy4gMi4xLCBmb3IgdGhlCiAgICAgKiAgICAgTEZPLWRyaXZlbi1lZmZlY3QgZnJhbWluZyAoYW4gTEZPIGRyaXZpbmcgYSBWQ0EgZ2FpbikgYW5kIHRoZSBzdGFuZGFyZCBMRk8KICAgICAqICAgICBzaGFwZXMgKHNpbmUgLyB0cmlhbmdsZSAvIHNxdWFyZSkuIFRoZSBwYXBlcidzIHRlY2huaWNhbCBjb3JlIChuZXVyYWwgTEZPCiAgICAgKiAgICAgZXh0cmFjdGlvbikgaXMgTk9UIHVzZWQgaGVyZTsgb25seSBpdHMgZnJhbWluZy4KICAgICAqCiAgICAgKiBBTSBnYWluIGxhdyAoZyA+PSAwIGFsd2F5cyDigJQgYSB0cnVlIHRyZW1vbG8gbmV2ZXIgaW52ZXJ0cyB0aGUgc2lnbmFsIHBoYXNlOwogICAgICogYWxsb3dpbmcgZyA8IDAgd291bGQgYmUgcmluZyBtb2R1bGF0aW9uIC8gY2FycmllciBzdXBwcmVzc2lvbiwgYSBkaWZmZXJlbnQKICAgICAqIGVmZmVjdCk6CiAgICAgKiAgIGxmbyA9IHNoYXBlKHBoYXNlICsgY2hhbm5lbEluZGV4ICogc3RlcmVvUGhhc2VSYWQpICAgaW4gWy0xLCAxXQogICAgICogICB1ICAgPSAwLjUgKiAoMSArIGxmbykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlwb2xhciBbMCwgMV0KICAgICAqICAgZyAgID0gKDEgLSBkZXB0aCkgKyBkZXB0aCAqIHUgICAgICAgICAgICAgICAgICAgICAgICAgc3dpbmdzICgxLWRlcHRoKSAuLiAxCiAgICAgKiAgIHkgICA9IHggKiBnCiAgICAgKiBUaGUgZ2FpbiBpcyBjb21wdXRlZCBQRVIgU0FNUExFIGZyb20gYSBjb250aW51b3VzbHktYWR2YW5jZWQgTEZPIHBoYXNlLCBzbwogICAgICogdGhlcmUgYXJlIG5vIGJsb2NrLXJhdGUgZ2FpbiBzdGVwcyAobm8gInppcHBlciIgY2xpY2tzKS4KICAgICAqCiAgICAgKiBTdGVyZW86IGVhY2ggY2hhbm5lbCdzIGNvcmUgaXMgY29uc3RydWN0ZWQgd2l0aCBpdHMgY2hhbm5lbEluZGV4OyB0aGUgTEZPCiAgICAgKiBvZmZzZXQgY2hhbm5lbEluZGV4ICogc3RlcmVvUGhhc2VSYWQgaXMgYXBwbGllZCBMSVZFIGVhY2ggYmxvY2ssIHNvIHN0ZXJlb1BoYXNlCiAgICAgKiBpcyBhbiBhZGp1c3RhYmxlIEF1ZGlvUGFyYW0gKHVubGlrZSB0aGUgbW9kdWxhdGVkLWRlbGF5L3BoYXNlciBmaXhlZCBjaCpwaS8yCiAgICAgKiBzZWVkKS4gc3RlcmVvUGhhc2UgPSAwIC0+IG1vbm8gdHJlbW9sbyAoYm90aCBjaGFubmVscyBpbiBwaGFzZSk7IDkwIC0+CiAgICAgKiBxdWFkcmF0dXJlOyAxODAgLT4gaGFyZCBhdXRvLXBhbiAoY2hhbm5lbHMgYW50aS1waGFzZSkuCiAgICAgKgogICAgICogVGhpcyBmaWxlIGhvbGRzIE9OTFkgcHVyZSBudW1lcmljIG1hdGggKHBsYWluIG51bWJlcnMgKyBGbG9hdDMyQXJyYXkpLiBJdCBoYXMKICAgICAqIG5vIEF1ZGlvV29ya2xldCAvIGdsb2JhbCBkZXBlbmRlbmNpZXMsIHNvIGl0IGlzIHVuaXQtdGVzdGFibGUgZGlyZWN0bHk7IHRoZQogICAgICogd29ya2xldCBzaGVsbCBpbiB0cmVtb2xvLnRzIGRlbGVnYXRlcyB0byBpdC4KICAgICAqLwogICAgLyoqCiAgICAgKiBEZWZhdWx0IEF1ZGlvUGFyYW0gdmFsdWVzIGZvciB0aGUgdHJlbW9sbyB3b3JrbGV0LCBpbiB0aGUgd29ya2xldCdzIG93biB1bml0cy4KICAgICAqIFNJTkdMRSBTT1VSQ0UgT0YgVFJVVEgg4oCUIHRoZSB3b3JrbGV0J3MgYHBhcmFtZXRlckRlc2NyaXB0b3JzYCAodHJlbW9sby50cykKICAgICAqIGJ1aWxkcyBpdHMgZGVzY3JpcHRvcnMgZnJvbSB0aGlzIHRhYmxlLCBhbmQgdGVzdHMgdGhhdCBuZWVkIHRoZSBzaGlwcGVkCiAgICAgKiBkZWZhdWx0cyBpbXBvcnQgaXQgaGVyZSByYXRoZXIgdGhhbiByZS10eXBpbmcgbGl0ZXJhbHMuCiAgICAgKgogICAgICogRGVmYXVsdHM6IGEgbW9kZXJhdGUgfjUgSHosIGhhbGYtZGVwdGggc2luZSB0cmVtb2xvLCBtb25vIChzdGVyZW9QaGFzZSAwKS4KICAgICAqLwogICAgY29uc3QgVFJFTU9MT19ERUZBVUxUUyA9IHsKICAgICAgICByYXRlOiA1LAogICAgICAgIGRlcHRoOiAwLjUsCiAgICAgICAgc3RlcmVvUGhhc2U6IDAsCiAgICB9OwogICAgLyoqCiAgICAgKiBFdmFsdWF0ZSBhbiBMRk8gd2F2ZWZvcm0gYXQgYHBoYXNlYCAocmFkaWFucykg4oCUIGJpcG9sYXIsIHJhbmdlIFstMSwgMV0sCiAgICAgKiAyKnBpIHBlcmlvZGljLiBFeHBvc2VkIHB1cmUgZm9yIHRlc3RpbmcgdGhlIHdhdmVmb3JtIHNoYXBlcyBpbiBpc29sYXRpb24uCiAgICAgKiAgIC0gc2luZTogICAgIHNpbihwaGFzZSkuCiAgICAgKiAgIC0gdHJpYW5nbGU6IDAgYXQgcGhhc2UgMC9waSwgKzEgYXQgcGkvMiwgLTEgYXQgM3BpLzIgKGNvbnRpbnVvdXMsIHBlYWtzICsvLTEpLgogICAgICogICAtIHNxdWFyZTogICArMSBvbiB0aGUgZmlyc3QgaGFsZi1jeWNsZSBbMCwgcGkpLCAtMSBvbiB0aGUgc2Vjb25kIFtwaSwgMnBpKS4KICAgICAqLwogICAgZnVuY3Rpb24gbGZvU2hhcGUocGhhc2UsIG1vZGUpIHsKICAgICAgICAvLyBXcmFwIHRvIFswLCAyKnBpKSBmb3IgdGhlIHBpZWNld2lzZSBzaGFwZXMuCiAgICAgICAgbGV0IHAgPSBwaGFzZSAlICgyICogTWF0aC5QSSk7CiAgICAgICAgaWYgKHAgPCAwKQogICAgICAgICAgICBwICs9IDIgKiBNYXRoLlBJOwogICAgICAgIGlmIChtb2RlID09PSAidHJpYW5nbGUiKSB7CiAgICAgICAgICAgIC8vIFJpc2luZyAwLT4rMSBvdmVyIFswLCBwaS8yXSwgKzEtPi0xIG92ZXIgW3BpLzIsIDNwaS8yXSwgLTEtPjAgb3ZlciBbM3BpLzIsIDJwaV0uCiAgICAgICAgICAgIC8vIEVxdWl2YWxlbnQgY2xvc2VkIGZvcm0gYW5jaG9yZWQgdG8gc2luJ3MgcGVha3M6IGEgdW5pdCB0cmlhbmdsZSBpbiBwaGFzZS4KICAgICAgICAgICAgY29uc3QgdCA9IHAgLyAoMiAqIE1hdGguUEkpOyAvLyBbMCwgMSkKICAgICAgICAgICAgLy8gVHJpYW5nbGUgdGhhdCBpcyAwIGF0IHQ9MCwgKzEgYXQgdD0wLjI1LCAwIGF0IHQ9MC41LCAtMSBhdCB0PTAuNzUsIDAgYXQgdD0xLgogICAgICAgICAgICBpZiAodCA8IDAuMjUpCiAgICAgICAgICAgICAgICByZXR1cm4gNCAqIHQ7CiAgICAgICAgICAgIGlmICh0IDwgMC43NSkKICAgICAgICAgICAgICAgIHJldHVybiAyIC0gNCAqIHQ7CiAgICAgICAgICAgIHJldHVybiA0ICogdCAtIDQ7CiAgICAgICAgfQogICAgICAgIGlmIChtb2RlID09PSAic3F1YXJlIikgewogICAgICAgICAgICByZXR1cm4gcCA8IE1hdGguUEkgPyAxIDogLTE7CiAgICAgICAgfQogICAgICAgIC8vIHNpbmUKICAgICAgICByZXR1cm4gTWF0aC5zaW4ocCk7CiAgICB9CiAgICAvKioKICAgICAqIFN0YXRlZnVsIHRyZW1vbG8gcHJvY2Vzc29yIGZvciBPTkUgY2hhbm5lbC4gT3ducyBvbmx5IHRoZSBMRk8gcGhhc2UsIGFuZCBpcwogICAgICogcmV1c2VkIGJsb2NrLXRvLWJsb2NrIHNvIHRoZSBMRk8gYWR2YW5jZXMgY29udGludW91c2x5IGFjcm9zcyBwcm9jZXNzKCkgY2FsbHMKICAgICAqIOKAlCBjb25zdHJ1Y3RpbmcgYSBmcmVzaCBpbnN0YW5jZSBwZXIgYmxvY2sgd291bGQgcmVzZXQgdGhlIExGTyBwaGFzZSBhbmQgcHJvZHVjZQogICAgICogYW4gYXVkaWJsZSBkaXNjb250aW51aXR5IChzYW1lIGhhemFyZCBkb2N1bWVudGVkIGZvciB0aGUgbW9kdWxhdGVkLWRlbGF5IGFuZAogICAgICogcGhhc2VyIGNvcmVzKS4KICAgICAqCiAgICAgKiBgY2hhbm5lbEluZGV4YCBpcyBmaXhlZCBhdCBjb25zdHJ1Y3Rpb24gYW5kIGNvbWJpbmVkIHdpdGggdGhlIChsaXZlKSBzdGVyZW9QaGFzZQogICAgICogcGFyYW0gdG8gZ2l2ZSBlYWNoIGNoYW5uZWwgaXRzIHBoYXNlIG9mZnNldCwgc28gYSBzdGVyZW8gcGFpciBjYW4gYmUgcGFubmVkIGJ5CiAgICAgKiBhZGp1c3Rpbmcgc3RlcmVvUGhhc2Ugd2l0aG91dCByZWNvbnN0cnVjdGluZyB0aGUgY29yZXMuCiAgICAgKi8KICAgIGNsYXNzIFRyZW1vbG9Qcm9jZXNzb3IgewogICAgICAgIHNhbXBsZVJhdGU7CiAgICAgICAgY2hhbm5lbEluZGV4OwogICAgICAgIC8qKiBDdXJyZW50IExGTyBwaGFzZSAocmFkaWFucykuICovCiAgICAgICAgbGZvUGhhc2UgPSAwOwogICAgICAgIGNvbnN0cnVjdG9yKHNhbXBsZVJhdGUsIGNoYW5uZWxJbmRleCkgewogICAgICAgICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlOwogICAgICAgICAgICB0aGlzLmNoYW5uZWxJbmRleCA9IGNoYW5uZWxJbmRleDsKICAgICAgICB9CiAgICAgICAgLyoqIFJlc2V0IHRoZSBMRk8gcGhhc2UgdG8gaXRzIGNvbnN0cnVjdGlvbiB2YWx1ZSAoMCkuICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBQcm9jZXNzIGEgYmxvY2suIGBpbnB1dGAgYW5kIGBvdXRwdXRgIG1heSBhbGlhcyAoaW4tcGxhY2UgaXMgZmluZSkuCiAgICAgICAgICoKICAgICAgICAgKiBQZXIgc2FtcGxlIChBTSBsYXcpOgogICAgICAgICAqICAgMS4gbGZvID0gc2hhcGUobGZvUGhhc2UgKyBjaGFubmVsSW5kZXggKiBzdGVyZW9QaGFzZVJhZCkgICAoYmlwb2xhciBbLTEsIDFdKQogICAgICAgICAqICAgMi4gZyAgID0gKDEgLSBkZXB0aCkgKyBkZXB0aCAqIDAuNSAqICgxICsgbGZvKSAgICAgICAgICAgICAoaW4gWzEtZGVwdGgsIDFdKQogICAgICAgICAqICAgMy4geSAgID0geCAqIGcKICAgICAgICAgKiBUaGUgTEZPIHBoYXNlIGFkdmFuY2VzIGJ5IDIqcGkqcmF0ZS9zYW1wbGVSYXRlIGVhY2ggc2FtcGxlIChwZXItc2FtcGxlIGdhaW4sCiAgICAgICAgICogc28gbm8gemlwcGVyKS4KICAgICAgICAgKi8KICAgICAgICBwcm9jZXNzKGlucHV0LCBvdXRwdXQsIHBhcmFtcykgewogICAgICAgICAgICAvLyBrLXJhdGUtc3R5bGUgY29lZmZpY2llbnRzIGNvbXB1dGVkIG9uY2UgcGVyIGJsb2NrLgogICAgICAgICAgICBjb25zdCBkZXB0aCA9IHBhcmFtcy5kZXB0aDsKICAgICAgICAgICAgY29uc3Qgc2hhcGUgPSBwYXJhbXMuc2hhcGU7CiAgICAgICAgICAgIGNvbnN0IHBoYXNlSW5jID0gKDIgKiBNYXRoLlBJICogcGFyYW1zLnJhdGUpIC8gdGhpcy5zYW1wbGVSYXRlOwogICAgICAgICAgICBjb25zdCBjaGFubmVsT2Zmc2V0ID0gdGhpcy5jaGFubmVsSW5kZXggKiAoKHBhcmFtcy5zdGVyZW9QaGFzZSAqIE1hdGguUEkpIC8gMTgwKTsKICAgICAgICAgICAgY29uc3QgbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBsZm8gPSBsZm9TaGFwZSh0aGlzLmxmb1BoYXNlICsgY2hhbm5lbE9mZnNldCwgc2hhcGUpOwogICAgICAgICAgICAgICAgLy8gZyA+PSAwIGFsd2F5czogdSA9IDAuNSooMStsZm8pIGluIFswLDFdLCBnID0gKDEtZGVwdGgpICsgZGVwdGgqdS4KICAgICAgICAgICAgICAgIGNvbnN0IGcgPSAxIC0gZGVwdGggKyBkZXB0aCAqIDAuNSAqICgxICsgbGZvKTsKICAgICAgICAgICAgICAgIG91dHB1dFtpXSA9IGlucHV0W2ldICogZzsKICAgICAgICAgICAgICAgIHRoaXMubGZvUGhhc2UgKz0gcGhhc2VJbmM7CiAgICAgICAgICAgICAgICBpZiAodGhpcy5sZm9QaGFzZSA+PSAyICogTWF0aC5QSSkKICAgICAgICAgICAgICAgICAgICB0aGlzLmxmb1BoYXNlIC09IDIgKiBNYXRoLlBJOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBUcmVtb2xvIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMIERTUAogICAgICogbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFRyZW1vbG9Qcm9jZXNzb3IgaW4gdHJlbW9sby1jb3JlLnRzLiBNaXJyb3JzIHRoZQogICAgICogbW9kdWxhdGVkLWRlbGF5LnRzIC8gcGhhc2VyLnRzIGNvcmUvc2hlbGwgc3BsaXQ6IHRoaXMgZmlsZSBvd25zIG9ubHkgdGhlCiAgICAgKiB3b3JrbGV0IHBsdW1iaW5nIChwYXJhbWV0ZXJEZXNjcmlwdG9ycywgcHJvY2VzcygpLCB0aGUgcmVnaXN0ZXJQcm9jZXNzb3IKICAgICAqIGNhbGwpOyB0aGUgYWxnb3JpdGhtIGxpdmVzIGluIHRoZSB1bml0LXRlc3RlZCBjb3JlLgogICAgICoKICAgICAqIEFsZ29yaXRobTogTEZPLWRyaXZlbiBhbXBsaXR1ZGUgbW9kdWxhdGlvbiAoYSBWQ0Egc3d1bmcgYnkgYSBsb3ctZnJlcXVlbmN5CiAgICAgKiBvc2NpbGxhdG9yKS4gU2VlIHRyZW1vbG8tY29yZS50cyBoZWFkZXIgZm9yIHRoZSBBTSBnYWluIGxhdywgaXRzIHNpZGViYW5kCiAgICAgKiBzdHJ1Y3R1cmUsIHRoZSBob25lc3QgcGFwZXIgYW5jaG9yaW5nIChBTSB0aGVvcnkgKyBEYXR0b3JybyAxOTk3IHAuNzc2CiAgICAgKiBxdWFkcmF0dXJlIExGTyArIE1pdGNoZWx0cmVlIGV0IGFsLiBEQUZ4MjMgTEZPIGZyYW1pbmcpLCBhbmQgdGhlIHBlci1zYW1wbGUKICAgICAqICh6aXBwZXItZnJlZSkgZ2Fpbi4KICAgICAqLwogICAgY29uc3QgV09SS0xFVF9MT0dfUFJFRklYID0gIltjYWNvcGhvbnkvd29ya2xldDp0cmVtb2xvXSI7CiAgICAvKioKICAgICAqIFNoYXBlIHNlbGVjdGlvbiByaWRlcyBvbiBhbiBBdWRpb1BhcmFtICh3aGljaCBjYW4gb25seSBjYXJyeSBudW1iZXJzKSwgc28gdGhlCiAgICAgKiAic2hhcGUiIHBhcmFtIGlzIGFuIGVudW0gaW5kZXg6IDAgPSBzaW5lLCAxID0gdHJpYW5nbGUsIDIgPSBzcXVhcmUuCiAgICAgKi8KICAgIGNvbnN0IFNIQVBFX0JZX0lOREVYID0gWyJzaW5lIiwgInRyaWFuZ2xlIiwgInNxdWFyZSJdOwogICAgZnVuY3Rpb24gc2hhcGVGcm9tSW5kZXgoaW5kZXgpIHsKICAgICAgICBjb25zdCBpID0gTWF0aC5yb3VuZChpbmRleCk7CiAgICAgICAgcmV0dXJuIFNIQVBFX0JZX0lOREVYW2ldID8/ICJzaW5lIjsKICAgIH0KICAgIGNsYXNzIFRyZW1vbG9Xb3JrbGV0UHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICAgICAgICAvLyBPbmUgc3RhdGVmdWwgY29yZSBwZXIgY2hhbm5lbCBzbyBlYWNoIGNoYW5uZWwga2VlcHMgaXRzIG93biBMRk8gcGhhc2UgYWNyb3NzCiAgICAgICAgLy8gcHJvY2VzcygpIGJsb2Nrcy4gRWFjaCBjb3JlIGlzIGNvbnN0cnVjdGVkIHdpdGggaXRzIGNoYW5uZWxJbmRleCBzbyB0aGUKICAgICAgICAvLyAobGl2ZSkgc3RlcmVvUGhhc2UgcGFyYW0gY2FuIHBhbiB0aGUgY2hhbm5lbHMgYXBhcnQgKERhdHRvcnJvIHAuNzc2KS4KICAgICAgICBjb3JlcyA9IFtdOwogICAgICAgIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICAgICAgICAgIC8vIERlZmF1bHQgVkFMVUVTIGNvbWUgZnJvbSBUUkVNT0xPX0RFRkFVTFRTICh0cmVtb2xvLWNvcmUudHMpIOKAlCB0aGUgc2luZ2xlCiAgICAgICAgICAgIC8vIHNvdXJjZSBvZiB0cnV0aCBzaGFyZWQgd2l0aCB0aGUgY29yZSB0ZXN0cy4gUmFuZ2VzIGxpdmUgaGVyZS4gYHNoYXBlYCBpcyBhbgogICAgICAgICAgICAvLyBlbnVtIGluZGV4ICgwPXNpbmUsIDE9dHJpYW5nbGUsIDI9c3F1YXJlKTsgc3RlcmVvUGhhc2UgaXMgaW4gZGVncmVlcy4KICAgICAgICAgICAgcmV0dXJuIFsKICAgICAgICAgICAgICAgIFsicmF0ZSIsIFRSRU1PTE9fREVGQVVMVFMucmF0ZSwgMCwgMjAsICJrLXJhdGUiXSwKICAgICAgICAgICAgICAgIFsiZGVwdGgiLCBUUkVNT0xPX0RFRkFVTFRTLmRlcHRoLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbInNoYXBlIiwgMCwgMCwgMiwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJzdGVyZW9QaGFzZSIsIFRSRU1PTE9fREVGQVVMVFMuc3RlcmVvUGhhc2UsIDAsIDE4MCwgImstcmF0ZSJdLAogICAgICAgICAgICBdLm1hcCgoW25hbWUsIGRlZmF1bHRWYWx1ZSwgbWluVmFsdWUsIG1heFZhbHVlLCBhdXRvbWF0aW9uUmF0ZV0pID0+ICh7CiAgICAgICAgICAgICAgICBuYW1lOiBuYW1lLAogICAgICAgICAgICAgICAgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUsCiAgICAgICAgICAgICAgICBtaW5WYWx1ZTogbWluVmFsdWUsCiAgICAgICAgICAgICAgICBtYXhWYWx1ZTogbWF4VmFsdWUsCiAgICAgICAgICAgICAgICBhdXRvbWF0aW9uUmF0ZTogYXV0b21hdGlvblJhdGUsCiAgICAgICAgICAgIH0pKTsKICAgICAgICB9CiAgICAgICAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgICAgICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF07CiAgICAgICAgICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICAgICAgICAgIGlmICghaW5wdXQgfHwgaW5wdXQubGVuZ3RoID09PSAwIHx8ICFvdXRwdXQgfHwgb3V0cHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gay1yYXRlIHBhcmFtczogdGFrZSB0aGUgZmlyc3QgKGNvbnN0YW50LW92ZXItYmxvY2spIHZhbHVlLgogICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7CiAgICAgICAgICAgICAgICByYXRlOiBwYXJhbWV0ZXJzLnJhdGVbMF0sCiAgICAgICAgICAgICAgICBkZXB0aDogcGFyYW1ldGVycy5kZXB0aFswXSwKICAgICAgICAgICAgICAgIHNoYXBlOiBzaGFwZUZyb21JbmRleChwYXJhbWV0ZXJzLnNoYXBlWzBdKSwKICAgICAgICAgICAgICAgIHN0ZXJlb1BoYXNlOiBwYXJhbWV0ZXJzLnN0ZXJlb1BoYXNlWzBdLAogICAgICAgICAgICB9OwogICAgICAgICAgICBjb25zdCBjaGFubmVsQ291bnQgPSBNYXRoLm1pbihpbnB1dC5sZW5ndGgsIG91dHB1dC5sZW5ndGgpOwogICAgICAgICAgICBmb3IgKGxldCBjaCA9IDA7IGNoIDwgY2hhbm5lbENvdW50OyBjaCsrKSB7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuY29yZXNbY2hdKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gUGFzcyB0aGUgY2hhbm5lbCBpbmRleCBzbyBzdGVyZW9QaGFzZSBvZmZzZXRzIHRoZSBwZXItY2hhbm5lbCBMRk8KICAgICAgICAgICAgICAgICAgICAvLyBsaXZlIChxdWFkcmF0dXJlL2F1dG8tcGFuIHN0ZXJlbyBmaWVsZCwgRGF0dG9ycm8gcC43NzYpLgogICAgICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdID0gbmV3IFRyZW1vbG9Qcm9jZXNzb3Ioc2FtcGxlUmF0ZSwgY2gpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5jb3Jlc1tjaF0ucHJvY2VzcyhpbnB1dFtjaF0sIG91dHB1dFtjaF0sIHBhcmFtcyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgfQogICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gbW9kdWxlIGV2YWx1YXRpbmdgKTsKICAgIHRyeSB7CiAgICAgICAgcmVnaXN0ZXJQcm9jZXNzb3IoInRyZW1vbG8iLCBUcmVtb2xvV29ya2xldFByb2Nlc3Nvcik7CiAgICAgICAgY29uc29sZS5pbmZvKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgY29tcGxldGVgKTsKICAgIH0KICAgIGNhdGNoIChlcnJvcikgewogICAgICAgIGNvbnNvbGUuZXJyb3IoYCR7V09SS0xFVF9MT0dfUFJFRklYfSByZWdpc3RlclByb2Nlc3NvciBmYWlsZWRgLCBlcnJvcik7CiAgICAgICAgdGhyb3cgZXJyb3I7CiAgICB9CgogICAgZXhwb3J0cy5UcmVtb2xvV29ya2xldFByb2Nlc3NvciA9IFRyZW1vbG9Xb3JrbGV0UHJvY2Vzc29yOwoKICAgIHJldHVybiBleHBvcnRzOwoKfSkoe30pOwovLyMgc291cmNlTWFwcGluZ1VSTD10cmVtb2xvLWJ1bmRsZS5qcy5tYXAK\"","export default \"data:text/javascript;base64,dmFyIHdhdmVzaGFwZXIgPSAoZnVuY3Rpb24gKGV4cG9ydHMpIHsKICAgICd1c2Ugc3RyaWN0JzsKCiAgICAvKgogICAgICogV2F2ZXNoYXBlciBwcm9jZXNzb3IgY29yZSDigJQgY29udGV4dC1mcmVlIERTUCBtYXRoIGZvciBhbiBhbnRpYWxpYXNlZAogICAgICogd2F2ZXNoYXBlciAvIGRpc3RvcnRpb24gdXNpbmcgZmlyc3Qtb3JkZXIgQW50aWRlcml2YXRpdmUgQW50aWFsaWFzaW5nIChBREFBKS4KICAgICAqCiAgICAgKiBJbXBsZW1lbnRzIHRoZSBmaXJzdC1vcmRlciAocmVjdGFuZ3VsYXIta2VybmVsKSBtZXRob2QgZnJvbToKICAgICAqICAgSi4gRC4gUGFya2VyLCBWLiBaYXZhbGlzaGluLCBFLiBMZSBCaXZpYywKICAgICAqICAgIlJlZHVjaW5nIHRoZSBBbGlhc2luZyBvZiBOb25saW5lYXIgV2F2ZXNoYXBpbmcgVXNpbmcgQ29udGludW91cy1UaW1lCiAgICAgKiAgICBDb252b2x1dGlvbiIsIFByb2MuIERBRngtMTYsIEJybm8sIEN6ZWNoIFJlcHVibGljLCBTZXB0LiAyMDE2LCBwcC4gMTM3LTE0NC4KICAgICAqCiAgICAgKiBDb3JlIGlkZWEgKFBhcmtlciAyMDE2LCBlcS45KTogcmF0aGVyIHRoYW4gb3ZlcnNhbXBsZSBhIG1lbW9yeWxlc3MKICAgICAqIG5vbmxpbmVhcml0eSB5W25dID0gZih4W25dKSAod2hvc2UgaGFybW9uaWNzIGZvbGQgYmFjayBhcyBhbGlhc2luZyksCiAgICAgKiByZWNvbnN0cnVjdCB0aGUgaW5wdXQgYnkgbGluZWFyIGludGVycG9sYXRpb24sIGFwcGx5IGYgaW4gY29udGludW91cyB0aW1lLAogICAgICogYW5kIGNvbnZvbHZlIHdpdGggYSB1bml0LXdpZHRoIHJlY3Rhbmd1bGFyIGxvd3Bhc3Mga2VybmVsLiBUaGF0IGFuYWx5dGljCiAgICAgKiBjb252b2x1dGlvbiBjb2xsYXBzZXMgdG8gYSBmaXJzdC1vcmRlciBkaWZmZXJlbmNlIG9mIGYncyBhbnRpZGVyaXZhdGl2ZSBGMDoKICAgICAqCiAgICAgKiAgICAgeVtuXSA9IChGMCh4X24pIC0gRjAoeF97bi0xfSkpIC8gKHhfbiAtIHhfe24tMX0pICAgICAgICAgICAgICAoZXEuOSkKICAgICAqCiAgICAgKiB3aGVyZSBGMCcgPSBmIGFuZCB0aGUgaW50ZWdyYXRpb24gY29uc3RhbnQgaXMgY2hvc2VuIHNvIEYwKDApID0gMCAodGhpcwogICAgICogbG9jYWxpemVzIGZpbml0ZS1wcmVjaXNpb24gbG9zcyB0byB0aGUgZGVub21pbmF0b3Igb25seTsgUGFya2VyIDIwMTYgcC4yLTMpLgogICAgICoKICAgICAqIFRoZSBlcS45IHF1b3RpZW50IGlzIDAvMCB3aGVuIHhfbiB+PSB4X3tuLTF9IChjYXRhc3Ryb3BoaWMgY2FuY2VsbGF0aW9uIGluCiAgICAgKiB0aGUgbnVtZXJhdG9yLCBkaXZpc2lvbiBieSB+MCBpbiB0aGUgZGVub21pbmF0b3IpLiBQYXJrZXIgMjAxNiBlcS4xMCBnaXZlcwogICAgICogdGhlIHdlbGwtY29uZGl0aW9uZWQgbGltaXQgdmlhIHRoZSBtZWFuIHZhbHVlIHRoZW9yZW06CiAgICAgKgogICAgICogICAgIChGMCh4X24pIC0gRjAoeF97bi0xfSkpIC8gKHhfbiAtIHhfe24tMX0pCiAgICAgKiAgICAgICAgIC0+IGYoKHhfbiArIHhfe24tMX0pIC8gMikgKyBPKCh4X24gLSB4X3tuLTF9KV4yKSAgICAgICAgICAoZXEuMTApCiAgICAgKgogICAgICogaS5lLiB3aGVuIHx4X24gLSB4X3tuLTF9fCA8IGVwcywgZmFsbCBiYWNrIHRvIGV2YWx1YXRpbmcgZiBhdCB0aGUgbWlkcG9pbnQKICAgICAqIG9mIHRoZSB0d28gY29uc2VjdXRpdmUgc2FtcGxlcy4gVGhpcyBhbHNvIG1ha2VzIGEgY29uc3RhbnQgaW5wdXQgcHJvZHVjZQogICAgICogZih4KSBleGFjdGx5LgogICAgICoKICAgICAqIEdST1VQIERFTEFZOiBmaXJzdC1vcmRlciBBREFBIGlzIGVxdWl2YWxlbnQgKGF0IGxvdyBzaWduYWwgbGV2ZWxzLCBmKHgpfj14KQogICAgICogdG8geVtuXSA9ICh4X24gKyB4X3tuLTF9KS8yLCBhIDAuNS1zYW1wbGUgZnJhY3Rpb25hbCBkZWxheSAoUGFya2VyIDIwMTYgZXEuMTcsCiAgICAgKiBwLjMpLiBDYWxsZXJzIHBsYWNpbmcgdGhpcyBpbiBhIGZlZWRiYWNrIGxvb3AgbXVzdCBjb21wZW5zYXRlIGZvciB0aGF0CiAgICAgKiBpbmhlcmVudCBoYWxmLXNhbXBsZSBncm91cCBkZWxheTsgaW4gYSBwbGFpbiBmb3J3YXJkIGVmZmVjdHMgY2hhaW4gaXQgaXMKICAgICAqIGhhcm1sZXNzLiBUaGUgbWV0aG9kIGFsc28gaW1wb3NlcyBhIG1pbGQgSEYgcm9sbG9mZiAobm9uLWJyaWNrd2FsbCBrZXJuZWwpLgogICAgICoKICAgICAqIFRoaXMgZmlsZSBob2xkcyBPTkxZIHB1cmUgbnVtZXJpYyBtYXRoIChwbGFpbiBudW1iZXJzICsgRmxvYXQzMkFycmF5KS4gSXQgaGFzCiAgICAgKiBubyBBdWRpb1dvcmtsZXQgLyBnbG9iYWwgZGVwZW5kZW5jaWVzLCBzbyBpdCBpcyB1bml0LXRlc3RhYmxlIGRpcmVjdGx5OyB0aGUKICAgICAqIHdvcmtsZXQgc2hlbGwgaW4gd2F2ZXNoYXBlci50cyBkZWxlZ2F0ZXMgdG8gaXQuCiAgICAgKi8KICAgIC8qKgogICAgICogVGhyZXNob2xkIG9uIHx4X24gLSB4X3tuLTF9fCBiZWxvdyB3aGljaCBlcS45IGlzIGlsbC1jb25kaXRpb25lZCBhbmQgd2UgdXNlCiAgICAgKiB0aGUgZXEuMTAgbWlkcG9pbnQgZmFsbGJhY2suIFNtYWxsIHJlbGF0aXZlIHRvIHR5cGljYWwgZHJpdmUgbGV2ZWxzIGJ1dCBsYXJnZQogICAgICogZW5vdWdoIHRvIHN0YXkgY2xlYXIgb2YgY2F0YXN0cm9waGljIGNhbmNlbGxhdGlvbiBpbiBzaW5nbGUgcHJlY2lzaW9uLgogICAgICovCiAgICBjb25zdCBBREFBX0VQUyA9IDFlLTU7CiAgICAvLyAtLS0gdGFuaCBub25saW5lYXJpdHkgKFBhcmtlciAyMDE2IFNlYyA1LjEsIGVxcyAxOS0yMCkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAvKiogZih4KSA9IHRhbmgoeCkgIChQYXJrZXIgMjAxNiBlcS4xOSkuIEJvdW5kZWQsIG1vbm90b25lLCBvZGQ7IHJhbmdlICgtMSwgMSkuICovCiAgICBmdW5jdGlvbiBmVGFuaCh4KSB7CiAgICAgICAgcmV0dXJuIE1hdGgudGFuaCh4KTsKICAgIH0KICAgIC8qKgogICAgICogRjAoeCkgPSBsb2coY29zaCh4KSkgIChQYXJrZXIgMjAxNiBlcS4yMCksIHRoZSBhbnRpZGVyaXZhdGl2ZSBvZiB0YW5oIHdpdGgKICAgICAqIEYwKDApID0gbG9nKGNvc2goMCkpID0gbG9nKDEpID0gMCwgc28gdGhlIEYwKDApPTAgY29uc3RhbnQgY29uZGl0aW9uIGhvbGRzLgogICAgICoKICAgICAqIENvbXB1dGVkIGluIHRoZSBvdmVyZmxvdy1zdGFibGUgZm9ybQogICAgICogICAgIGxvZyhjb3NoKHgpKSA9IHx4fCArIGxvZygoMSArIGVeey0yfHh8fSkgLyAyKQogICAgICogd2hpY2ggYXZvaWRzIGNvc2goeCkgb3ZlcmZsb3dpbmcgdG8gK0luZiBmb3IgbGFyZ2UgfHh8IChhIG5haXZlCiAgICAgKiBNYXRoLmxvZyhNYXRoLmNvc2goeCkpIHJldHVybnMgSW5mIHBhc3QgfHh8IH49IDcxMCkuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGYwVGFuaCh4KSB7CiAgICAgICAgY29uc3QgYXggPSBNYXRoLmFicyh4KTsKICAgICAgICByZXR1cm4gYXggKyBNYXRoLmxvZygoMSArIE1hdGguZXhwKC0yICogYXgpKSAvIDIpOwogICAgfQogICAgLy8gLS0tIGhhcmQgY2xpcHBlciBub25saW5lYXJpdHkgKFBhcmtlciAyMDE2IFNlYyA1LjIsIGVxcyAyNC0yNSkgLS0tLS0tLS0tLS0tLQogICAgLyoqCiAgICAgKiBmKHgpID0geCBmb3IgLTEgPD0geCA8PSAxLCBlbHNlIHNnbih4KSAgKFBhcmtlciAyMDE2IGVxLjI0KS4gU2F0dXJhdGVzIHRvCiAgICAgKiArLy0xIGJleW9uZCB0aGUgdW5pdCB0aHJlc2hvbGQuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGZIYXJkQ2xpcCh4KSB7CiAgICAgICAgaWYgKHggPiAxKQogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICBpZiAoeCA8IC0xKQogICAgICAgICAgICByZXR1cm4gLTE7CiAgICAgICAgcmV0dXJuIHg7CiAgICB9CiAgICAvKioKICAgICAqIEYwKHgpLCBhbnRpZGVyaXZhdGl2ZSBvZiB0aGUgaGFyZCBjbGlwcGVyIChQYXJrZXIgMjAxNiBlcS4yNSk6CiAgICAgKiAgICAgRjAoeCkgPSAoMS8yKSB4XjIgICAgICAgICAgICAgICAgICBmb3IgLTEgPD0geCA8PSAxCiAgICAgKiAgICAgRjAoeCkgPSB4KnNnbih4KSAtIDEvMiAgPSB8eHwgLSAxLzIgICBvdGhlcndpc2UKICAgICAqIFRoZSBpbnRlZ3JhdGlvbiBjb25zdGFudCBpcyBjaG9zZW4gc28gRjAoMCkgPSAwIGFuZCBGMCBpcyBjb250aW51b3VzIGF0CiAgICAgKiB4ID0gKy8tMSAoYm90aCBicmFuY2hlcyBnaXZlIDEvMiB0aGVyZSksIHBlciBQYXJrZXIgMjAxNiBwLjQuCiAgICAgKi8KICAgIGZ1bmN0aW9uIGYwSGFyZENsaXAoeCkgewogICAgICAgIGNvbnN0IGF4ID0gTWF0aC5hYnMoeCk7CiAgICAgICAgaWYgKGF4IDw9IDEpCiAgICAgICAgICAgIHJldHVybiAwLjUgKiB4ICogeDsKICAgICAgICByZXR1cm4gYXggLSAwLjU7CiAgICB9CiAgICAvKiogUmVzb2x2ZSBhIHNoYXBlIG5hbWUgdG8gaXRzIG1hdGNoZWQgKGYsIEYwKSBwYWlyLiAqLwogICAgZnVuY3Rpb24gbm9ubGluZWFyaXR5KHNoYXBlKSB7CiAgICAgICAgaWYgKHNoYXBlID09PSAidGFuaCIpCiAgICAgICAgICAgIHJldHVybiB7IGY6IGZUYW5oLCBmMDogZjBUYW5oIH07CiAgICAgICAgcmV0dXJuIHsgZjogZkhhcmRDbGlwLCBmMDogZjBIYXJkQ2xpcCB9OwogICAgfQogICAgLyoqCiAgICAgKiBTdGF0ZWZ1bCBmaXJzdC1vcmRlciBBREFBIHdhdmVzaGFwZXIgZm9yIE9ORSBjaGFubmVsLiBIb2xkcyB0aGUgcHJldmlvdXMKICAgICAqIGlucHV0IHNhbXBsZSB4X3tuLTF9IGFuZCBpdHMgYW50aWRlcml2YXRpdmUgdmFsdWUgRjAoeF97bi0xfSkgYWNyb3NzCiAgICAgKiBwcm9jZXNzKCkgYmxvY2tzIHNvIHRoZSBlcS45IGRpZmZlcmVuY2UgaXMgY29udGludW91cyBvdmVyIGJsb2NrIGJvdW5kYXJpZXMuCiAgICAgKi8KICAgIGNsYXNzIFdhdmVzaGFwZXJQcm9jZXNzb3IgewogICAgICAgIC8qKiB4X3tuLTF9OiBwcmV2aW91cyAoZHJpdmVuKSBpbnB1dCBzYW1wbGUuICovCiAgICAgICAgeFByZXYgPSAwOwogICAgICAgIC8qKiBGMCh4X3tuLTF9KTogcHJldmlvdXMgYW50aWRlcml2YXRpdmUgdmFsdWUsIGNhY2hlZCBzbyBvbmx5IG9uZSBGMCBldmFsIHBlciBzYW1wbGUuICovCiAgICAgICAgZjBQcmV2ID0gMDsKICAgICAgICAvKiogU2hhcGUgd2hvc2UgRjAgY2FjaGUgKGYwUHJldikgaXMgY3VycmVudGx5IHZhbGlkOyByZS1wcmltZXMgZjBQcmV2IG9uIGNoYW5nZS4gKi8KICAgICAgICBjYWNoZWRTaGFwZSA9ICJoYXJkY2xpcCI7CiAgICAgICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgICAgIC8vIGYwUHJldiA9IEYwKDApID0gMCBmb3IgYm90aCBzaGFwZXMgKEYwKDApPTAgYnkgY29uc3RydWN0aW9uKS4KICAgICAgICAgICAgdGhpcy5mMFByZXYgPSAwOwogICAgICAgIH0KICAgICAgICAvKioKICAgICAgICAgKiBBcHBseSBmaXJzdC1vcmRlciBBREFBIHdhdmVzaGFwaW5nIHRvIGBpbnB1dGAsIHdyaXRpbmcgdG8gYG91dHB1dGAuCiAgICAgICAgICogYGlucHV0YCBhbmQgYG91dHB1dGAgbWF5IGJlIHRoZSBzYW1lIEZsb2F0MzJBcnJheSAoaW4tcGxhY2Ugc2FmZSDigJQgdGhlCiAgICAgICAgICogZHJpdmVuIHNhbXBsZSBpcyByZWFkIGludG8gYSBsb2NhbCBiZWZvcmUgb3V0cHV0W2ldIGlzIHdyaXR0ZW4pLgogICAgICAgICAqLwogICAgICAgIHByb2Nlc3MoaW5wdXQsIG91dHB1dCwgcGFyYW1zKSB7CiAgICAgICAgICAgIGNvbnN0IHsgZiwgZjAgfSA9IG5vbmxpbmVhcml0eShwYXJhbXMuc2hhcGUpOwogICAgICAgICAgICBjb25zdCBkcml2ZSA9IHBhcmFtcy5kcml2ZTsKICAgICAgICAgICAgY29uc3Qgb3V0ID0gcGFyYW1zLm91dHB1dDsKICAgICAgICAgICAgY29uc3QgbWl4ID0gcGFyYW1zLm1peDsKICAgICAgICAgICAgLy8gSWYgdGhlIHNoYXBlIGNoYW5nZWQgc2luY2UgdGhlIGxhc3QgYmxvY2ssIHRoZSBjYWNoZWQgRjAoeF97bi0xfSkgYmVsb25ncwogICAgICAgICAgICAvLyB0byB0aGUgb2xkIGFudGlkZXJpdmF0aXZlIOKAlCByZS1ldmFsdWF0ZSBpdCB1bmRlciB0aGUgbmV3IHNoYXBlIHNvIGVxLjkgaXMKICAgICAgICAgICAgLy8gY29uc2lzdGVudC4KICAgICAgICAgICAgaWYgKHBhcmFtcy5zaGFwZSAhPT0gdGhpcy5jYWNoZWRTaGFwZSkgewogICAgICAgICAgICAgICAgdGhpcy5mMFByZXYgPSBmMCh0aGlzLnhQcmV2KTsKICAgICAgICAgICAgICAgIHRoaXMuY2FjaGVkU2hhcGUgPSBwYXJhbXMuc2hhcGU7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgbiA9IE1hdGgubWluKGlucHV0Lmxlbmd0aCwgb3V0cHV0Lmxlbmd0aCk7CiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgICAgICAgICBjb25zdCBkcnkgPSBpbnB1dFtpXTsKICAgICAgICAgICAgICAgIGNvbnN0IHhuID0gZHJ5ICogZHJpdmU7IC8vIHByZS1nYWluIChkcml2ZSkgaW50byB0aGUgbm9ubGluZWFyaXR5CiAgICAgICAgICAgICAgICBjb25zdCBmMG4gPSBmMCh4bik7IC8vIHNpbmdsZSBGMCBldmFsdWF0aW9uIHBlciBzYW1wbGUgKFBhcmtlciAyMDE2IHAuMikKICAgICAgICAgICAgICAgIGNvbnN0IGRlbm9tID0geG4gLSB0aGlzLnhQcmV2OwogICAgICAgICAgICAgICAgbGV0IHNoYXBlZDsKICAgICAgICAgICAgICAgIGlmIChNYXRoLmFicyhkZW5vbSkgPCBBREFBX0VQUykgewogICAgICAgICAgICAgICAgICAgIC8vIGVxLjEwIG1pZHBvaW50IGZhbGxiYWNrOiBlcS45IGlzIDAvMCBoZXJlOyB0aGUgd2VsbC1jb25kaXRpb25lZAogICAgICAgICAgICAgICAgICAgIC8vIGxpbWl0IGlzIGYgYXQgdGhlIG1pZHBvaW50IG9mIHRoZSB0d28gY29uc2VjdXRpdmUgc2FtcGxlcy4KICAgICAgICAgICAgICAgICAgICBzaGFwZWQgPSBmKCh4biArIHRoaXMueFByZXYpIC8gMik7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvLyBlcS45IGZpcnN0LW9yZGVyIEFEQUE6IGRpZmZlcmVuY2Ugb2YgdGhlIGFudGlkZXJpdmF0aXZlIG92ZXIgdGhlCiAgICAgICAgICAgICAgICAgICAgLy8gaW5wdXQgaW5jcmVtZW50LgogICAgICAgICAgICAgICAgICAgIHNoYXBlZCA9IChmMG4gLSB0aGlzLmYwUHJldikgLyBkZW5vbTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIC8vIGFkdmFuY2UgcGVyLWNoYW5uZWwgQURBQSBzdGF0ZQogICAgICAgICAgICAgICAgdGhpcy54UHJldiA9IHhuOwogICAgICAgICAgICAgICAgdGhpcy5mMFByZXYgPSBmMG47CiAgICAgICAgICAgICAgICAvLyB3ZXQvZHJ5IG1peCwgdGhlbiBvdXRwdXQgZ2FpbgogICAgICAgICAgICAgICAgb3V0cHV0W2ldID0gKG1peCAqIHNoYXBlZCArICgxIC0gbWl4KSAqIGRyeSkgKiBvdXQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLyoqIFJlc2V0IHBlci1jaGFubmVsIEFEQUEgaGlzdG9yeSAoeF97bi0xfSwgRjAoeF97bi0xfSkpIHRvIHRoZSBvcmlnaW4uICovCiAgICAgICAgcmVzZXQoKSB7CiAgICAgICAgICAgIHRoaXMueFByZXYgPSAwOwogICAgICAgICAgICB0aGlzLmYwUHJldiA9IDA7CiAgICAgICAgfQogICAgfQoKICAgIC8qCiAgICAgKiBXYXZlc2hhcGVyIEF1ZGlvV29ya2xldCBzaGVsbCDigJQgdGhpbiBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgdGhhdCBkZWxlZ2F0ZXMgQUxMCiAgICAgKiBEU1AgbWF0aCB0byB0aGUgY29udGV4dC1mcmVlIFdhdmVzaGFwZXJQcm9jZXNzb3IgaW4gd2F2ZXNoYXBlci1jb3JlLnRzLgogICAgICogTWlycm9ycyB0aGUgZHluYW1pY3MudHMgLyBkYXR0b3Jyby1yZXZlcmIudHMgY29yZS9zaGVsbCBzcGxpdDogdGhpcyBmaWxlIG93bnMKICAgICAqIG9ubHkgdGhlIHdvcmtsZXQgcGx1bWJpbmcgKHBhcmFtZXRlckRlc2NyaXB0b3JzLCBwcm9jZXNzKCksIHRoZQogICAgICogcmVnaXN0ZXJQcm9jZXNzb3IgY2FsbCk7IHRoZSBhbGdvcml0aG0gbGl2ZXMgaW4gdGhlIHVuaXQtdGVzdGVkIGNvcmUuCiAgICAgKgogICAgICogQWxnb3JpdGhtOiBhbnRpYWxpYXNlZCB3YXZlc2hhcGluZyAvIGRpc3RvcnRpb24gdmlhIGZpcnN0LW9yZGVyIEFudGlkZXJpdmF0aXZlCiAgICAgKiBBbnRpYWxpYXNpbmcgKEFEQUEpLCBQYXJrZXIsIFphdmFsaXNoaW4gJiBMZSBCaXZpYyAyMDE2IChEQUZ4LTE2KS4gU2VlCiAgICAgKiB3YXZlc2hhcGVyLWNvcmUudHMgaGVhZGVyIGZvciBlcS45ICh0aGUgQURBQSBkaWZmZXJlbmNlKSwgZXEuMTAgKHRoZSBtaWRwb2ludAogICAgICogc2luZ3VsYXJpdHkgZmFsbGJhY2spLCB0aGUgRjAgYW50aWRlcml2YXRpdmVzLCBhbmQgdGhlIGluaGVyZW50IDAuNS1zYW1wbGUKICAgICAqIGdyb3VwIGRlbGF5LgogICAgICovCiAgICBjb25zdCBXT1JLTEVUX0xPR19QUkVGSVggPSAiW2NhY29waG9ueS93b3JrbGV0OndhdmVzaGFwZXJdIjsKICAgIC8qKgogICAgICogU2hhcGUgc2VsZWN0aW9uIHJpZGVzIG9uIGFuIEF1ZGlvUGFyYW0gKHdoaWNoIGNhbiBvbmx5IGNhcnJ5IG51bWJlcnMpLCBzbyB0aGUKICAgICAqICJzaGFwZSIgcGFyYW0gaXMgYW4gZW51bSBpbmRleDogMCA9IGhhcmRjbGlwIChwb2x5bm9taWFsIEYwLCBQYXJrZXIgMjAxNgogICAgICogZXEuMjUpLCAxID0gdGFuaCBzb2Z0IGNsaXAgKEYwID0gbG9nIGNvc2gsIFBhcmtlciAyMDE2IGVxLjIwKS4KICAgICAqLwogICAgY29uc3QgU0hBUEVfQllfSU5ERVggPSBbImhhcmRjbGlwIiwgInRhbmgiXTsKICAgIGZ1bmN0aW9uIHNoYXBlRnJvbUluZGV4KGluZGV4KSB7CiAgICAgICAgY29uc3QgaSA9IE1hdGgucm91bmQoaW5kZXgpOwogICAgICAgIHJldHVybiBTSEFQRV9CWV9JTkRFWFtpXSA/PyAiaGFyZGNsaXAiOwogICAgfQogICAgY2xhc3MgV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogICAgICAgIC8vIE9uZSBzdGF0ZWZ1bCBBREFBIGNvcmUgcGVyIGNoYW5uZWwgc28gZWFjaCBjaGFubmVsIGtlZXBzIGl0cyBvd24KICAgICAgICAvLyB4X3tuLTF9IC8gRjAoeF97bi0xfSkgaGlzdG9yeSAoUGFya2VyIDIwMTYgZXEuOSkgYWNyb3NzIHByb2Nlc3MoKSBibG9ja3MuCiAgICAgICAgY29yZXMgPSBbXTsKICAgICAgICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewogICAgICAgICAgICAvLyBkcml2ZTogcHJlLWdhaW4gaW50byB0aGUgbm9ubGluZWFyaXR5ICgxID0gdW5pdHksIGhpZ2hlciA9IG1vcmUKICAgICAgICAgICAgLy8gICBzYXR1cmF0aW9uL2hhcm1vbmljcykuIHNoYXBlOiAwPWhhcmRjbGlwLCAxPXRhbmguIG1peDogd2V0L2RyeSAoMT13ZXQpLgogICAgICAgICAgICAvLyBvdXRwdXQ6IHBvc3QgZ2Fpbi4gQWxsIGstcmF0ZSAoY29uc3RhbnQgb3ZlciB0aGUgMTI4LXNhbXBsZSBibG9jaykuCiAgICAgICAgICAgIHJldHVybiBbCiAgICAgICAgICAgICAgICBbImRyaXZlIiwgMSwgMCwgMTAwLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbInNoYXBlIiwgMCwgMCwgMSwgImstcmF0ZSJdLAogICAgICAgICAgICAgICAgWyJtaXgiLCAxLCAwLCAxLCAiay1yYXRlIl0sCiAgICAgICAgICAgICAgICBbIm91dHB1dCIsIDEsIDAsIDQsICJrLXJhdGUiXSwKICAgICAgICAgICAgXS5tYXAoKFtuYW1lLCBkZWZhdWx0VmFsdWUsIG1pblZhbHVlLCBtYXhWYWx1ZSwgYXV0b21hdGlvblJhdGVdKSA9PiAoewogICAgICAgICAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICAgICAgICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlLAogICAgICAgICAgICAgICAgbWluVmFsdWU6IG1pblZhbHVlLAogICAgICAgICAgICAgICAgbWF4VmFsdWU6IG1heFZhbHVlLAogICAgICAgICAgICAgICAgYXV0b21hdGlvblJhdGU6IGF1dG9tYXRpb25SYXRlLAogICAgICAgICAgICB9KSk7CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICAgICAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgICAgICAgICBjb25zdCBvdXRwdXQgPSBvdXRwdXRzWzBdOwogICAgICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0Lmxlbmd0aCA9PT0gMCB8fCAhb3V0cHV0IHx8IG91dHB1dC5sZW5ndGggPT09IDApIHsKICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIGstcmF0ZSBwYXJhbXM6IHRha2UgdGhlIGZpcnN0IChjb25zdGFudC1vdmVyLWJsb2NrKSB2YWx1ZS4KICAgICAgICAgICAgY29uc3QgcGFyYW1zID0gewogICAgICAgICAgICAgICAgZHJpdmU6IHBhcmFtZXRlcnMuZHJpdmVbMF0sCiAgICAgICAgICAgICAgICBzaGFwZTogc2hhcGVGcm9tSW5kZXgocGFyYW1ldGVycy5zaGFwZVswXSksCiAgICAgICAgICAgICAgICBtaXg6IHBhcmFtZXRlcnMubWl4WzBdLAogICAgICAgICAgICAgICAgb3V0cHV0OiBwYXJhbWV0ZXJzLm91dHB1dFswXSwKICAgICAgICAgICAgfTsKICAgICAgICAgICAgY29uc3QgY2hhbm5lbENvdW50ID0gTWF0aC5taW4oaW5wdXQubGVuZ3RoLCBvdXRwdXQubGVuZ3RoKTsKICAgICAgICAgICAgZm9yIChsZXQgY2ggPSAwOyBjaCA8IGNoYW5uZWxDb3VudDsgY2grKykgewogICAgICAgICAgICAgICAgaWYgKCF0aGlzLmNvcmVzW2NoXSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdID0gbmV3IFdhdmVzaGFwZXJQcm9jZXNzb3IoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRoaXMuY29yZXNbY2hdLnByb2Nlc3MoaW5wdXRbY2hdLCBvdXRwdXRbY2hdLCBwYXJhbXMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IG1vZHVsZSBldmFsdWF0aW5nYCk7CiAgICB0cnkgewogICAgICAgIHJlZ2lzdGVyUHJvY2Vzc29yKCJ3YXZlc2hhcGVyIiwgV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IpOwogICAgICAgIGNvbnNvbGUuaW5mbyhgJHtXT1JLTEVUX0xPR19QUkVGSVh9IHJlZ2lzdGVyUHJvY2Vzc29yIGNvbXBsZXRlYCk7CiAgICB9CiAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICBjb25zb2xlLmVycm9yKGAke1dPUktMRVRfTE9HX1BSRUZJWH0gcmVnaXN0ZXJQcm9jZXNzb3IgZmFpbGVkYCwgZXJyb3IpOwogICAgICAgIHRocm93IGVycm9yOwogICAgfQoKICAgIGV4cG9ydHMuV2F2ZXNoYXBlcldvcmtsZXRQcm9jZXNzb3IgPSBXYXZlc2hhcGVyV29ya2xldFByb2Nlc3NvcjsKCiAgICByZXR1cm4gZXhwb3J0czsKCn0pKHt9KTsKLy8jIHNvdXJjZU1hcHBpbmdVUkw9d2F2ZXNoYXBlci1idW5kbGUuanMubWFwCg==\"","// Single source of truth for the library's AudioWorklet modules.\n//\n// Each entry pairs the `registerProcessor` name (the string passed to\n// `AudioWorkletNode` construction and to `audioWorklet.addModule`) with the\n// bundle URL. The URLs are imported with Vite's `?url` suffix; in the library\n// build Vite currently inlines each bundle as a base64 `data:` URI (see the\n// audit notes — splitting them into sibling files is deferred until a real\n// browser smoke test exists). This module has NO logic and NO dependency on\n// `cacophony.ts` or `effects.ts`, so all three can import it without a cycle.\n\nimport barberpoleBundleUrl from \"./bundles/barberpole-bundle.js?url\";\nimport dattorroReverbBundleUrl from \"./bundles/dattorro-reverb-bundle.js?url\";\nimport dynamicsBundleUrl from \"./bundles/dynamics-bundle.js?url\";\nimport fdnReverbBundleUrl from \"./bundles/fdn-reverb-bundle.js?url\";\nimport frequencyShifterBundleUrl from \"./bundles/frequency-shifter-bundle.js?url\";\nimport harmonizerBundleUrl from \"./bundles/harmonizer-bundle.js?url\";\nimport loudnessMeterBundleUrl from \"./bundles/loudness-meter-bundle.js?url\";\nimport modulatedDelayBundleUrl from \"./bundles/modulated-delay-bundle.js?url\";\nimport pcmStreamBundleUrl from \"./bundles/pcm-stream-bundle.js?url\";\nimport phaseVocoderBundleUrl from \"./bundles/phase-vocoder-bundle.js?url\";\nimport phaserBundleUrl from \"./bundles/phaser-bundle.js?url\";\nimport spectralFreezeBundleUrl from \"./bundles/spectral-freeze-bundle.js?url\";\nimport stereoToBFormatBundleUrl from \"./bundles/stereo-to-bformat-bundle.js?url\";\nimport stereoWidenerBundleUrl from \"./bundles/stereo-widener-bundle.js?url\";\nimport tremoloBundleUrl from \"./bundles/tremolo-bundle.js?url\";\nimport waveshaperBundleUrl from \"./bundles/waveshaper-bundle.js?url\";\n\n/**\n * A registrable AudioWorklet module: the processor `name` and the `url` of the\n * bundle that calls `registerProcessor(name, ...)`.\n */\nexport interface WorkletModule {\n /** The `registerProcessor` name — also the per-context load-dedup key. */\n readonly name: string;\n /** The worklet bundle URL passed to `audioWorklet.addModule`. */\n readonly url: string;\n}\n\n/**\n * The library's worklet modules, keyed by a stable identifier. Effects and the\n * pitch-shift path reference these instead of repeating name/url literals.\n */\nexport const WORKLETS = {\n phaseVocoder: { name: \"phase-vocoder\", url: phaseVocoderBundleUrl },\n harmonizer: { name: \"harmonizer\", url: harmonizerBundleUrl },\n spectralFreeze: { name: \"spectral-freeze\", url: spectralFreezeBundleUrl },\n frequencyShifter: { name: \"frequency-shifter\", url: frequencyShifterBundleUrl },\n barberpole: { name: \"barberpole\", url: barberpoleBundleUrl },\n stereoWidener: { name: \"stereo-widener\", url: stereoWidenerBundleUrl },\n stereoToBFormat: { name: \"stereo-to-bformat\", url: stereoToBFormatBundleUrl },\n dattorroReverb: { name: \"dattorro-reverb\", url: dattorroReverbBundleUrl },\n dynamics: { name: \"dynamics\", url: dynamicsBundleUrl },\n fdnReverb: { name: \"fdn-reverb\", url: fdnReverbBundleUrl },\n waveshaper: { name: \"waveshaper\", url: waveshaperBundleUrl },\n modulatedDelay: { name: \"modulated-delay\", url: modulatedDelayBundleUrl },\n pcmStream: { name: \"pcm-stream\", url: pcmStreamBundleUrl },\n phaser: { name: \"phaser\", url: phaserBundleUrl },\n tremolo: { name: \"tremolo\", url: tremoloBundleUrl },\n loudnessMeter: { name: \"loudness-meter\", url: loudnessMeterBundleUrl },\n} satisfies Record<string, WorkletModule>;\n\n/** Every worklet module, for eager preload (see `Cacophony.loadWorklets`). */\nexport const ALL_WORKLETS: readonly WorkletModule[] = Object.values(WORKLETS);\n","/**\n * Cacophony Effects: a thin abstraction over Web Audio nodes that lets a Bus\n * carry shared processing or a playback build an independent source effect.\n *\n * A {@link CacophonyEffect} is responsible for producing a live AudioNode or\n * endpoint graph when its `build` method is invoked. Single-node effects return\n * the node directly. Multi-node effects return a {@link BuiltEffectGraph} with\n * distinct `input` and `output` endpoints so effect chains can connect into and out\n * of the graph without pretending the head node is also the tail.\n *\n * The `build` return is async-capable so worklet-backed effects (e.g.\n * DattorroReverb) can `await` their AudioWorklet module load before\n * constructing the node, while pure-DOM effects (BiquadFilter, simple GainNode\n * wrappers, ConvolverNode graphs) can return synchronously.\n */\n\nimport type { AudioBuffer, AudioNode, AudioParam, AudioWorkletNode, BaseContext, BiquadFilterNode } from \"./context\";\nimport type { WorkletModule } from \"./worklets\";\nimport { WORKLETS } from \"./worklets\";\n\n/**\n * The single structural surface every worklet-backed {@link CacophonyEffect}\n * needs from its host (a {@link Cacophony} instance). Declared locally so this\n * module avoids a circular import on cacophony.ts. `buildWorkletEffect`\n * idempotently registers the worklet module on `context` (or the host's own\n * context when omitted — the cross-context contract `build(context)` promises)\n * and constructs the node with the supplied `parameterData`.\n */\nexport interface WorkletEffectHost {\n buildWorkletEffect(\n worklet: WorkletModule,\n parameterData: Record<string, number>,\n context?: BaseContext,\n nodeOptions?: AudioWorkletNodeOptions,\n ): Promise<AudioWorkletNode>;\n}\n\n/**\n * Minimal structural interface for the Cacophony surface {@link FoaDecoder} needs.\n * Declared locally (like {@link ReverbHost}) so this module avoids a circular\n * import on cacophony.ts. `loadFoaHrir` resolves (and per-context memoizes) the\n * bundled order-1 SH-HRIR `AudioBuffer`; the decoder slices it into the two\n * stereo ConvolverNode buffers.\n */\ninterface FoaDecoderHost {\n loadFoaHrir(context?: BaseContext): Promise<AudioBuffer>;\n /** The host's own default audio context, used when no context is supplied. */\n defaultContext(): BaseContext;\n}\n\n/**\n * Minimal structural interface for loading URL-backed impulse responses without\n * importing `Cacophony` into this module.\n */\ninterface ImpulseResponseHost {\n loadImpulseResponseBuffer(url: string, context: BaseContext, signal?: AbortSignal): Promise<AudioBuffer>;\n}\n\n/**\n * A built effect graph with distinct endpoints. `input` is where the previous\n * Bus chain entry connects, and `output` is where the next entry connects.\n * `handle` is the public AudioNode identity returned by `Bus.addFilter()` and\n * exposed through `Bus.filters`; when omitted the bus uses `input`.\n */\nexport interface BuiltEffectGraph {\n input: AudioNode;\n output: AudioNode;\n handle?: AudioNode;\n params?: Readonly<Record<string, AudioParam>>;\n dispose?: () => void;\n}\n\nexport type BuiltEffect = AudioNode | BuiltEffectGraph;\n\n/**\n * Public surface every Cacophony effect implements. `build` materializes the\n * effect's live node graph against a specific audio context. A Bus consumes an\n * effect as shared live state; a source consumes it as a recipe and calls\n * `build` once for every playback. Cacophony itself does not cache.\n */\nexport interface CacophonyEffect {\n build(context: BaseContext): Promise<BuiltEffect> | BuiltEffect;\n}\n\n/**\n * Base class for every worklet-backed effect. Holds the {@link WorkletModule}\n * to build and the construction-time options, and resolves the options into the\n * `parameterData` the worklet node is constructed with. `build` honors the\n * cross-context contract — it builds against the bus's `context`, not the\n * host's own — by forwarding `context` to {@link WorkletEffectHost.buildWorkletEffect}.\n *\n * Subclasses supply a {@link WorkletModule} and, when a field needs translation\n * before it can ride as an AudioParam (e.g. a string mode alias → integer index),\n * override {@link toParameterData}. The default passes the options through as a\n * numeric record, exactly as the per-effect classes did before.\n */\nexport abstract class WorkletEffect<O extends object> implements CacophonyEffect {\n protected constructor(\n protected readonly host: WorkletEffectHost,\n private readonly worklet: WorkletModule,\n protected readonly options: O,\n ) {}\n\n /**\n * Translate the construction options into worklet `parameterData`. Default:\n * pass through as a numeric record (the worklet validates/clamps downstream).\n */\n protected toParameterData(options: O): Record<string, number> {\n return { ...options } as Record<string, number>;\n }\n\n build(context: BaseContext): Promise<AudioWorkletNode> {\n return this.host.buildWorkletEffect(this.worklet, this.toParameterData(this.options), context);\n }\n}\n\n/**\n * Tracks AudioNodes that were produced by Cacophony's own factories so the\n * Bus filter-chain admission policy can distinguish a Cacophony-built node\n * (admit directly) from a raw third-party node (reject — user must wrap via\n * `cacophony.shareEffect`). WeakSet membership is GC-safe and identity-keyed.\n */\nconst cacophonyBuiltBiquads = new WeakSet<BiquadFilterNode>();\n\n/**\n * Mark a BiquadFilterNode as Cacophony-built. Called by\n * {@link Cacophony.createBiquadFilter} on the freshly constructed node.\n */\nexport function markAsCacophonyBiquad(node: BiquadFilterNode): void {\n cacophonyBuiltBiquads.add(node);\n}\n\n/**\n * Test whether a node was produced by {@link Cacophony.createBiquadFilter}.\n * When a context is supplied, the node must also belong to that context so a\n * factory-built biquad cannot be admitted to a foreign audio graph.\n */\nexport function isCacophonyBuiltBiquad(node: unknown, context?: BaseContext): node is BiquadFilterNode {\n return (\n typeof node === \"object\" &&\n node !== null &&\n cacophonyBuiltBiquads.has(node as BiquadFilterNode) &&\n (context === undefined || (node as BiquadFilterNode).context === context)\n );\n}\n\n/**\n * Effect wrapper for a pre-constructed BiquadFilterNode. `build` returns\n * the same node every time, so the same Biquad is shared across every bus\n * that adds this effect. Use when you want explicit shared filter state\n * across multiple buses.\n */\nexport class BiquadEffect implements CacophonyEffect {\n constructor(private readonly node: BiquadFilterNode) {}\n\n build(_context: BaseContext): AudioNode {\n return this.node;\n }\n}\n\n/**\n * Treats a BiquadFilterNode as a per-playback recipe. Unlike BiquadEffect,\n * which intentionally shares its node, every build creates an independent\n * node and copies the template's public filter state.\n */\nexport class BiquadRecipeEffect implements CacophonyEffect {\n constructor(private readonly template: BiquadFilterNode) {}\n\n build(context: BaseContext): BiquadFilterNode {\n const node = context.createBiquadFilter();\n node.type = this.template.type;\n node.frequency.value = this.template.frequency.value;\n node.detune.value = this.template.detune.value;\n node.Q.value = this.template.Q.value;\n node.gain.value = this.template.gain.value;\n return node;\n }\n}\n\n/**\n * Explicit \"I know what I'm doing\" wrapper around a raw AudioNode the user\n * built outside of Cacophony's factories. `Bus.addFilter` rejects raw\n * AudioNodes by default to surface the shared-state implication; wrapping\n * the node in a `ShareEffect` (via `cacophony.shareEffect(node)`) is the\n * documented opt-in. The same node instance is returned from every `build`\n * call, so all buses using the effect feed the same Web Audio node.\n */\nexport class ShareEffect implements CacophonyEffect {\n constructor(private readonly node: AudioNode) {}\n\n build(_context: BaseContext): AudioNode {\n return this.node;\n }\n}\n\nexport type ImpulseResponseSource = AudioBuffer | string;\n\nexport interface ImpulseResponseOptions {\n /** Web Audio convolver normalization. Default false to preserve measured IR gain. */\n normalize?: boolean;\n /** Dry gain for an inline dry/wet graph. Default 0 for wet-only send-bus use. */\n dry?: number;\n /** Wet gain for the convolved path. Default 1. */\n wet?: number;\n /** Optional abort signal for URL-backed impulse-response loading. */\n signal?: AbortSignal;\n}\n\n/**\n * Native ConvolverNode impulse-response effect. Wet-only construction returns a\n * single ConvolverNode and is intended for send buses. Supplying `dry` or a\n * non-unity `wet` builds an owned dry/wet graph with exposed `dry` and `wet`\n * gain AudioParams.\n */\nexport class ImpulseResponseEffect implements CacophonyEffect {\n constructor(\n private readonly host: ImpulseResponseHost,\n private readonly source: ImpulseResponseSource,\n private readonly options: ImpulseResponseOptions = {},\n ) {}\n\n async build(context: BaseContext): Promise<BuiltEffect> {\n if (!context.createConvolver) {\n throw new Error(\"ImpulseResponseEffect requires createConvolver\");\n }\n\n const buffer =\n typeof this.source === \"string\"\n ? await this.host.loadImpulseResponseBuffer(this.source, context, this.options.signal)\n : this.source;\n const convolver = context.createConvolver();\n convolver.normalize = this.options.normalize ?? false;\n convolver.buffer = buffer;\n\n const dry = this.options.dry ?? 0;\n const wet = this.options.wet ?? 1;\n if (dry === 0 && wet === 1) {\n return convolver;\n }\n\n const input = context.createGain();\n const dryGain = context.createGain();\n const wetGain = context.createGain();\n const output = context.createGain();\n dryGain.gain.value = dry;\n wetGain.gain.value = wet;\n\n input.connect(dryGain);\n dryGain.connect(output);\n input.connect(convolver);\n convolver.connect(wetGain);\n wetGain.connect(output);\n\n return {\n input,\n output,\n handle: input,\n params: { dry: dryGain.gain, wet: wetGain.gain },\n dispose: () => {\n try {\n input.disconnect(dryGain);\n } catch {}\n try {\n dryGain.disconnect(output);\n } catch {}\n try {\n input.disconnect(convolver);\n } catch {}\n try {\n convolver.disconnect(wetGain);\n } catch {}\n try {\n wetGain.disconnect(output);\n } catch {}\n },\n };\n }\n}\n\n/**\n * Subset of the {@link import('./processors/dattorro-reverb').DattorroReverbProcessor}\n * AudioWorkletProcessor's AudioParam set that we expose for construction-time\n * configuration via {@link Cacophony.createReverb}. All values are optional\n * and clamped to the worklet's documented ranges (0..1 for most params).\n *\n * Every field corresponds to a `parameterData` entry passed to the\n * AudioWorkletNode constructor; the worklet handles validation downstream.\n */\nexport interface ReverbOptions {\n /** Pre-delay in samples (0..sampleRate-1). Default 0. */\n preDelay?: number;\n /** Input bandwidth (0..1). Default 0.9999. */\n bandwidth?: number;\n /** First input diffusion stage (0..1). Default 0.75. */\n inputDiffusion1?: number;\n /** Second input diffusion stage (0..1). Default 0.625. */\n inputDiffusion2?: number;\n /** Tail decay (0..1). Default 0.5. */\n decay?: number;\n /** First decay diffusion stage (0..0.999999). Default 0.7. */\n decayDiffusion1?: number;\n /** Second decay diffusion stage (0..0.999999). Default 0.5. */\n decayDiffusion2?: number;\n /** Frequency damping (0..1). Default 0.005. */\n damping?: number;\n /** Modulation rate (0..2). Default 0.5. */\n excursionRate?: number;\n /** Modulation depth (0..2). Default 0.7. */\n excursionDepth?: number;\n /** Wet mix (0..1). Default 0.3. */\n wet?: number;\n /** Dry mix (0..1). Default 0.6. */\n dry?: number;\n}\n\n/**\n * CacophonyEffect that builds a DattorroReverb AudioWorkletNode. Calls\n * `cacophony.loadDattorroReverb(undefined, context)` first to idempotently\n * ensure the worklet module is registered on the supplied context, then\n * constructs the worklet node on the same context with the supplied options\n * as `parameterData`. The returned node is the head AND tail of the\n * subgraph (single-node wet/dry handled internally by the processor).\n *\n * Cross-context support: `build(context)` honors the {@link CacophonyEffect}\n * contract — when the effect is added to a Bus whose context differs from\n * the creating Cacophony's own context, the worklet is loaded and the\n * AudioWorkletNode is constructed against the bus's context, not the host's.\n */\nexport class ReverbEffect extends WorkletEffect<ReverbOptions> {\n constructor(host: WorkletEffectHost, options: ReverbOptions = {}) {\n super(host, WORKLETS.dattorroReverb, options);\n }\n}\n\n/**\n * Construction-time configuration for a {@link DynamicsEffect}, mirroring the\n * `dynamics` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/dynamics').DynamicsWorkletProcessor}). All fields\n * are optional; the worklet clamps to its documented ranges downstream.\n *\n * A single parameter set drives compressor, limiter, expander and gate\n * (Giannoulis 2012): ratio > 1 compresses, a very large ratio limits, and\n * ratio < 1 is downward expansion (an extreme low ratio is a gate). The\n * `createLimiter` / `createGate` factories are presets over these same params.\n */\nexport interface DynamicsOptions {\n /** Threshold T (dB), level above which compression starts. Default -24. Range -100..0. */\n threshold?: number;\n /** Ratio R (reciprocal of slope above T). Default 4. >1 compress, large=limit, <1 expand. Range 0.05..1000. */\n ratio?: number;\n /** Knee width W (dB), soft-knee transition centered on T (0 = hard). Default 6. Range 0..40. */\n knee?: number;\n /** Attack time tau_A (s). Default 0.003. Range 0..1. */\n attack?: number;\n /** Release time tau_R (s). Default 0.25. Range 0..5. */\n release?: number;\n /** Make-up gain M (dB), constant output boost. Default 0. Range -24..24. */\n makeup?: number;\n}\n\n/**\n * CacophonyEffect that builds a `dynamics` AudioWorkletNode — a feed-forward\n * dynamics processor (compressor/limiter/expander/gate) implementing Giannoulis,\n * Massberg & Reiss 2012. Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node\n * with the supplied {@link DynamicsOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class DynamicsEffect extends WorkletEffect<DynamicsOptions> {\n constructor(host: WorkletEffectHost, options: DynamicsOptions = {}) {\n super(host, WORKLETS.dynamics, options);\n }\n}\n\n/**\n * Construction-time configuration for an {@link FdnReverbEffect}, mirroring the\n * `fdn-reverb` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/fdn-reverb').FdnReverbWorkletProcessor}). All\n * fields are optional; the worklet clamps to its documented ranges downstream.\n *\n * The reverb is a Feedback Delay Network: a lossless paraunitary Hadamard\n * feedback core keeps it stable (Schlecht & Habets 2019), per-line absorption\n * filters set the decay (Jot & Chaigne 1991), and a sparse velvet-noise FIR\n * adds early echo density at no multiply cost (Fagerström et al. 2020).\n */\nexport interface FdnReverbOptions {\n /** Reverberation time T60 in seconds (−60 dB decay). Default 1.5. Range 0.001..20. */\n decayTime?: number;\n /** Pre-delay before the wet path in seconds. Default 0. Range 0..1. */\n preDelay?: number;\n /** High-frequency damping (0..1); higher shortens the HF tail. Default 0.3. */\n damping?: number;\n /** Velvet-noise diffusion amount (0..1); 0 bypasses. Default 0.5. */\n diffusion?: number;\n /** Wet/dry mix (0..1); 0 = dry, 1 = wet. Default 0.3. */\n mix?: number;\n}\n\n/**\n * CacophonyEffect that builds an `fdn-reverb` AudioWorkletNode — a Feedback\n * Delay Network reverberator (lossless paraunitary Hadamard feedback, Schlecht\n * & Habets 2019; Jot 1991 absorption-filter decay; Fagerström 2020 velvet-noise\n * diffusion). Mirrors {@link ReverbEffect}: `build` idempotently loads the\n * worklet module on the supplied context, then constructs the node with the\n * supplied {@link FdnReverbOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class FdnReverbEffect extends WorkletEffect<FdnReverbOptions> {\n constructor(host: WorkletEffectHost, options: FdnReverbOptions = {}) {\n super(host, WORKLETS.fdnReverb, options);\n }\n}\n\n/**\n * String aliases for the integer mode indices that {@link WaveshaperOptions},\n * {@link TremoloOptions} and {@link ModulatedDelayOptions} flow straight through\n * as `parameterData`. An AudioParam can only carry a number, so these maps are\n * applied inside each effect's `build()` to translate a string mode into its\n * integer index BEFORE the options become `parameterData`. The index assignments\n * mirror the worklet shells' `*_BY_INDEX` arrays exactly\n * (`processors/waveshaper.ts`, `processors/tremolo.ts`,\n * `processors/modulated-delay.ts`). Declared locally so this module does not\n * import from the worklet/processor modules (keeping that boundary intact).\n */\nconst WAVESHAPER_SHAPE_TO_INDEX = { hardclip: 0, tanh: 1 } as const;\ntype WaveshaperShapeAlias = keyof typeof WAVESHAPER_SHAPE_TO_INDEX;\n\nconst TREMOLO_SHAPE_TO_INDEX = { sine: 0, triangle: 1, square: 2 } as const;\ntype TremoloShapeAlias = keyof typeof TREMOLO_SHAPE_TO_INDEX;\n\nconst MODULATED_DELAY_INTERPOLATION_TO_INDEX = { cubic: 0, linear: 1 } as const;\ntype ModulatedDelayInterpolationAlias = keyof typeof MODULATED_DELAY_INTERPOLATION_TO_INDEX;\n\n/**\n * Resolve a mode field that may be a number (pass through unchanged, exactly as\n * today) or a string alias (translate to its integer index). An unknown/invalid\n * string maps to index 0, mirroring the worklet's `?? first` fallback. Returns\n * `undefined` when the field is absent so the worklet keeps its own default.\n */\nfunction resolveModeIndex(value: number | string | undefined, table: Record<string, number>): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value === \"number\") {\n return value;\n }\n return table[value] ?? 0;\n}\n\n/**\n * Construction-time configuration for a {@link WaveshaperEffect}, mirroring the\n * `waveshaper` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/waveshaper').WaveshaperWorkletProcessor}). All\n * fields are optional; the worklet clamps to its documented ranges downstream.\n *\n * The effect is an antialiased distortion/waveshaper using first-order\n * Antiderivative Antialiasing (Parker, Zavalishin & Le Bivic 2016, DAFx-16):\n * y[n] = (F0(x_n) - F0(x_{n-1})) / (x_n - x_{n-1}) (eq.9), with an f(midpoint)\n * fallback at the 0/0 singularity (eq.10). It carries an inherent 0.5-sample\n * group delay (first-order ADAA, eq.17).\n */\nexport interface WaveshaperOptions {\n /** Pre-gain (drive) into the nonlinearity. Default 1. Range 0..100; >1 = harder saturation. */\n drive?: number;\n /**\n * Nonlinearity: 0 = hard clip (polynomial F0, eq.25), 1 = tanh soft clip\n * (F0 = log cosh, eq.20). Default 0. Accepts either the integer index or the\n * matching string alias (`\"hardclip\"` = 0, `\"tanh\"` = 1), translated to the\n * index in `build()`.\n */\n shape?: number | WaveshaperShapeAlias;\n /** Wet/dry mix (0..1); 0 = dry bypass, 1 = fully shaped. Default 1. */\n mix?: number;\n /** Post-nonlinearity output gain (linear). Default 1. Range 0..4. */\n output?: number;\n}\n\n/**\n * CacophonyEffect that builds a `waveshaper` AudioWorkletNode — an antialiased\n * distortion/waveshaper implementing first-order Antiderivative Antialiasing\n * (Parker, Zavalishin & Le Bivic 2016, DAFx-16). Mirrors {@link ReverbEffect}:\n * `build` idempotently loads the worklet module on the supplied context, then\n * constructs the node with the supplied {@link WaveshaperOptions} as\n * `parameterData`. Honors the cross-context contract (builds against the bus's\n * context, not the host's own).\n */\nexport class WaveshaperEffect extends WorkletEffect<WaveshaperOptions> {\n constructor(host: WorkletEffectHost, options: WaveshaperOptions = {}) {\n super(host, WORKLETS.waveshaper, options);\n }\n\n protected override toParameterData(options: WaveshaperOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const shapeIndex = resolveModeIndex(options.shape, WAVESHAPER_SHAPE_TO_INDEX);\n if (shapeIndex !== undefined) {\n parameterData.shape = shapeIndex;\n }\n return parameterData;\n }\n}\n\n/**\n * Construction-time configuration for a {@link ModulatedDelayEffect}, mirroring\n * the `modulated-delay` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/modulated-delay').ModulatedDelayWorkletProcessor}).\n * All fields are optional; the worklet clamps to its documented ranges\n * downstream. Field NAMES match the AudioParam names exactly (they flow straight\n * through as `parameterData`).\n *\n * The effect is Dattorro's unified modulated-delay circuit (JAES 1997, Fig. 36)\n * with Lagrange FIR fractional-delay interpolation (Laakso 1996). One topology —\n * dry (`blend`) + a modulated wet tap (`feedforward`) + feedback on a FIXED\n * center tap — yields delay/echo, chorus, flanger, vibrato and doubling from\n * knob presets (Table 6). The `createDelay`/`createChorus`/`createFlanger`/\n * `createVibrato`/`createDoubling` factories are presets over these params.\n */\nexport interface ModulatedDelayOptions {\n /** Nominal (center) delay in ms — the fixed feedback tap center. Default 5. Range 0..1000. */\n delayTime?: number;\n /** Peak LFO delay excursion in ms (CHORUS_WIDTH); 0 = pure delay. Default 0. Range 0..50. */\n depth?: number;\n /** LFO rate f_e in Hz. Default 0.5. Range 0..20. */\n rate?: number;\n /** Feedback gain on the fixed center tap. Default 0. Range -0.9999999..0.9999999. */\n feedback?: number;\n /** Dry path gain (blend). Default 1. Range 0..1. */\n blend?: number;\n /** Wet (modulated tap) gain (feedforward). Default 0.7071. Range 0..1. */\n feedforward?: number;\n /**\n * Interpolation: 0 = cubic (4-tap Lagrange N=3), 1 = linear (2-tap N=1).\n * Default 0. Accepts either the integer index or the matching string alias\n * (`\"cubic\"` = 0, `\"linear\"` = 1), translated to the index in `build()`.\n */\n interpolation?: number | ModulatedDelayInterpolationAlias;\n}\n\n/**\n * CacophonyEffect that builds a `modulated-delay` AudioWorkletNode — Dattorro's\n * unified modulated-delay circuit (JAES 1997, Fig. 36) backing\n * delay/chorus/flanger/vibrato/doubling, with Lagrange FIR fractional-delay\n * interpolation (Laakso 1996). Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node with\n * the supplied {@link ModulatedDelayOptions} as `parameterData`. Honors the\n * cross-context contract (builds against the bus's context, not the host's own).\n */\nexport class ModulatedDelayEffect extends WorkletEffect<ModulatedDelayOptions> {\n constructor(host: WorkletEffectHost, options: ModulatedDelayOptions = {}) {\n super(host, WORKLETS.modulatedDelay, options);\n }\n\n protected override toParameterData(options: ModulatedDelayOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const interpolationIndex = resolveModeIndex(options.interpolation, MODULATED_DELAY_INTERPOLATION_TO_INDEX);\n if (interpolationIndex !== undefined) {\n parameterData.interpolation = interpolationIndex;\n }\n return parameterData;\n }\n}\n\n/**\n * Construction-time configuration for a {@link PhaserEffect}, mirroring the\n * `phaser` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/phaser').PhaserWorkletProcessor}). All fields are\n * optional; the worklet clamps to its documented ranges downstream. Field NAMES\n * match the AudioParam names exactly (they flow straight through as\n * `parameterData`).\n *\n * The effect is a classic MXR/Univibe-style phaser: a cascade of first-order\n * allpass sections at a common LFO-swept break frequency, summed additively with\n * the dry signal to sweep notches through the spectrum (Smith STAN-M-21; PASP\n * §8.9). Two allpass sections make one notch.\n */\nexport interface PhaserOptions {\n /** Center break frequency of the allpass sections, in Hz. Default 500. Range 20..10000. */\n frequency?: number;\n /** LFO rate in Hz. Default 0.5. Range 0..20. */\n rate?: number;\n /** Log sweep depth in octaves (break freq *= 2^(depth*lfo)). Default 1.5. Range 0..4. */\n depth?: number;\n /** Number of first-order allpass sections (2 per notch). Default 4. Range 2..12. */\n stages?: number;\n /** Feedback (regeneration/resonance). Default 0. Range -0.95..0.95. */\n feedback?: number;\n /** Additive wet gain (notch depth); y = x + mix*cascade. Default 0.5. Range 0..1. */\n mix?: number;\n}\n\n/**\n * CacophonyEffect that builds a `phaser` AudioWorkletNode — a classic\n * MXR/Univibe-style allpass-cascade phase shifter (Smith STAN-M-21; PASP §8.9).\n * Mirrors {@link ReverbEffect}: `build` idempotently loads the worklet module on\n * the supplied context, then constructs the node with the supplied\n * {@link PhaserOptions} as `parameterData`. Honors the cross-context contract\n * (builds against the bus's context, not the host's own).\n */\nexport class PhaserEffect extends WorkletEffect<PhaserOptions> {\n constructor(host: WorkletEffectHost, options: PhaserOptions = {}) {\n super(host, WORKLETS.phaser, options);\n }\n}\n\n/**\n * Construction-time configuration for a {@link TremoloEffect}, mirroring the\n * `tremolo` AudioWorkletProcessor's AudioParam set (see\n * {@link import('./processors/tremolo').TremoloWorkletProcessor}). All fields are\n * optional; the worklet clamps to its documented ranges downstream. Field NAMES\n * match the AudioParam names exactly (they flow straight through as\n * `parameterData`).\n *\n * The effect is LFO-driven amplitude modulation (a VCA swung by an LFO) — the\n * gain swings between (1 - depth) and 1, never inverting (a true tremolo, not\n * ring modulation). Anchored to standard AM theory, the quadrature stereo LFO of\n * Dattorro 1997 (p.776), and the LFO-driven-effect framing of Mitcheltree et al.\n * (DAFx23). `stereoPhase` offsets the per-channel LFO (0 = mono, 90 = quadrature,\n * 180 = hard auto-pan).\n */\nexport interface TremoloOptions {\n /** LFO rate in Hz. Default 5. Range 0..20. */\n rate?: number;\n /** Modulation depth (0 = bypass, 1 = full 0..1 swing). Default 0.5. Range 0..1. */\n depth?: number;\n /**\n * LFO shape: 0 = sine, 1 = triangle, 2 = square. Default 0. Accepts either\n * the integer index or the matching string alias (`\"sine\"` = 0,\n * `\"triangle\"` = 1, `\"square\"` = 2), translated to the index in `build()`.\n */\n shape?: number | TremoloShapeAlias;\n /** Per-channel LFO phase offset in degrees (0 = mono, 90 = quadrature, 180 = auto-pan). Default 0. Range 0..180. */\n stereoPhase?: number;\n}\n\n/**\n * CacophonyEffect that builds a `tremolo` AudioWorkletNode — LFO-driven amplitude\n * modulation (AM theory; Dattorro 1997 p.776 quadrature stereo LFO; Mitcheltree\n * et al. DAFx23 LFO framing). Mirrors {@link ReverbEffect}: `build` idempotently\n * loads the worklet module on the supplied context, then constructs the node with\n * the supplied {@link TremoloOptions} as `parameterData`. Honors the cross-context\n * contract (builds against the bus's context, not the host's own).\n */\nexport class TremoloEffect extends WorkletEffect<TremoloOptions> {\n constructor(host: WorkletEffectHost, options: TremoloOptions = {}) {\n super(host, WORKLETS.tremolo, options);\n }\n\n protected override toParameterData(options: TremoloOptions): Record<string, number> {\n const parameterData = { ...options } as Record<string, number>;\n const shapeIndex = resolveModeIndex(options.shape, TREMOLO_SHAPE_TO_INDEX);\n if (shapeIndex !== undefined) {\n parameterData.shape = shapeIndex;\n }\n return parameterData;\n }\n}\n\n/** Options for Wardle's Hilbert-transform single-sideband frequency shifter. */\nexport interface FrequencyShifterOptions {\n /** Signed frequency translation in Hz. Positive shifts up; negative shifts down. Default 100. */\n frequency?: number;\n /** Wet amount (0 = latency-aligned dry, 1 = shifted). Default 1. */\n mix?: number;\n}\n\n/** Equal-Hz frequency translation that deliberately breaks harmonic ratios. */\nexport class FrequencyShifterEffect extends WorkletEffect<FrequencyShifterOptions> {\n constructor(host: WorkletEffectHost, options: FrequencyShifterOptions = {}) {\n super(host, WORKLETS.frequencyShifter, options);\n }\n}\n\n/** Options for the SSB spectral-delay barberpole phaser (DAFx-15 Fig. 12). */\nexport interface BarberpoleOptions {\n /** Signed notch travel rate and direction in Hz. Default 0.1. */\n rate?: number;\n /** Cascaded allpass count; two stages produce one notch. Default 32. */\n stages?: number;\n /** Spectral-delay allpass coefficient. -0.5 approximates octave spacing. Default -0.5. */\n coefficient?: number;\n /** Effect mix (0 = aligned dry, 1 = barberpole sum). Default 1. */\n mix?: number;\n}\n\n/** Endlessly rising/falling, warped-notch SSB barberpole phaser. */\nexport class BarberpoleEffect extends WorkletEffect<BarberpoleOptions> {\n constructor(host: WorkletEffectHost, options: BarberpoleOptions = {}) {\n super(host, WORKLETS.barberpole, options);\n }\n}\n\n/** Two-voice Laroche-Dolson identity-phase-locked harmonizer options. */\nexport interface HarmonizerOptions {\n semitonesA?: number;\n semitonesB?: number;\n gainA?: number;\n gainB?: number;\n dry?: number;\n}\n\n/** Adds two independently shifted voices in one shared STFT analysis pass. */\nexport class HarmonizerEffect extends WorkletEffect<HarmonizerOptions> {\n constructor(host: WorkletEffectHost, options: HarmonizerOptions = {}) {\n super(host, WORKLETS.harmonizer, options);\n }\n}\n\n/** Spectral-frame capture and indefinite phase-coherent sustain options. */\nexport interface SpectralFreezeOptions {\n /** >=0.5 captures/holds; automate through rampEffectParam. Default 0. */\n freeze?: number;\n /** Blend captured magnitude with the preceding three frames. Default 0.5. */\n smear?: number;\n /** Frozen wet mix. Default 1. */\n mix?: number;\n}\n\n/** Phase-continuing spectral freeze that avoids looping one static FFT frame. */\nexport class SpectralFreezeEffect extends WorkletEffect<SpectralFreezeOptions> {\n constructor(host: WorkletEffectHost, options: SpectralFreezeOptions = {}) {\n super(host, WORKLETS.spectralFreeze, options);\n }\n}\n\n/** Sparse velvet-noise stereo-width options. */\nexport interface StereoWidenerOptions {\n width?: number;\n decorrelation?: number;\n transientProtection?: number;\n}\n\n/** Mono/stereo to explicit stereo decorrelator with transient protection. */\nexport class StereoWidenerEffect implements CacophonyEffect {\n constructor(\n private readonly host: WorkletEffectHost,\n private readonly options: StereoWidenerOptions = {},\n ) {}\n\n build(context: BaseContext): Promise<AudioWorkletNode> {\n return this.host.buildWorkletEffect(\n WORKLETS.stereoWidener,\n { ...this.options } as Record<string, number>,\n context,\n {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [2],\n channelCount: 2,\n channelCountMode: \"explicit\",\n channelInterpretation: \"speakers\",\n },\n );\n }\n}\n\n/**\n * Construction-time configuration for a {@link FoaDecoder}.\n */\nexport interface FoaDecoderOptions {\n /**\n * Caller-supplied order-1 SH-HRIR. When omitted, the decoder resolves the\n * bundled Omnitone `sh_hrir_order_1.wav` (Apache-2.0) via\n * {@link FoaDecoderHost.loadFoaHrir}. Supply a 4-channel `AudioBuffer` (ACN\n * rows W,Y,Z,X) to override — e.g. in tests, or to ship a different\n * measured SH-HRIR. If you substitute a different file the WY/ZX row\n * grouping and the Y right-ear sign inversion must still match Omnitone's\n * convention (see {@link FoaDecoder}).\n */\n hrir?: AudioBuffer;\n}\n\n/**\n * Standalone 4-channel-in / 2-channel-out FOA → binaural FORMAT CONVERTER,\n * built entirely from native Web Audio nodes (NO worklet).\n *\n * This standalone construct exposes the two endpoints explicitly:\n * - {@link input} — the 4-channel `ChannelSplitterNode` you feed FOA into.\n * - {@link output} — the 2-channel binaural `GainNode` you route downstream.\n *\n * Wire it explicitly when building custom FOA graphs:\n * ```ts\n * const decoder = await cacophony.createFoaDecoder();\n * foaSourceNode.connect(decoder.input); // 4-ch FOA in\n * decoder.output.connect(bus.input /* or context.destination *\\/); // stereo out\n * ```\n *\n * For the common bus-filter case use {@link FoaDecoderEffect}, created via\n * `cacophony.createFoaDecoderEffect()`, which returns the same endpoint graph\n * through the standard `CacophonyEffect.build()` contract.\n *\n * ## Math (Ahrens 2022, eq. 31)\n * Under a real (SN3D/ACN) SH basis the binaural decode collapses to a single\n * per-EAR multiply-accumulate over ALL the SH channels:\n * B^{L,R}(w) = sum_{n,m} S_{n,m}(w) * H_{n,m}^{L,R}(w)\n * (Ahrens, \"Binaural Audio Rendering in the Spherical Harmonic Domain\", 2022,\n * eq. 31 — the real-basis form where the conjugation and m -> -m degree flip\n * vanish). EACH ear sums the contribution of EVERY FOA channel (W, Y, Z, X)\n * convolved with that ear's HRIR — neither ear may drop a channel. With a\n * single stored SH-HRIR the L/R-symmetric channels (W, Z, X) share the same\n * coefficient for both ears while the L/R-ANTISYMMETRIC channel (Y) is the\n * sign-flip for the right ear, so:\n * B^L = W*H_W + Y*H_Y + Z*H_Z + X*H_X\n * B^R = W*H_W - Y*H_Y + Z*H_Z + X*H_X (only the Y term differs, by sign)\n *\n * ## Topology (Omnitone WY/ZX packing — GoogleChrome/omnitone foa-convolver.js)\n * Rather than 8 mono convolvers, the four SH channels are grouped into two\n * STEREO `ConvolverNode`s — W+Y into one, Z+X into the other — each convolved\n * against a 2-row slice of the 4-row SH-HRIR. A 4-channel ConvolverNode would\n * do unwanted cross-channel convolution per the Web Audio spec; the stereo\n * packing is Omnitone's production graph and is mirrored here VERBATIM (the\n * `.connect` calls below are line-for-line Omnitone's `_buildAudioGraph`):\n *\n * input (ChannelSplitter, 4ch)\n * ch0(W),ch1(Y) -> mergerWY(2ch) -> convolverWY (HRIR rows {W,Y}, stereo)\n * ch2(Z),ch3(X) -> mergerZX(2ch) -> convolverZX (HRIR rows {Z,X}, stereo)\n * convolverWY -> splitterWY(2ch); convolverZX -> splitterZX(2ch)\n * splitterWY.ch0 (W) -> L AND R\n * splitterWY.ch1 (Y) -> L, and (via -1 inverter) -> R\n * splitterZX.ch0 (Z) -> L AND R\n * splitterZX.ch1 (X) -> L AND R\n * mergerBinaural(2ch) -> output (stereo)\n *\n * The result is exactly eq.31: BOTH ears receive ALL four SH channels; only the\n * Y channel's right-ear contribution is sign-flipped by a `GainNode(-1)`,\n * because Y is the sole left/right-antisymmetric FOA channel. (This is the fix\n * for the prior broken graph, which sent only W+Z to the left ear and only\n * -Y+X to the right — each ear missing two channels.) `convolver.normalize` is\n * set `false` on both: the SH-HRIR is already correctly scaled and Web Audio\n * convolver normalization would corrupt it.\n *\n * ## Normalization (SN3D end-to-end — NO sqrt(3) rescale)\n * The decoder, the bundled Omnitone HRIR, and {@link encodeMonoToFoaSN3D} are\n * ALL SN3D. The decode is the plain MAC of two SN3D-matched coefficient sets,\n * so NO per-channel sqrt(3) (N3D<->SN3D) rescale is applied — inserting one\n * would double-normalize.\n *\n * NOTE on the resurrection path: the dormant `StereoToFoaUpmixer`\n * (`createStereoToBFormatNode`) emits ACN `[W,Y,Z,X]` but is a perceptual,\n * frequency-banded, coherence-gated mix with per-channel non-constant gain — it\n * is NEITHER N3D nor SN3D and no single normalization bridge exists. Its 4-ch\n * output plugs straight into {@link input} (ACN ordering already lines up), and\n * the resulting binaural is a documented PERCEPTUAL APPROXIMATION, not a\n * physically-correct sound field. The clean, physically-correct path is\n * {@link encodeMonoToFoaSN3D} -> this decoder.\n */\nexport class FoaDecoder {\n /** The 4-channel FOA `[W, Y, Z, X]` (ACN) entry node. Feed FOA into this. */\n readonly input: AudioNode;\n /** The 2-channel binaural stereo node. Route THIS downstream. */\n readonly output: AudioNode;\n private readonly ownedNodes: readonly AudioNode[];\n\n private constructor(input: AudioNode, output: AudioNode, ownedNodes: readonly AudioNode[]) {\n this.input = input;\n this.output = output;\n this.ownedNodes = ownedNodes;\n }\n\n /** Disconnects the decoder's owned native nodes. Safe to call more than once. */\n dispose(): void {\n for (const node of this.ownedNodes) {\n try {\n node.disconnect();\n } catch {}\n }\n }\n\n /**\n * Builds the live decode graph against `context`, resolving the SH-HRIR\n * (caller-supplied or bundled Omnitone) and wiring the Omnitone WY/ZX graph.\n * Async because the bundled HRIR is fetched + `decodeAudioData`-d.\n */\n static async create(\n host: FoaDecoderHost,\n options: FoaDecoderOptions = {},\n context?: BaseContext,\n ): Promise<FoaDecoder> {\n const ctx = context ?? host.defaultContext();\n if (!ctx.createChannelSplitter || !ctx.createChannelMerger || !ctx.createConvolver || !ctx.createBuffer) {\n throw new Error(\n \"FoaDecoder requires createChannelSplitter, createChannelMerger, createConvolver and createBuffer\",\n );\n }\n\n // SN3D SH-HRIR (4 rows: ACN W,Y,Z,X). Caller override or bundled Omnitone.\n const hrir = options.hrir ?? (await host.loadFoaHrir(ctx));\n if (hrir.numberOfChannels < 4) {\n throw new Error(`FoaDecoder requires an HRIR with at least 4 channels; received ${hrir.numberOfChannels}`);\n }\n\n // Input: 4-channel FOA [W, Y, Z, X] (ACN). Externally-visible input node.\n const input = ctx.createChannelSplitter(4);\n\n // --- WY / ZX stereo grouping (Omnitone foa-convolver.js) ---\n const mergerWY = ctx.createChannelMerger(2);\n const mergerZX = ctx.createChannelMerger(2);\n // W(ch0)->mergerWY in0, Y(ch1)->mergerWY in1\n input.connect(mergerWY, 0, 0);\n input.connect(mergerWY, 1, 1);\n // Z(ch2)->mergerZX in0, X(ch3)->mergerZX in1\n input.connect(mergerZX, 2, 0);\n input.connect(mergerZX, 3, 1);\n\n const convolverWY = ctx.createConvolver();\n const convolverZX = ctx.createConvolver();\n // The SH-HRIR is already correctly scaled; Web Audio convolver\n // normalization would corrupt it (Omnitone sets disableNormalization=true).\n convolverWY.normalize = false;\n convolverZX.normalize = false;\n convolverWY.buffer = FoaDecoder.sliceHrirRows(ctx, hrir, 0, 1); // {W, Y} rows\n convolverZX.buffer = FoaDecoder.sliceHrirRows(ctx, hrir, 2, 3); // {Z, X} rows\n mergerWY.connect(convolverWY);\n mergerZX.connect(convolverZX);\n\n // --- Per-ear sum (Ahrens eq.31 MAC, executed by the convolver graph) ---\n // This is Omnitone foa-convolver.js _buildAudioGraph VERBATIM: both convolved\n // SH-channel pairs fan into BOTH ears, so each ear sums all four SH channels;\n // only the Y (splitterWY ch1) right-ear path is inverted.\n const splitterWY = ctx.createChannelSplitter(2);\n const splitterZX = ctx.createChannelSplitter(2);\n convolverWY.connect(splitterWY);\n convolverZX.connect(splitterZX);\n\n const mergerBinaural = ctx.createChannelMerger(2);\n const yRightInverter = ctx.createGain();\n yRightInverter.gain.value = -1;\n\n // W (splitterWY ch0) -> L (in0) and R (in1)\n splitterWY.connect(mergerBinaural, 0, 0);\n splitterWY.connect(mergerBinaural, 0, 1);\n // Y (splitterWY ch1) -> L (in0) directly; -> R (in1) through the -1 inverter\n splitterWY.connect(mergerBinaural, 1, 0);\n splitterWY.connect(yRightInverter, 1, 0);\n yRightInverter.connect(mergerBinaural, 0, 1);\n // Z (splitterZX ch0) -> L (in0) and R (in1)\n splitterZX.connect(mergerBinaural, 0, 0);\n splitterZX.connect(mergerBinaural, 0, 1);\n // X (splitterZX ch1) -> L (in0) and R (in1)\n splitterZX.connect(mergerBinaural, 1, 0);\n splitterZX.connect(mergerBinaural, 1, 1);\n\n const output = ctx.createGain();\n mergerBinaural.connect(output);\n\n return new FoaDecoder(input, output, [\n input,\n mergerWY,\n mergerZX,\n convolverWY,\n convolverZX,\n splitterWY,\n splitterZX,\n yRightInverter,\n mergerBinaural,\n output,\n ]);\n }\n\n /**\n * Builds a 2-channel `AudioBuffer` from rows `rowA`/`rowB` of the 4-row\n * SH-HRIR — the stereo buffer a WY or ZX `ConvolverNode` convolves against.\n */\n private static sliceHrirRows(context: BaseContext, hrir: AudioBuffer, rowA: number, rowB: number): AudioBuffer {\n const stereo = context.createBuffer!(2, hrir.length, hrir.sampleRate);\n stereo.copyToChannel(hrir.getChannelData(rowA), 0);\n stereo.copyToChannel(hrir.getChannelData(rowB), 1);\n return stereo;\n }\n}\n\n/**\n * `CacophonyEffect` wrapper for a FOA decoder endpoint graph. Use only on a\n * dedicated 4-channel ACN/SN3D FOA bus where the decoder is the first/only\n * filter converting the bus from FOA to stereo binaural.\n */\nexport class FoaDecoderEffect implements CacophonyEffect {\n constructor(\n private readonly host: FoaDecoderHost,\n private readonly options: FoaDecoderOptions = {},\n ) {}\n\n async build(context: BaseContext): Promise<BuiltEffectGraph> {\n const decoder = await FoaDecoder.create(this.host, this.options, context);\n return {\n input: decoder.input,\n output: decoder.output,\n handle: decoder.input,\n dispose: () => decoder.dispose(),\n };\n }\n}\n\nfunction isAudioNodeLike(value: unknown): value is AudioNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AudioNode).connect === \"function\" &&\n typeof (value as AudioNode).disconnect === \"function\"\n );\n}\n\n/**\n * Type guard for the endpoint-graph shape returned by multi-node effects.\n */\nexport function isBuiltEffectGraph(value: unknown): value is BuiltEffectGraph {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"input\" in value &&\n \"output\" in value &&\n isAudioNodeLike((value as { input: unknown }).input) &&\n isAudioNodeLike((value as { output: unknown }).output)\n );\n}\n\n/**\n * Type guard for values returned by {@link CacophonyEffect.build}.\n */\nexport function isBuiltEffect(value: unknown): value is BuiltEffect {\n return isAudioNodeLike(value) || isBuiltEffectGraph(value);\n}\n\n/**\n * Type guard for the structural shape `CacophonyEffect`. A value qualifies if\n * it exposes a callable `build` property.\n */\nexport function isCacophonyEffect(value: unknown): value is CacophonyEffect {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"build\" in value &&\n typeof (value as { build: unknown }).build === \"function\"\n );\n}\n","import type { FadeType } from \"./cacophony\";\nimport type { AudioNode, AudioParam, AudioWorkletNode } from \"./context\";\nimport type { BuiltEffect } from \"./effects\";\nimport { isBuiltEffect, isBuiltEffectGraph } from \"./effects\";\n\ninterface EffectChainEntry {\n handle: AudioNode;\n input: AudioNode;\n output: AudioNode;\n params?: Readonly<Record<string, AudioParam>>;\n dispose?: () => void;\n}\n\ninterface EffectChainReservation {\n readonly reservation: symbol;\n}\n\ntype EffectChainSlot = EffectChainEntry | EffectChainReservation;\n\n/**\n * Incrementally reconciles a series of built effects between two caller-owned\n * endpoint nodes.\n */\nexport class EffectChain {\n private readonly entries: EffectChainSlot[] = [];\n private readonly bypassed = new Set<AudioNode>();\n private readonly edges: Array<readonly [AudioNode, AudioNode]> = [];\n private destroyed = false;\n\n constructor(\n private input: AudioNode,\n private output: AudioNode,\n private readonly diagnosticScope = \"EffectChain\",\n ) {\n this.connectEdge(input, output);\n }\n\n get nodes(): readonly AudioNode[] {\n return this.entries.filter(this.isEntry).map((entry) => entry.handle);\n }\n\n has(node: AudioNode): boolean {\n return this.entries.some((entry) => this.isEntry(entry) && entry.handle === node);\n }\n\n setEndpoints(input: AudioNode, output: AudioNode): void {\n if (input === this.input && output === this.output) {\n return;\n }\n this.disconnectEdges();\n this.input = input;\n this.output = output;\n this.refresh();\n }\n\n add(built: BuiltEffect, index = this.entries.length): AudioNode {\n if (this.destroyed) {\n throw this.guardError(\"cannot add an effect to a destroyed chain\");\n }\n const entry = this.normalize(built);\n if (this.has(entry.handle)) {\n entry.dispose?.();\n throw this.guardError(\"cannot add the same effect node twice\");\n }\n this.entries.splice(index, 0, entry);\n this.refresh();\n return entry.handle;\n }\n\n /** Reserve a declaration-order position for an asynchronously built effect. */\n reserve(): symbol {\n if (this.destroyed) {\n throw this.guardError(\"cannot reserve an effect on a destroyed chain\");\n }\n const reservation = Symbol(\"effect-chain-reservation\");\n this.entries.push({ reservation });\n return reservation;\n }\n\n /** Replace a reservation with its built effect while retaining its position. */\n resolve(reservation: symbol, built: BuiltEffect): AudioNode {\n const entry = this.normalize(built);\n if (this.destroyed) {\n entry.dispose?.();\n return entry.handle;\n }\n const index = this.entries.findIndex(\n (candidate) => !this.isEntry(candidate) && candidate.reservation === reservation,\n );\n if (index === -1) {\n entry.dispose?.();\n throw this.guardError(\"cannot resolve an unknown effect-chain reservation\");\n }\n if (this.has(entry.handle)) {\n this.entries.splice(index, 1);\n entry.dispose?.();\n throw this.guardError(\"cannot add the same effect node twice\");\n }\n this.entries[index] = entry;\n this.refresh();\n return entry.handle;\n }\n\n cancel(reservation: symbol): void {\n const index = this.entries.findIndex(\n (candidate) => !this.isEntry(candidate) && candidate.reservation === reservation,\n );\n if (index !== -1) {\n this.entries.splice(index, 1);\n }\n }\n\n remove(node: AudioNode): void {\n const index = this.entries.findIndex((entry) => this.isEntry(entry) && entry.handle === node);\n if (index === -1) {\n throw this.guardError(\"cannot remove an effect that was never added\");\n }\n const [entry] = this.entries.splice(index, 1);\n this.bypassed.delete(node);\n this.refresh();\n if (entry && this.isEntry(entry)) {\n entry.dispose?.();\n }\n }\n\n setOrder(nodes: readonly AudioNode[]): void {\n const isPermutation =\n nodes.length === this.nodes.length &&\n new Set(nodes).size === nodes.length &&\n nodes.every((node) => this.has(node));\n if (!isPermutation) {\n throw this.guardError(\"effect order must be a permutation of the current nodes\");\n }\n const ordered = nodes.map((node) => this.entries.find((entry) => this.isEntry(entry) && entry.handle === node)!);\n let orderedIndex = 0;\n for (let index = 0; index < this.entries.length; index += 1) {\n if (this.isEntry(this.entries[index]!)) {\n this.entries[index] = ordered[orderedIndex++]!;\n }\n }\n this.refresh();\n }\n\n setBypassed(node: AudioNode, bypassed: boolean): void {\n if (!this.has(node)) {\n throw this.guardError(\"cannot bypass an effect that was never added\");\n }\n const alreadyBypassed = this.bypassed.has(node);\n if (bypassed === alreadyBypassed) {\n return;\n }\n if (bypassed) {\n this.bypassed.add(node);\n } else {\n this.bypassed.delete(node);\n }\n this.refresh();\n }\n\n isBypassed(node: AudioNode): boolean {\n return this.bypassed.has(node);\n }\n\n rampParam(node: AudioNode, paramName: string, value: number, options?: { duration?: number; type?: FadeType }): void {\n const entry = this.entries.filter(this.isEntry).find((candidate) => candidate.handle === node);\n if (!entry) {\n console.warn(\n `${this.diagnosticScope}.rampFilterParam: node is not a filter on this ${this.diagnosticScope.toLowerCase()}; ignoring automation of '${paramName}'.`,\n );\n return;\n }\n\n const param = entry.params?.[paramName] ?? this.resolveAudioParam(node, paramName);\n if (!param) {\n console.warn(\n `${this.diagnosticScope}.rampFilterParam: could not resolve AudioParam '${paramName}' on the given node; ignoring.`,\n );\n return;\n }\n\n const now = node.context.currentTime;\n const duration = options?.duration;\n if (duration === undefined || duration <= 0) {\n param.setValueAtTime(value, now);\n return;\n }\n\n const endTime = now + duration / 1000;\n param.setValueAtTime(param.value, now);\n if (options?.type === \"exponential\") {\n param.exponentialRampToValueAtTime(value === 0 ? 0.0001 : value, endTime);\n } else {\n param.linearRampToValueAtTime(value, endTime);\n }\n }\n\n destroy(): void {\n this.destroyed = true;\n this.disconnectEdges();\n for (const entry of this.entries) {\n if (this.isEntry(entry)) {\n try {\n entry.dispose?.();\n } catch {}\n }\n }\n this.entries.length = 0;\n this.bypassed.clear();\n }\n\n private refresh(): void {\n const desired = this.desiredEdges();\n for (const [source, destination] of this.edges) {\n const stillPresent = desired.some(([desiredSource, desiredDestination]) => {\n return desiredSource === source && desiredDestination === destination;\n });\n if (!stillPresent) {\n try {\n source.disconnect(destination);\n } catch {}\n }\n }\n for (const [source, destination] of desired) {\n const alreadyConnected = this.edges.some(([currentSource, currentDestination]) => {\n return currentSource === source && currentDestination === destination;\n });\n if (!alreadyConnected) {\n source.connect(destination);\n }\n }\n this.edges.length = 0;\n this.edges.push(...desired);\n }\n\n private desiredEdges(): Array<readonly [AudioNode, AudioNode]> {\n const active = this.entries.filter(this.isEntry).filter((entry) => !this.bypassed.has(entry.handle));\n if (active.length === 0) {\n return [[this.input, this.output]];\n }\n const desired: Array<readonly [AudioNode, AudioNode]> = [];\n let previous = this.input;\n for (const entry of active) {\n desired.push([previous, entry.input]);\n previous = entry.output;\n }\n desired.push([previous, this.output]);\n return desired;\n }\n\n private normalize(built: BuiltEffect): EffectChainEntry {\n if (!isBuiltEffect(built)) {\n throw this.guardError(\"effect build must return an AudioNode or BuiltEffectGraph\");\n }\n if (isBuiltEffectGraph(built)) {\n return {\n handle: built.handle ?? built.input,\n input: built.input,\n output: built.output,\n params: built.params,\n dispose: built.dispose,\n };\n }\n return {\n handle: built,\n input: built,\n output: built,\n };\n }\n\n private isEntry(slot: EffectChainSlot): slot is EffectChainEntry {\n return \"handle\" in slot;\n }\n\n private guardError(message: string): Error {\n return new Error(`${this.diagnosticScope}: ${message}`);\n }\n\n private resolveAudioParam(node: AudioNode, paramName: string): AudioParam | undefined {\n const parameters = (node as AudioWorkletNode).parameters;\n if (parameters && typeof parameters.get === \"function\") {\n const workletParam = parameters.get(paramName);\n if (this.isAudioParam(workletParam)) {\n return workletParam;\n }\n }\n\n const nativeParam = (node as unknown as Record<string, unknown>)[paramName];\n if (this.isAudioParam(nativeParam)) {\n return nativeParam;\n }\n\n return undefined;\n }\n\n private isAudioParam(value: unknown): value is AudioParam {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AudioParam).setValueAtTime === \"function\" &&\n typeof (value as AudioParam).linearRampToValueAtTime === \"function\" &&\n typeof (value as AudioParam).exponentialRampToValueAtTime === \"function\"\n );\n }\n\n private connectEdge(source: AudioNode, destination: AudioNode): void {\n source.connect(destination);\n this.edges.push([source, destination]);\n }\n\n private disconnectEdges(): void {\n for (const [source, destination] of this.edges) {\n try {\n source.disconnect(destination);\n } catch {}\n }\n this.edges.length = 0;\n }\n}\n","/**\n * Bus — a named summing node with its own filter chain and per-edge gain.\n *\n * A Bus is a first-class routing primitive. Sounds and synths route into a\n * bus via `routeTo`; the bus mixes its inputs through an optional filter\n * chain and emits to one or more downstream targets (other buses or the\n * master bus). Connections out can be either unity-gain (direct) or\n * attenuated through an allocated GainNode (per-edge gain).\n *\n * Internal topology:\n * input → [filter1 → ... → filterN] → output → (sends to targets)\n *\n * `input` and `output` are separate GainNodes so the chain has a stable\n * external interface even as the internal filter chain is rebuilt.\n *\n * Buses are SHARED LIVE — there is no per-playback clone. The filter chain\n * holds the live node instances added via `addFilter`. Adding the same\n * filter to two buses means the same Web Audio node is used; mutating its\n * parameters affects both.\n */\n\nimport type { FadeType } from \"./cacophony\";\nimport type { AudioNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { EffectChain } from \"./effectChain\";\nimport type { BuiltEffect, CacophonyEffect } from \"./effects\";\nimport { isCacophonyBuiltBiquad, isCacophonyEffect } from \"./effects\";\n\n/**\n * Connection target for {@link Bus.connect} / {@link Bus.disconnect}. Either\n * another Bus (we connect into its `input`) or a raw AudioNode (we connect\n * directly to it — escape hatch for advanced wiring).\n */\nexport type BusConnectionTarget = Bus | AudioNode;\n\n/**\n * Structural contract a routed source (e.g. a {@link Sound}) implements so a\n * Bus can move it off itself before teardown. Declared here — rather than\n * importing `Sound` — to avoid an import cycle (`sound.ts` already imports\n * `Bus`). A Bus only ever needs this one method on its inbound sources.\n */\nexport interface BusRoutedSource {\n /**\n * Called by {@link Bus.drainTo} to move this source off the draining bus\n * onto `target`. The source reroutes its primary route and/or any send that\n * targeted `bus`.\n */\n _onBusDrained(bus: Bus, target: Bus): void;\n}\n\n/**\n * A named summing node with a filter chain and per-edge send gain. See\n * module-level docstring for topology.\n */\nexport class Bus {\n /** Stable name for registry lookup, or null for anonymous buses. */\n readonly name: string | null;\n\n /**\n * Entry node — connect upstream sources here (sound playbacks, other bus\n * outputs).\n */\n readonly input: GainNode;\n\n /**\n * Exit node — connected to downstream targets (other bus inputs, master,\n * raw nodes) via {@link connect}.\n */\n readonly output: GainNode;\n\n private readonly _context: BaseContext;\n private readonly _effectChain: EffectChain;\n private readonly _sendGains: Map<BusConnectionTarget, GainNode> = new Map();\n private readonly _directConnections: Set<BusConnectionTarget> = new Set();\n /**\n * Inbound sources currently routed to this bus (primary route and/or sends).\n * A Web Audio node cannot enumerate its own inputs, so sources register\n * themselves here (via {@link _registerRoutedSource}) when they route to\n * this bus and unregister on reroute/cleanup. {@link drainTo} walks this set\n * to move live sounds off the bus before it is torn down.\n */\n private readonly _routedSources = new Set<BusRoutedSource>();\n /**\n * Hook invoked by the owning Cacophony instance to remove this bus from\n * the named-bus registry on destroy. Anonymous buses leave this undefined.\n */\n private readonly _onDestroy?: () => void;\n private readonly _destroyable: boolean;\n private _destroyed = false;\n\n /**\n * @param context Web Audio context the bus's nodes live on.\n * @param name Name to register under, or null for an anonymous bus.\n * @param input Optional pre-existing GainNode to use as the input. Used by\n * the `master` bus to alias `cacophony.globalGainNode`. If omitted, a\n * fresh GainNode is allocated.\n * @param onDestroy Optional registry-cleanup hook fired by destroy().\n * @param destroyable Whether destroy() may tear down this bus.\n */\n constructor(\n context: BaseContext,\n name: string | null = null,\n input?: GainNode,\n onDestroy?: () => void,\n destroyable = true,\n ) {\n this._context = context;\n this.name = name;\n this.input = input ?? context.createGain();\n this.output = context.createGain();\n this._onDestroy = onDestroy;\n this._destroyable = destroyable;\n this._effectChain = new EffectChain(this.input, this.output, \"Bus\");\n }\n\n /** True after {@link destroy} has been called. */\n get destroyed(): boolean {\n return this._destroyed;\n }\n\n /** Output node gain — controls the overall level the bus sends downstream. */\n get gain(): number {\n return this.output.gain.value;\n }\n\n set gain(v: number) {\n this.output.gain.value = v;\n }\n\n /** Live filter chain (read-only view). */\n get filters(): readonly AudioNode[] {\n return this._effectChain.nodes;\n }\n\n /**\n * Add a filter node to the bus's chain. Accepts:\n *\n * - A Cacophony-built BiquadFilterNode (created via\n * `cacophony.createBiquadFilter`) → added directly to the chain.\n * - A {@link CacophonyEffect} → `build(context)` is awaited; the resulting\n * node is added to the chain.\n * - A raw third-party AudioNode → REJECTED. Wrap it with\n * `cacophony.shareEffect(node)` (or a proper CacophonyEffect class) to\n * make the shared-state intent explicit.\n *\n * @returns the built AudioNode that was added to the chain. For a biquad this\n * is the argument itself; for a {@link CacophonyEffect} it is the node\n * produced by `build`. The returned handle can be passed to\n * {@link rampFilterParam} to automate the node's parameters. Existing\n * callers that ignore the result keep working unchanged.\n * @throws if the bus has been destroyed, or if the argument is a raw\n * AudioNode that is not a Cacophony-built biquad.\n */\n addFilter(arg: BiquadFilterNode | CacophonyEffect | AudioNode): Promise<AudioNode> {\n if (!isCacophonyBuiltBiquad(arg, this._context) && !isCacophonyEffect(arg)) {\n throw new Error(\n \"Bus.addFilter rejects raw AudioNodes. Wrap with cacophony.shareEffect(node) or a CacophonyEffect to make the shared-state intent explicit.\",\n );\n }\n\n let reservation: symbol;\n try {\n this._throwIfDestroyed();\n reservation = this._effectChain.reserve();\n } catch (error) {\n return Promise.reject(error);\n }\n\n let built: BuiltEffect | Promise<BuiltEffect>;\n try {\n built = isCacophonyBuiltBiquad(arg, this._context) ? arg : arg.build(this._context);\n } catch (error) {\n this._effectChain.cancel(reservation);\n return Promise.reject(error);\n }\n\n if (built instanceof Promise || (typeof built === \"object\" && built !== null && \"then\" in built)) {\n return Promise.resolve(built).then(\n (resolved) => this._effectChain.resolve(reservation, resolved),\n (error) => {\n this._effectChain.cancel(reservation);\n throw error;\n },\n );\n }\n\n try {\n return Promise.resolve(this._effectChain.resolve(reservation, built));\n } catch (error) {\n this._effectChain.cancel(reservation);\n return Promise.reject(error);\n }\n }\n\n /**\n * Remove a filter node from the bus's chain. The node must have been added\n * via {@link addFilter}; the same object identity is used to match.\n *\n * @throws if the bus has been destroyed or if the node was never added.\n */\n removeFilter(node: AudioNode): void {\n this._throwIfDestroyed();\n this._effectChain.remove(node);\n }\n\n /**\n * Reorder the existing filter chain. `nodes` must be a PERMUTATION of the\n * current filters — the same set of node objects (matched by identity), the\n * same length, with no duplicates — just in a new order. Because the owned\n * {@link EffectChain} reconciles incrementally, only the edges that actually\n * move are reconnected; unchanged edges are left untouched.\n *\n * @throws if the bus has been destroyed, or if `nodes` is not a permutation\n * of the current filters.\n */\n setFilterOrder(nodes: readonly AudioNode[]): void {\n this._throwIfDestroyed();\n this._effectChain.setOrder(nodes);\n }\n\n /**\n * Bypass (or un-bypass) a filter without removing it from the chain. A\n * bypassed filter stays in {@link filters} — its order, identity, and live\n * AudioParams are preserved (so an automation target survives a bypass) — but\n * it is skipped in the audible series chain: the signal is wired around it.\n * Un-bypassing wires it back in at its original position.\n *\n * The reconnect goes through the incremental {@link EffectChain}, so only the\n * seam around `node` is touched — the rest of the chain is left connected.\n *\n * @param node A filter node currently on this bus (from {@link addFilter} or\n * {@link filters}).\n * @param bypassed `true` to skip the node, `false` to wire it back in. A no-op\n * if the node is already in the requested state.\n * @throws if the bus has been destroyed, or if `node` was never added to this\n * bus.\n */\n setFilterBypassed(node: AudioNode, bypassed: boolean): void {\n this._throwIfDestroyed();\n this._effectChain.setBypassed(node, bypassed);\n }\n\n /**\n * Whether `node` is currently bypassed (skipped in the audible chain). Returns\n * `false` for nodes that were never added to this bus.\n */\n isFilterBypassed(node: AudioNode): boolean {\n return this._effectChain.isBypassed(node);\n }\n\n /**\n * Ramp an effect node's parameter to a target value over time. This is the\n * uniform automation handle for filter-chain effects: pass a node obtained\n * from {@link addFilter} (or the {@link filters} getter) and the name of the\n * parameter to drive.\n *\n * Parameter resolution:\n * - If `node` exposes a worklet-style `parameters` AudioParamMap, the param\n * is resolved via `parameters.get(paramName)` (e.g. a worklet effect's\n * named params).\n * - Otherwise, if `node[paramName]` is itself an AudioParam (native nodes\n * such as a biquad expose `.frequency` / `.Q` / `.gain` directly), that is\n * used.\n *\n * Ramp shape (mirrors the codebase fade convention): the target time base is\n * `node.context.currentTime`. With no `duration` (or `duration <= 0`) the\n * value is set immediately via `setValueAtTime(value, now)`. Otherwise the\n * start is pinned with `setValueAtTime(param.value, now)` and the value ramps\n * to `now + duration / 1000` (milliseconds) using `linearRampToValueAtTime`\n * (default) or `exponentialRampToValueAtTime` when `type` is `\"exponential\"`\n * (an exponential target of 0 is floored to 0.0001, matching `fadeTo`).\n *\n * Automation degrades gracefully: if `node` is not on this bus, or the\n * parameter cannot be resolved to an AudioParam, a warning is logged and the\n * call is a no-op. The only condition that throws is a destroyed bus.\n *\n * @param node A filter node currently on this bus (from {@link addFilter}).\n * @param paramName The name of the parameter to automate.\n * @param value The target value.\n * @param options.duration Ramp duration in milliseconds. Absent/`<= 0` sets\n * the value immediately.\n * @param options.type Ramp curve, `\"linear\"` (default) or `\"exponential\"`.\n * @throws if the bus has been destroyed.\n */\n rampFilterParam(\n node: AudioNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n this._throwIfDestroyed();\n this._effectChain.rampParam(node, paramName, value, options);\n }\n\n /**\n * Connect this bus's output to another bus or to a raw AudioNode.\n *\n * If `gain` is omitted or equal to 1, connect directly (output →\n * targetInput). If `gain` is provided, allocate an internal GainNode for\n * per-edge attenuation: output → sendGain → targetInput. The sendGain is\n * tracked so {@link disconnect} can tear it down cleanly.\n *\n * Re-connecting a target that is already wired is a no-op for the direct\n * case; for a gained connection, the existing sendGain's `gain.value` is\n * updated in place (no new edge is allocated).\n *\n * @throws if the bus has been destroyed.\n */\n connect(target: BusConnectionTarget, gain?: number): void {\n this._throwIfDestroyed();\n const targetNode = target instanceof Bus ? target.input : target;\n if (gain === undefined || gain === 1) {\n if (this._directConnections.has(target)) {\n return;\n }\n // If a gained connection exists, tear it down first so the connection\n // mode is unambiguous.\n const existingSend = this._sendGains.get(target);\n if (existingSend) {\n try {\n this.output.disconnect(existingSend);\n } catch {}\n try {\n existingSend.disconnect();\n } catch {}\n this._sendGains.delete(target);\n }\n this.output.connect(targetNode);\n this._directConnections.add(target);\n return;\n }\n const existingSend = this._sendGains.get(target);\n if (existingSend) {\n existingSend.gain.value = gain;\n return;\n }\n // Tear down any direct edge first (so the routing topology to this\n // target stays unambiguous).\n if (this._directConnections.has(target)) {\n try {\n this.output.disconnect(targetNode);\n } catch {}\n this._directConnections.delete(target);\n }\n const sendGain = this._context.createGain();\n sendGain.gain.value = gain;\n this.output.connect(sendGain);\n sendGain.connect(targetNode);\n this._sendGains.set(target, sendGain);\n }\n\n /**\n * Disconnect this bus's output from a target previously connected with\n * {@link connect}. Tears down the allocated sendGain (if any). No-op if\n * the target was never connected.\n *\n * @throws if the bus has been destroyed.\n */\n disconnect(target: BusConnectionTarget): void {\n this._throwIfDestroyed();\n const targetNode = target instanceof Bus ? target.input : target;\n const sendGain = this._sendGains.get(target);\n if (sendGain) {\n try {\n this.output.disconnect(sendGain);\n } catch {}\n try {\n sendGain.disconnect();\n } catch {}\n this._sendGains.delete(target);\n }\n if (this._directConnections.has(target)) {\n try {\n this.output.disconnect(targetNode);\n } catch {}\n this._directConnections.delete(target);\n }\n }\n\n /**\n * Register an inbound source that routes to this bus (primary and/or send).\n * Called by the source when it begins routing here. Idempotent (Set). Safe\n * to call without a destroyed guard — registration during normal routing\n * must never throw — but a destroyed bus has nothing to drain, so this\n * early-returns once destroyed.\n *\n * @internal\n */\n _registerRoutedSource(source: BusRoutedSource): void {\n if (this._destroyed) {\n return;\n }\n this._routedSources.add(source);\n }\n\n /**\n * Unregister an inbound source (it rerouted away or was cleaned up). No-op\n * if the source was never registered.\n *\n * @internal\n */\n _unregisterRoutedSource(source: BusRoutedSource): void {\n this._routedSources.delete(source);\n }\n\n /**\n * Move every source currently routed to this bus onto `target`, so live\n * sounds keep feeding a live bus instead of the dead `input` after this bus\n * is torn down. Each registered source's {@link BusRoutedSource._onBusDrained}\n * reroutes its primary route and/or the send that targeted this bus.\n *\n * @throws if this bus has been destroyed, or if `target` is this bus.\n */\n drainTo(target: Bus): void {\n this._throwIfDestroyed();\n if (target === this) {\n throw new Error(\"Cannot drain a bus to itself\");\n }\n for (const source of [...this._routedSources]) {\n source._onBusDrained(this, target);\n }\n this._routedSources.clear();\n }\n\n /**\n * Tear down the bus — disconnects input, output, every send-gain, every\n * filter, then deregisters from the owner Cacophony's named-bus map.\n * Subsequent `addFilter`/`removeFilter`/`connect`/`disconnect` calls throw.\n *\n * If `options.drainTo` is provided, every source routed to this bus is first\n * rerouted onto that bus (via {@link drainTo}) so live sounds keep playing\n * through a live bus. With no options the default teardown is unchanged:\n * sounds routed to the destroyed bus fall back to master on their next\n * playback (the routeTo machinery checks `destroyed` at preplay).\n */\n destroy(options?: { drainTo?: Bus }): void {\n if (!this._destroyable) {\n throw new Error(\"The master bus cannot be destroyed\");\n }\n if (this._destroyed) {\n return;\n }\n if (options?.drainTo) {\n this.drainTo(options.drainTo);\n }\n this._destroyed = true;\n // Tear down all outgoing send-gain allocations.\n for (const [, sendGain] of this._sendGains) {\n try {\n sendGain.disconnect();\n } catch {}\n }\n this._sendGains.clear();\n this._directConnections.clear();\n // Disconnect only this bus's internal chain edges. Filter nodes may be\n // shared with other buses, so broad node.disconnect() would steal their\n // routes.\n this._effectChain.destroy();\n try {\n this.input.disconnect();\n } catch {}\n try {\n this.output.disconnect();\n } catch {}\n this._onDestroy?.();\n }\n\n private _throwIfDestroyed(): void {\n if (this._destroyed) {\n throw new Error(`Bus '${this.name ?? \"<anonymous>\"}' has been destroyed`);\n }\n }\n}\n","import type { BaseContext } from \"./context\";\nimport type { AudioEventCallbacks, CacheHitEvent, LoadingProgressEvent } from \"./events\";\n\n/**\n * Subset of {@link AudioEventCallbacks} relevant to {@link AudioCache} public API.\n * Lets callers opt in to any combination of loading/cache events without\n * having to import the full union.\n */\nexport type CacheCallbacks = Pick<\n AudioEventCallbacks,\n | \"onLoadingStart\"\n | \"onLoadingProgress\"\n | \"onLoadingComplete\"\n | \"onLoadingError\"\n | \"onCacheHit\"\n | \"onCacheMiss\"\n | \"onCacheError\"\n>;\n\n/**\n * Coerce an unknown caught value into an `Error`. Use at the boundary\n * of `catch (error: unknown)` blocks where downstream typing requires a real\n * `Error` (e.g. `LoadingErrorEvent.error`, `CacheErrorEvent.error`).\n */\nfunction toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\n/**\n * Build the metadata cache key for a given URL. Centralised so the\n * \":meta\" suffix never drifts across writers/readers/cleanup paths.\n */\nfunction metaKey(url: string): string {\n return `${url}:meta`;\n}\n\nclass ByteBoundedLRUCache<K, V> {\n private maxBytes: number;\n private cachedBytes = 0;\n private cache: Map<K, { value: V; bytes: number }>;\n private estimateBytes: (value: V) => number;\n\n constructor(maxBytes: number, estimateBytes: (value: V) => number) {\n this.maxBytes = maxBytes;\n this.estimateBytes = estimateBytes;\n this.cache = new Map();\n }\n\n get(key: K): V | undefined {\n const entry = this.cache.get(key);\n if (entry === undefined) return undefined;\n this.cache.delete(key);\n this.cache.set(key, entry);\n return entry.value;\n }\n\n set(key: K, value: V): void {\n const bytes = this.estimateBytes(value);\n\n if (this.cache.has(key)) {\n this.cachedBytes -= this.cache.get(key)?.bytes ?? 0;\n this.cache.delete(key);\n }\n\n if (bytes > this.maxBytes) {\n return;\n }\n\n while (this.cachedBytes + bytes > this.maxBytes) {\n const first = this.cache.keys().next();\n if (first.done) {\n break;\n }\n const firstKey = first.value;\n const oldest = this.cache.get(firstKey);\n this.cachedBytes -= oldest?.bytes ?? 0;\n this.cache.delete(firstKey);\n }\n\n this.cache.set(key, { value, bytes });\n this.cachedBytes += bytes;\n }\n}\n\ninterface CacheMetadata {\n url: string;\n etag?: string;\n lastModified?: string;\n cacheControl?: string;\n timestamp: number;\n}\n\n/**\n * Validator tokens carried in cache metadata, used to build conditional\n * requests (`If-None-Match`, `If-Modified-Since`).\n */\ntype CacheValidators = Pick<CacheMetadata, \"etag\" | \"lastModified\">;\n\nconst DEFAULT_DECODED_BUFFER_CACHE_BYTES = 64 * 1024 * 1024;\nconst BYTES_PER_FLOAT32_SAMPLE = 4;\n\nfunction estimateAudioBufferBytes(audioBuffer: AudioBuffer): number {\n return audioBuffer.length * audioBuffer.numberOfChannels * BYTES_PER_FLOAT32_SAMPLE;\n}\n\n/**\n * Parse the max-age value from a Cache-Control header.\n *\n * Accepts unquoted (`max-age=3600`) and properly-quoted (`max-age=\"3600\"`)\n * forms only; rejects malformed half-quoted variants like `max-age=\"3600`\n * or `max-age=3600\"`.\n *\n * @returns max-age in seconds, or null if not found / malformed\n */\nfunction parseMaxAge(cacheControlHeader: string | undefined): number | null {\n if (!cacheControlHeader) {\n return null;\n }\n // Either both quotes or neither; (?:...) groups the alternatives.\n const match = cacheControlHeader.match(/max-age\\s*=\\s*(?:\"(\\d+)\"|(\\d+))/i);\n if (!match) return null;\n const captured = match[1] ?? match[2];\n if (captured === undefined) return null;\n const parsed = Number.parseInt(captured, 10);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Check if Cache-Control header contains directives that require revalidation\n * @param cacheControlHeader - The Cache-Control header value\n * @returns true if revalidation is required regardless of age\n */\nfunction requiresRevalidation(cacheControlHeader: string | undefined): boolean {\n if (!cacheControlHeader) {\n return false;\n }\n return /(?:^|,)\\s*(no-cache|no-store|must-revalidate)\\s*(?:,|$)/i.test(cacheControlHeader);\n}\n\nfunction getNetworkErrorType(error: unknown): \"network\" | \"abort\" | \"unknown\" {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return \"abort\";\n }\n if (error instanceof Error) {\n return \"network\";\n }\n return \"unknown\";\n}\n\n/**\n * Parse a `data:` URL into its mime type and decoded bytes.\n * Returns `null` if the URL is malformed (no comma, undecodable base64, etc.)\n * so callers can fire a typed `onLoadingError` instead of crashing.\n */\nfunction parseDataUrl(url: string): { mime: string; bytes: Uint8Array } | null {\n const commaIndex = url.indexOf(\",\");\n if (commaIndex < 0) return null;\n const header = url.slice(5, commaIndex); // strip \"data:\"\n const payload = url.slice(commaIndex + 1);\n const isBase64 = /;base64$/i.test(header);\n const mime = (isBase64 ? header.replace(/;base64$/i, \"\") : header) || \"text/plain\";\n try {\n if (isBase64) {\n const binary = atob(payload);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return { mime, bytes };\n }\n const decoded = decodeURIComponent(payload);\n const bytes = new Uint8Array(decoded.length);\n for (let i = 0; i < decoded.length; i++) {\n bytes[i] = decoded.charCodeAt(i);\n }\n return { mime, bytes };\n } catch {\n return null;\n }\n}\n\nexport interface ICache {\n getAudioBuffer(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n callbacks?: CacheCallbacks,\n ): Promise<AudioBuffer>;\n clearMemoryCache(): void;\n}\n\ntype PendingProgressCallbacks = Pick<CacheCallbacks, \"onLoadingProgress\">;\n\ninterface PendingAudioRequest {\n promise: Promise<AudioBuffer>;\n controller: AbortController;\n callers: number;\n}\n\n/**\n * AudioCache provides efficient caching of audio resources using HTTP caching standards.\n *\n * Features:\n * - Three-layer caching: Memory (LRU) → Browser Cache API → Network\n * - HTTP conditional requests with ETag and Last-Modified support\n * - Robust error handling with cache inconsistency recovery\n *\n * Caching Strategy:\n * - Always makes conditional requests when validation tokens (ETag/Last-Modified) are available\n * - Uses TTL as fallback only when no validation tokens exist\n * - Conditional requests are lightweight (304 responses have no body)\n *\n * @example\n * ```typescript\n * const cache = new AudioCache();\n * const audioBuffer = await cache.getAudioBuffer(audioContext, 'audio.mp3');\n *\n * // Optional: Configure TTL for when no validation tokens exist\n * AudioCache.setCacheExpirationTime(60 * 60 * 1000); // 1 hour\n * ```\n */\nexport class AudioCache implements ICache {\n private pendingRequests = new WeakMap<BaseContext, Map<string, PendingAudioRequest>>();\n private pendingCallbacks = new WeakMap<BaseContext, Map<string, PendingProgressCallbacks[]>>();\n private decodedBuffers = new WeakMap<BaseContext, ByteBoundedLRUCache<string, AudioBuffer>>();\n private static cacheExpirationTime: number = 24 * 60 * 60 * 1000; // 24 hours in milliseconds\n\n public static setCacheExpirationTime(time: number): void {\n AudioCache.cacheExpirationTime = time;\n }\n\n /**\n * Whether the browser Cache API (`caches`) global is available in the\n * current runtime. Uses `typeof` so it never throws when the global is\n * entirely absent (e.g. Node). When this returns `false`, the URL path\n * degrades to fetch + decode (still using the in-memory LRU) instead of\n * touching the persistent Cache API.\n */\n private static isCacheApiAvailable(): boolean {\n return typeof caches !== \"undefined\" && caches != null;\n }\n\n private static async openCache(): Promise<Cache> {\n try {\n return await caches.open(\"audio-cache\");\n } catch (error) {\n console.error(\"Failed to open cache:\", error);\n throw error;\n }\n }\n\n /**\n * Calls all registered callbacks for a specific event type on a URL.\n * Generic over the callback name so the payload type is checked against\n * the canonical {@link CacheCallbacks} shape rather than `any`.\n */\n private static callAllCallbacks<K extends keyof CacheCallbacks>(\n pendingCallbacks: Map<string, PendingProgressCallbacks[]> | undefined,\n url: string,\n callbackName: K,\n eventData: Parameters<NonNullable<CacheCallbacks[K]>>[0],\n ): void {\n const callbacks = pendingCallbacks?.get(url);\n if (callbacks) {\n callbacks.forEach((callbackSet) => {\n const callback = callbackSet[callbackName as \"onLoadingProgress\"];\n if (callback) {\n try {\n // Cast eventData here: the surrounding generic guarantees the\n // payload type matches the chosen callback name.\n (callback as (e: typeof eventData) => void)(eventData);\n } catch (error) {\n console.error(`Error in ${String(callbackName)} callback:`, error);\n }\n }\n });\n }\n }\n\n private async getOrCreatePendingRequest(\n context: BaseContext,\n url: string,\n createRequest: (signal: AbortSignal) => Promise<AudioBuffer | undefined>,\n signal?: AbortSignal,\n callbacks?: PendingProgressCallbacks,\n ): Promise<AudioBuffer> {\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n let pendingRequests = this.pendingRequests.get(context);\n if (pendingRequests === undefined) {\n pendingRequests = new Map();\n this.pendingRequests.set(context, pendingRequests);\n }\n let pendingCallbacks = this.pendingCallbacks.get(context);\n if (pendingCallbacks === undefined) {\n pendingCallbacks = new Map();\n this.pendingCallbacks.set(context, pendingCallbacks);\n }\n\n // Add callbacks to aggregation if provided\n if (callbacks) {\n const existingCallbacks = pendingCallbacks.get(url) || [];\n existingCallbacks.push(callbacks);\n pendingCallbacks.set(url, existingCallbacks);\n }\n\n let pendingRequest = pendingRequests.get(url);\n if (!pendingRequest) {\n const controller = new AbortController();\n const requestPromise = (async () => {\n try {\n const result = await createRequest(controller.signal);\n if (result === undefined) {\n throw new Error(\"Failed to create audio buffer.\");\n }\n return result;\n } finally {\n if (pendingRequests.get(url) === pendingRequest) {\n pendingRequests.delete(url);\n pendingCallbacks.delete(url);\n }\n }\n })();\n\n pendingRequest = { promise: requestPromise, controller, callers: 0 };\n pendingRequests.set(url, pendingRequest);\n }\n\n pendingRequest.callers++;\n return new Promise((resolve, reject) => {\n let settled = false;\n const release = (aborted: boolean) => {\n if (settled) return;\n settled = true;\n signal?.removeEventListener(\"abort\", handleAbort);\n pendingRequest.callers--;\n\n if (aborted && callbacks) {\n const registeredCallbacks = pendingCallbacks.get(url);\n const callbackIndex = registeredCallbacks?.indexOf(callbacks) ?? -1;\n if (callbackIndex >= 0) {\n registeredCallbacks?.splice(callbackIndex, 1);\n }\n }\n\n if (aborted && pendingRequest.callers === 0 && pendingRequests.get(url) === pendingRequest) {\n pendingRequests.delete(url);\n pendingCallbacks.delete(url);\n pendingRequest.controller.abort();\n }\n };\n const handleAbort = () => {\n release(true);\n reject(new DOMException(\"Operation was aborted\", \"AbortError\"));\n };\n\n signal?.addEventListener(\"abort\", handleAbort, { once: true });\n pendingRequest.promise.then(\n (buffer) => {\n if (settled) return;\n release(false);\n resolve(buffer);\n },\n (error: unknown) => {\n if (settled) return;\n release(false);\n reject(error);\n },\n );\n });\n }\n\n private static async updateMetadata(cache: Cache, url: string, data: Partial<CacheMetadata>): Promise<void> {\n const metadata: CacheMetadata = {\n url,\n timestamp: Date.now(),\n ...data,\n };\n\n await cache.put(\n metaKey(url),\n new Response(JSON.stringify(metadata), {\n headers: { \"Content-Type\": \"application/json\" },\n }),\n );\n }\n\n private static async getBufferFromCache(url: string, cache: Cache): Promise<ArrayBuffer | null> {\n try {\n const response = await cache.match(url);\n if (response?.ok) {\n return await response.arrayBuffer();\n }\n return null;\n } catch (error) {\n console.error(\"Failed to get data from cache:\", error);\n return null;\n }\n }\n\n private async fetchAndCacheBuffer(\n context: BaseContext,\n url: string,\n cache: Cache,\n validators?: CacheValidators,\n signal?: AbortSignal,\n callbacks?: Pick<CacheCallbacks, \"onCacheHit\">,\n ): Promise<ArrayBuffer> {\n const etag = validators?.etag;\n const lastModified = validators?.lastModified;\n const headers = new Headers();\n if (etag) headers.append(\"If-None-Match\", etag);\n if (lastModified) headers.append(\"If-Modified-Since\", lastModified);\n\n console.debug(`[AudioCache] Fetching ${url}`, {\n headers: Object.fromEntries(headers.entries()),\n hasEtag: !!etag,\n hasLastModified: !!lastModified,\n });\n\n const fetchResponse = await fetch(url, { headers, signal });\n\n console.debug(`[AudioCache] Response ${url}`, {\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n etag: fetchResponse.headers?.get(\"ETag\"),\n lastModified: fetchResponse.headers?.get(\"Last-Modified\"),\n cacheControl: fetchResponse.headers?.get(\"Cache-Control\"),\n });\n\n if (fetchResponse.status === 304) {\n const cachedResponse = await cache.match(url);\n if (cachedResponse) {\n // Emit cache hit for 304 responses\n if (callbacks?.onCacheHit) {\n const event: CacheHitEvent = {\n url,\n cacheType: \"conditional\",\n timestamp: Date.now(),\n };\n callbacks.onCacheHit(event);\n }\n\n // Update metadata timestamp on revalidation\n const timestamp = Date.now();\n const newCacheControl = fetchResponse.headers?.get(\"Cache-Control\");\n await AudioCache.updateMetadata(cache, url, {\n timestamp,\n etag,\n lastModified,\n // Only update cacheControl if present in response, otherwise preserve existing\n ...(newCacheControl ? { cacheControl: newCacheControl } : {}),\n });\n return await cachedResponse.arrayBuffer();\n } else {\n // Cache inconsistency: 304 response but no cached body\n // This can happen if cache was partially corrupted or cleared\n // Fall back to re-fetching without validation headers\n console.warn(`Cache inconsistency detected for ${url}: 304 response but no cached body. Re-fetching.`);\n\n // Re-fetch without validation headers to get fresh content\n const freshResponse = await fetch(url, { signal });\n if (freshResponse.status === 200) {\n const responseClone = freshResponse.clone();\n const newEtag = freshResponse.headers.get(\"ETag\");\n const newLastModified = freshResponse.headers.get(\"Last-Modified\");\n const newCacheControl = freshResponse.headers.get(\"Cache-Control\");\n\n await AudioCache.writeBufferAndMetadata(cache, url, responseClone, {\n timestamp: Date.now(),\n etag: newEtag || undefined,\n lastModified: newLastModified || undefined,\n cacheControl: newCacheControl || undefined,\n });\n\n // Use progress tracking for cache recovery scenario if body exists\n if (freshResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, freshResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n } else {\n // Fallback for mock responses without body (testing scenario)\n return await freshResponse.arrayBuffer();\n }\n } else {\n throw new Error(\n `Failed to fetch resource after cache inconsistency: ${freshResponse.status} ${freshResponse.statusText}`,\n );\n }\n }\n }\n\n if (fetchResponse.status === 200) {\n const responseClone = fetchResponse.clone();\n const newEtag = fetchResponse.headers.get(\"ETag\");\n const newLastModified = fetchResponse.headers.get(\"Last-Modified\");\n const newCacheControl = fetchResponse.headers.get(\"Cache-Control\");\n\n await AudioCache.writeBufferAndMetadata(cache, url, responseClone, {\n timestamp: Date.now(),\n etag: newEtag || undefined,\n lastModified: newLastModified || undefined,\n cacheControl: newCacheControl || undefined,\n });\n }\n\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n // Use progress tracking for the main response if body exists\n if (fetchResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, fetchResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n } else {\n // Fallback for mock responses without body (testing scenario)\n return await fetchResponse.arrayBuffer();\n }\n }\n\n /**\n * Atomically write a response body and its metadata into the cache.\n * On failure, deletes both partial entries (best-effort via `allSettled`)\n * and rethrows the original error.\n */\n private static async writeBufferAndMetadata(\n cache: Cache,\n url: string,\n response: Response,\n metadata: Partial<CacheMetadata>,\n ): Promise<void> {\n try {\n await Promise.all([cache.put(url, response), AudioCache.updateMetadata(cache, url, metadata)]);\n } catch (error) {\n // Clean up partial cache entries on error; both deletes run regardless.\n await Promise.allSettled([cache.delete(url), cache.delete(metaKey(url))]);\n throw error;\n }\n }\n\n /**\n * Creates a ReadableStream wrapper that tracks download progress.\n * Uses the callback aggregation system to emit progress to all registered listeners.\n * Honours `signal` between reads and releases the underlying reader on\n * any exit path (done, error, abort).\n * @param response - The fetch Response object with ReadableStream body\n * @param url - URL being downloaded (for progress event data and callback lookup)\n * @param signal - Optional AbortSignal observed at chunk boundaries\n * @returns Object containing the progress-tracking stream and total size\n */\n private createProgressTrackingStream(\n context: BaseContext,\n response: Response,\n url: string,\n signal?: AbortSignal,\n ): { stream: ReadableStream<Uint8Array>; total: number | null } {\n // Extract Content-Length from response headers\n const contentLengthHeader = response.headers.get(\"content-length\");\n const total = contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : null;\n\n let loaded = 0;\n\n if (!response.body) {\n // Fallback for responses without body - shouldn't happen for audio files\n throw new Error(\"Response body is null\");\n }\n\n const reader = response.body.getReader();\n const pendingCallbacks = this.pendingCallbacks.get(context);\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n while (true) {\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) {\n // Emit final progress event at 100%\n AudioCache.callAllCallbacks(pendingCallbacks, url, \"onLoadingProgress\", {\n url,\n loaded,\n total,\n progress: total ? 1 : -1, // 100% if total known, -1 if unknown\n timestamp: Date.now(),\n } satisfies LoadingProgressEvent);\n controller.close();\n return;\n }\n\n if (value) {\n loaded += value.byteLength;\n\n // Emit progress event\n const progress = total ? loaded / total : -1;\n AudioCache.callAllCallbacks(pendingCallbacks, url, \"onLoadingProgress\", {\n url,\n loaded,\n total,\n progress,\n timestamp: Date.now(),\n } satisfies LoadingProgressEvent);\n\n controller.enqueue(value);\n }\n }\n } catch (error) {\n controller.error(error);\n } finally {\n reader.releaseLock();\n }\n },\n\n cancel(reason) {\n // Clean up reader when stream is cancelled\n return reader.cancel(reason);\n },\n });\n\n return { stream, total };\n }\n\n /**\n * Collects all chunks from a ReadableStream into a single ArrayBuffer\n * @param stream - The ReadableStream to collect from\n * @returns Promise that resolves to the complete ArrayBuffer\n */\n private static async collectStreamToArrayBuffer(\n stream: ReadableStream<Uint8Array>,\n knownLength?: number,\n ): Promise<ArrayBuffer> {\n const reader = stream.getReader();\n\n try {\n if (knownLength !== undefined && knownLength > 0) {\n // Pre-allocation path: we know the exact size\n const result = new ArrayBuffer(knownLength);\n const uint8View = new Uint8Array(result);\n let offset = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n if (value) {\n uint8View.set(value, offset);\n offset += value.byteLength;\n }\n }\n\n return result;\n } else {\n // Exponential growth path: unknown size\n let buffer = new Uint8Array(8192); // Start with 8KB\n let totalLength = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n if (value) {\n // Grow buffer if needed\n if (totalLength + value.byteLength > buffer.length) {\n const newSize = Math.max(buffer.length * 2, totalLength + value.byteLength);\n const newBuffer = new Uint8Array(newSize);\n newBuffer.set(buffer.subarray(0, totalLength));\n buffer = newBuffer;\n }\n\n buffer.set(value, totalLength);\n totalLength += value.byteLength;\n }\n }\n\n // Return exact-sized ArrayBuffer\n return buffer.slice(0, totalLength).buffer;\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Fetch + decode an audio URL WITHOUT touching the persistent Cache API.\n * Used when the Cache API global (`caches`) is unavailable (e.g. Node).\n *\n * Mirrors the network branch of {@link fetchAndCacheBuffer}/{@link getAudioBuffer}\n * but never reads from or writes to `caches`. Still:\n * - uses the in-memory decoded-buffer LRU (write happens in {@link getAudioBuffer}),\n * - emits progress callbacks via {@link createProgressTrackingStream},\n * - is deduped through {@link getOrCreatePendingRequest} by the caller.\n */\n private async fetchAndDecodeWithoutCache(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n ): Promise<ArrayBuffer> {\n const fetchResponse = await fetch(url, { signal });\n\n if (fetchResponse.status !== 200 && !fetchResponse.ok) {\n throw new Error(`Failed to fetch resource: ${fetchResponse.status} ${fetchResponse.statusText}`);\n }\n\n if (signal?.aborted) {\n throw new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n // Use progress tracking for the response if body exists\n if (fetchResponse.body) {\n const { stream, total } = this.createProgressTrackingStream(context, fetchResponse, url, signal);\n return await AudioCache.collectStreamToArrayBuffer(stream, total || undefined);\n }\n // Fallback for mock responses without body (testing scenario)\n return await fetchResponse.arrayBuffer();\n }\n\n /**\n * Run the shared network-result pipeline: fetch bytes, decode them, emit\n * loading callbacks, and retain the decoded buffer in the in-memory LRU.\n * The caller supplies the fetch strategy so persistent-cache and degraded\n * runtimes share this plumbing without changing their network behavior.\n */\n private async fetchDecodeAndCache(\n context: BaseContext,\n url: string,\n decodedBuffers: ByteBoundedLRUCache<string, AudioBuffer>,\n fetchBuffer: () => Promise<ArrayBuffer>,\n callbacks?: Pick<CacheCallbacks, \"onLoadingComplete\" | \"onLoadingError\" | \"onCacheError\">,\n reportCacheError = false,\n ): Promise<AudioBuffer> {\n try {\n const arrayBuffer = await fetchBuffer();\n let audioBuffer: AudioBuffer;\n try {\n audioBuffer = await AudioCache.decodeAudioData(context, arrayBuffer);\n } catch (error) {\n callbacks?.onLoadingError?.({\n url,\n error: toError(error),\n errorType: \"decode\",\n timestamp: Date.now(),\n });\n throw error;\n }\n\n callbacks?.onLoadingComplete?.({\n url,\n duration: audioBuffer.duration,\n size: arrayBuffer.byteLength,\n timestamp: Date.now(),\n });\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n } catch (error) {\n callbacks?.onLoadingError?.({\n url,\n error: toError(error),\n errorType: getNetworkErrorType(error),\n timestamp: Date.now(),\n });\n if (reportCacheError) {\n callbacks?.onCacheError?.({\n url,\n error: toError(error),\n operation: \"get\",\n timestamp: Date.now(),\n });\n }\n throw error;\n }\n }\n\n private static async decodeAudioData(context: BaseContext, arrayBuffer: ArrayBuffer): Promise<AudioBuffer> {\n try {\n return await context.decodeAudioData(arrayBuffer);\n } catch (error) {\n console.error(\"Failed to decode audio data:\", error);\n throw error;\n }\n }\n\n private static async getMetadataFromCache(url: string, cache: Cache): Promise<CacheMetadata | null> {\n try {\n const metaResponse = await cache.match(metaKey(url));\n if (metaResponse?.ok) {\n return await metaResponse.json();\n }\n return null;\n } catch (error) {\n console.error(\"Failed to get metadata from cache:\", error);\n return null;\n }\n }\n\n /**\n * Get an AudioBuffer for the specified URL, using intelligent caching strategies.\n *\n * Caching Flow:\n * 1. Check memory cache (LRU) for decoded AudioBuffer\n * 2. Check persistent cache for raw ArrayBuffer and metadata\n * 3. Make conditional HTTP request if validation tokens available\n * 4. Decode audio data and cache at all levels\n *\n * The cache prioritizes HTTP conditional requests (ETag/Last-Modified) over TTL\n * to ensure content freshness while maintaining performance through 304 responses.\n *\n * @param context - AudioContext for decoding audio data\n * @param url - URL of the audio resource to fetch\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to decoded AudioBuffer\n * @throws Error if audio cannot be fetched or decoded\n */\n public async getAudioBuffer(\n context: BaseContext,\n url: string,\n signal?: AbortSignal,\n callbacks?: CacheCallbacks,\n ): Promise<AudioBuffer> {\n let decodedBuffers = this.decodedBuffers.get(context);\n if (decodedBuffers === undefined) {\n decodedBuffers = new ByteBoundedLRUCache<string, AudioBuffer>(\n DEFAULT_DECODED_BUFFER_CACHE_BYTES,\n estimateAudioBufferBytes,\n );\n this.decodedBuffers.set(context, decodedBuffers);\n }\n\n // Call loading start callback\n if (callbacks?.onLoadingStart) {\n callbacks.onLoadingStart({\n url,\n timestamp: Date.now(),\n });\n }\n\n // Check if the decoded buffer is already available in memory cache.\n // Single get + narrow avoids the has/get/`!` race.\n const memoryHit = decodedBuffers.get(url);\n if (memoryHit !== undefined) {\n if (callbacks?.onCacheHit) {\n callbacks.onCacheHit({\n url,\n cacheType: \"memory\",\n timestamp: Date.now(),\n });\n }\n return memoryHit;\n }\n\n // handle data: urls\n if (url.startsWith(\"data:\")) {\n const parsed = parseDataUrl(url);\n if (parsed === null) {\n const error = new Error(`Malformed data: URL: ${url.slice(0, 32)}…`);\n if (callbacks?.onLoadingError) {\n callbacks.onLoadingError({\n url,\n error,\n errorType: \"decode\",\n timestamp: Date.now(),\n });\n }\n throw error;\n }\n const audioBuffer = await AudioCache.decodeAudioData(context, parsed.bytes.buffer as ArrayBuffer);\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n }\n\n // When the browser Cache API is unavailable (e.g. Node), transparently\n // degrade to a fetch-only path: no persistent cache read/write, no error\n // log — still using the in-memory LRU, pending-request dedup, and progress\n // callbacks. The available-cache path below is byte-for-byte unchanged.\n if (!AudioCache.isCacheApiAvailable()) {\n return this.getOrCreatePendingRequest(\n context,\n url,\n async (requestSignal) => {\n // No persistent cache, so this is always a miss.\n if (callbacks?.onCacheMiss) {\n callbacks.onCacheMiss({\n url,\n reason: \"not-found\",\n timestamp: Date.now(),\n });\n }\n\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () => this.fetchAndDecodeWithoutCache(context, url, requestSignal),\n callbacks,\n );\n },\n signal,\n { onLoadingProgress: callbacks?.onLoadingProgress },\n );\n }\n\n const cache = await AudioCache.openCache();\n\n const metadata = await AudioCache.getMetadataFromCache(url, cache);\n\n // Determine if we should make a network request\n // This logic implements HTTP caching best practices:\n // 1. Check Cache-Control freshness first (RFC-compliant behavior)\n // 2. If stale, use validation headers for conditional requests\n // 3. Fall back to TTL when no validation tokens exist\n const shouldFetch = (() => {\n if (!metadata) {\n return true; // Must fetch if nothing is cached\n }\n\n // Check for directives that require revalidation\n if (requiresRevalidation(metadata.cacheControl)) {\n return true; // Must revalidate due to no-cache, no-store, or must-revalidate\n }\n\n // Check Cache-Control freshness\n const maxAge = parseMaxAge(metadata.cacheControl);\n if (maxAge !== null) {\n const age = (Date.now() - metadata.timestamp) / 1000;\n if (maxAge > 0 && age < maxAge) {\n return false; // Fresh content, serve from cache\n }\n // If max-age=0 or content is stale, proceed to validation\n }\n\n // Content is stale (or max-age=0), check if we can revalidate\n if (metadata.etag || metadata.lastModified) {\n return true; // Stale but can be validated with conditional request\n }\n\n // No validation headers available, fall back to TTL\n return Date.now() - metadata.timestamp > AudioCache.cacheExpirationTime;\n })();\n\n return this.getOrCreatePendingRequest(\n context,\n url,\n async (requestSignal) => {\n if (shouldFetch) {\n // Cache miss - need to fetch from network\n if (callbacks?.onCacheMiss) {\n callbacks.onCacheMiss({\n url,\n reason: metadata ? \"expired\" : \"not-found\",\n timestamp: Date.now(),\n });\n }\n\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () =>\n this.fetchAndCacheBuffer(\n context,\n url,\n cache,\n { etag: metadata?.etag, lastModified: metadata?.lastModified },\n requestSignal,\n { onCacheHit: callbacks?.onCacheHit },\n ),\n callbacks,\n true,\n );\n } else {\n // Content should be fresh in cache\n const cachedBuffer = await AudioCache.getBufferFromCache(url, cache);\n if (cachedBuffer) {\n // Cache hit from browser cache\n if (callbacks?.onCacheHit) {\n callbacks.onCacheHit({\n url,\n cacheType: \"browser\",\n timestamp: Date.now(),\n });\n }\n\n const audioBuffer = await AudioCache.decodeAudioData(context, cachedBuffer);\n decodedBuffers.set(url, audioBuffer);\n return audioBuffer;\n } else {\n // Cache inconsistency - metadata exists but body is missing\n if (callbacks?.onCacheError) {\n callbacks.onCacheError({\n url,\n error: new Error(\"Cache inconsistency: metadata exists but body is missing\"),\n operation: \"get\",\n timestamp: Date.now(),\n });\n }\n\n // Fallback to network if body missing but metadata is fresh\n return await this.fetchDecodeAndCache(\n context,\n url,\n decodedBuffers,\n () =>\n this.fetchAndCacheBuffer(\n context,\n url,\n cache,\n { etag: metadata?.etag, lastModified: metadata?.lastModified },\n requestSignal,\n { onCacheHit: callbacks?.onCacheHit },\n ),\n callbacks,\n );\n }\n }\n },\n signal,\n { onLoadingProgress: callbacks?.onLoadingProgress },\n );\n }\n\n public clearMemoryCache(): void {\n this.decodedBuffers = new WeakMap();\n this.pendingRequests = new WeakMap();\n this.pendingCallbacks = new WeakMap();\n }\n}\n","export default \"data:audio/wav;base64,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\"","import type { BaseContext } from \"./context\";\n\n/**\n * Autoplay unlock — Howler-parity automatic AudioContext resume on first\n * user gesture.\n *\n * On mobile browsers (especially iOS Safari) an `AudioContext` constructed\n * before any user interaction is created in `suspended` state. It will not\n * produce sound until BOTH:\n *\n * 1. `context.resume()` is called, AND\n * 2. an `AudioBufferSourceNode` is created and started inside the user\n * gesture's call stack (the iOS \"primer\").\n *\n * Many callers do (1) but not (2), and hear silence with no obvious cause.\n * This module watches the context for suspension and arms `touchend` / `click`\n * / `keydown` listeners on `document.body` (or `document` as fallback). A\n * gesture resumes the context, plays a 1-sample silent primer inside the\n * gesture call stack, removes all three listeners, and calls the `onUnlock`\n * callback. If the context suspends again, the listeners are re-armed.\n *\n * Design adapted (not copied) from Howler.js `_unlockAudio` — MIT licensed.\n */\n\nconst UNLOCK_EVENT_TYPES: ReadonlyArray<\"touchend\" | \"click\" | \"keydown\"> = [\"touchend\", \"click\", \"keydown\"];\nconst GESTURE_LISTENER_OPTIONS = { capture: true, passive: true } as const;\n\n/**\n * A `Document`-like type with the methods we need. Avoids a hard dependency\n * on DOM lib types so the package keeps building when `lib: [\"dom\"]` is off.\n */\ninterface DocumentLike {\n body?: EventTargetLike | null;\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\ninterface EventTargetLike {\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\n/**\n * Options for `installAutoplayUnlock`.\n */\nexport interface AutoplayUnlockOptions {\n /**\n * The audio context that should be resumed and primed.\n */\n context: BaseContext;\n\n /**\n * Called after the context has been resumed and the primer buffer has been\n * started. Used by `Cacophony` to emit the public `unlock` event.\n * Errors thrown by the callback are caught and logged so a faulty listener\n * cannot break the unlock flow.\n */\n onUnlock: () => void;\n}\n\n/**\n * Watch for context suspension and install unlock listeners while needed.\n * Returns a cleanup function that removes both the context watcher and any\n * armed gesture listeners (e.g. when a `Cacophony` instance is torn down).\n *\n * If the environment is non-browser (`document` is undefined), this is a\n * no-op and returns a no-op cleanup.\n *\n * @internal\n */\nexport function installAutoplayUnlock(opts: AutoplayUnlockOptions): () => void {\n const { context, onUnlock } = opts;\n\n // Server-side / non-browser: nothing to do.\n if (typeof document === \"undefined\") {\n return () => {};\n }\n\n // `BaseContext.resume` is optional. If this context has no resume() we\n // cannot actually unlock it — installing gesture listeners would be noise,\n // and firing the `unlock` event without a fulfilled resume() would violate\n // the contract (caller would see `unlock` while `Cacophony.locked` stays\n // true, since `locked` is derived from `context.state === \"suspended\"`).\n // Treat the absence as \"this context isn't ours to unlock\" and bail.\n const resume = (context as unknown as { resume?: () => Promise<void> }).resume;\n if (typeof resume !== \"function\") {\n return () => {};\n }\n\n const doc = document as unknown as DocumentLike;\n const target: EventTargetLike = (doc.body as EventTargetLike | null | undefined) ?? doc;\n const observableContext = context as unknown as {\n state?: string;\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n };\n\n let armed = false;\n let resumeInFlight = false;\n let disposed = false;\n\n const removeAll = () => {\n if (!armed) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.removeEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = false;\n };\n\n const handler = () => {\n if (disposed || resumeInFlight) return;\n resumeInFlight = true;\n\n // Remove listeners FIRST so a re-entrant gesture cannot re-trigger us\n // while resume() is in flight.\n removeAll();\n\n // Play the iOS primer INSIDE the gesture call stack. iOS requires that\n // an AudioBufferSourceNode be created and started synchronously inside\n // the gesture handler for the context to truly unlock — calling\n // resume() alone is not sufficient. A 1-sample silent buffer is the\n // minimum that satisfies the requirement and is inaudible.\n try {\n // BaseContext doesn't declare createBuffer because Cacophony's normal\n // code path always feeds an AudioBuffer constructed externally; the\n // primer needs to construct a 1-sample buffer in-place, so we widen\n // the type locally here. Both AudioContext and OfflineAudioContext\n // expose createBuffer.\n const ctxWithCreateBuffer = context as unknown as {\n createBuffer(numberOfChannels: number, length: number, sampleRate: number): unknown;\n };\n const buffer = ctxWithCreateBuffer.createBuffer(1, 1, context.sampleRate);\n const source = context.createBufferSource();\n source.buffer = buffer as any;\n source.connect(context.destination);\n // The load-bearing iOS pattern is start(0); stop(0) on a silent buffer\n // INSIDE the gesture call stack. A 1-sample non-looping buffer would\n // end on its own, but the pair is what the platform contract names.\n source.start(0);\n source.stop(0);\n } catch (err) {\n // Primer failure should not block the rest of the unlock; the\n // context.resume() call below is still useful on platforms that do\n // not require the primer.\n console.warn(\"[cacophony/autoplayUnlock] primer failed:\", err);\n }\n\n // Resume the context, then emit unlock. The `unlock` event signals that\n // audio is actually playable, so we must wait for resume() to fulfill\n // before emitting. On rejection we surface the error and do NOT emit\n // unlock — a failed resume is not an unlock. `resume` is guaranteed to\n // be a function here (guarded at install time above).\n resume.call(context).then(\n () => {\n resumeInFlight = false;\n if (disposed) return;\n try {\n onUnlock();\n } catch (err) {\n console.error(\"[cacophony/autoplayUnlock] onUnlock callback threw:\", err);\n }\n },\n (err: unknown) => {\n resumeInFlight = false;\n if (disposed) return;\n console.warn(\"[cacophony/autoplayUnlock] resume failed:\", err);\n syncGestureListeners();\n },\n );\n };\n\n const armAll = () => {\n if (disposed || armed || resumeInFlight) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.addEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = true;\n };\n\n function syncGestureListeners(): void {\n if (observableContext.state === \"suspended\") {\n armAll();\n } else {\n removeAll();\n }\n }\n\n const handleStateChange = () => {\n if (!disposed) syncGestureListeners();\n };\n\n observableContext.addEventListener?.(\"statechange\", handleStateChange);\n syncGestureListeners();\n\n return () => {\n if (disposed) return;\n disposed = true;\n removeAll();\n observableContext.removeEventListener?.(\"statechange\", handleStateChange);\n };\n}\n","type EventMap = Record<string, unknown>;\ntype EventKey<T extends EventMap> = string & keyof T;\ntype EventListener<T> = (params: T) => void | Promise<void>;\n\ntype ListenerEntry<T extends EventMap, K extends keyof T> = {\n fn: EventListener<T[K]>;\n once: boolean;\n};\n\ntype ListenerStore<T extends EventMap> = Partial<{\n [K in keyof T]: Array<ListenerEntry<T, K>>;\n}>;\n\n/**\n * Type-safe event emitter.\n */\nexport class TypedEventEmitter<T extends EventMap> {\n private listeners: ListenerStore<T> = {};\n\n /**\n * Register event listener.\n * @returns Cleanup function\n * @example\n * const cleanup = emitter.on('play', (playback) => console.log(playback));\n * cleanup(); // Remove listener\n */\n on<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): () => void {\n const list = (this.listeners[eventName] ??= []);\n list.push({ fn, once: false });\n return () => this.off(eventName, fn);\n }\n\n /**\n * Register one-time event listener.\n * The returned cleanup is idempotent: calling it after the listener has\n * auto-removed (because the event fired) is a safe no-op.\n * @returns Cleanup function\n */\n once<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): () => void {\n const list = (this.listeners[eventName] ??= []);\n list.push({ fn, once: true });\n return () => this.off(eventName, fn);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends EventKey<T>>(eventName: K, fn: EventListener<T[K]>): void {\n const listeners = this.listeners[eventName];\n if (listeners) {\n this.listeners[eventName] = listeners.filter((listener) => listener.fn !== fn);\n }\n }\n\n /**\n * Emit event synchronously.\n *\n * Listeners fire in registration order against a snapshot taken at the\n * start of the dispatch. Concurrent mutations to `this.listeners[eventName]`\n * during dispatch — including `off()` calls from inside a listener and a\n * re-entrant `emit()` — do not affect which listeners fire for THIS emit.\n *\n * `once` listeners are removed from the stored array BEFORE iteration, so\n * a re-entrant `emit()` does not see them and they cannot fire twice. An\n * in-listener `off()` of a sibling is preserved because removal operates\n * on the current stored array, not on the pre-emit snapshot.\n */\n emit<K extends EventKey<T>>(eventName: K, params: T[K]): void {\n const stored = this.listeners[eventName];\n if (!stored || stored.length === 0) return;\n // Pre-split: keep stays in storage, fire is the dispatch snapshot.\n // Setting storage to `keep` BEFORE dispatch makes once-removal atomic with\n // dispatch (re-entrant emit cannot see a not-yet-fired once listener) and\n // leaves a stable array for in-listener off()/on() to mutate.\n const fire = stored.slice();\n const keep = stored.filter((listener) => !listener.once);\n this.listeners[eventName] = keep;\n for (const listener of fire) {\n listener.fn(params);\n }\n }\n\n /**\n * Emit event asynchronously with error isolation.\n * Listener errors are logged but don't break other listeners.\n *\n * Same snapshot semantics as `emit`: the dispatched listener set is\n * frozen at call time, and `once` listeners are removed from storage\n * before dispatch so a re-entrant emit cannot re-fire them.\n */\n emitAsync<K extends EventKey<T>>(eventName: K, params: T[K]): Promise<void> {\n const stored = this.listeners[eventName];\n if (!stored || stored.length === 0) return Promise.resolve();\n const fire = stored.slice();\n const keep = stored.filter((listener) => !listener.once);\n this.listeners[eventName] = keep;\n const promises = fire.map((listener) =>\n Promise.resolve()\n .then(() => listener.fn(params))\n .catch((error) => console.error(`Error in listener for event ${eventName}:`, error)),\n );\n return Promise.all(promises).then(() => {});\n }\n\n /**\n * Remove all event listeners.\n */\n removeAllListeners(): void {\n this.listeners = {};\n }\n}\n","import type { Bus } from \"./bus\";\nimport type { BaseSound, FadeType, LoopCount, PlayOptions, Position } from \"./cacophony\";\nimport type { BiquadFilterNode } from \"./context\";\nimport type { Playback } from \"./playback\";\nimport type { Sound } from \"./sound\";\n\nexport class Group implements BaseSound {\n private _position: Position = [0, 0, 0];\n loopCount: LoopCount = 0;\n private playIndex: number = 0;\n\n constructor(public sounds: Sound[] = []) {}\n\n /**\n * Prepares a random sound from the group for playback.\n * @returns The playback object representing the prepared sound, or undefined if the group is empty.\n */\n preplayRandom(): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n const randomSound = this.randomSound();\n const playbacks = randomSound.preplay();\n return playbacks.length > 0 ? playbacks[0] : undefined;\n }\n\n /**\n * Plays a random sound from the group.\n * @param options - Options for configuring fade behavior.\n * @returns The playback object representing the played sound, or undefined if the group is empty.\n */\n playRandom(options?: PlayOptions): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n return this.randomSound().play(options)[0];\n }\n\n /**\n * Prepares the sounds in the group for playback in a specific order.\n *\n * @param shouldLoop - Indicates whether the sounds should be prepared for looping.\n * @returns The playback object representing the first sound being prepared, or undefined if the group is empty.\n */\n preplayOrdered(shouldLoop: boolean = true): Playback | undefined {\n if (this.sounds.length === 0) {\n return undefined;\n }\n if (this.playIndex >= this.sounds.length) {\n return undefined;\n }\n const sound = this.sounds[this.playIndex];\n const playbacks = sound.preplay();\n if (playbacks.length === 0) {\n return undefined;\n }\n this.playIndex = (this.playIndex + 1) % this.sounds.length;\n if (!shouldLoop && this.playIndex === 0) {\n this.playIndex = this.sounds.length;\n }\n return playbacks[0];\n }\n\n /**\n * Plays the sounds in the group in a specific order.\n *\n * @param shouldLoop - Indicates whether the sounds should be played in a loop.\n * @param options - Options for configuring fade behavior.\n * @returns The playback object representing the first sound being played, or undefined if the group is empty.\n */\n playOrdered(shouldLoop: boolean = true, options?: PlayOptions): Playback | undefined {\n if (this.sounds.length === 0 || this.playIndex >= this.sounds.length) {\n return undefined;\n }\n const playbacks = this.sounds[this.playIndex].play(options);\n if (playbacks.length === 0) {\n return undefined;\n }\n this.playIndex = (this.playIndex + 1) % this.sounds.length;\n if (!shouldLoop && this.playIndex === 0) {\n this.playIndex = this.sounds.length;\n }\n return playbacks[0];\n }\n\n /**\n * Restarts ordered playback from the first sound in the group.\n */\n resetOrder(): void {\n this.playIndex = 0;\n }\n\n get duration() {\n return this.sounds.map((sound) => sound.duration).reduce((a, b) => Math.max(a, b), 0);\n }\n\n seek(time: number): void {\n this.sounds.forEach((sound) => sound.seek?.(time));\n }\n\n addSound(sound: Sound): void {\n this.sounds.push(sound);\n }\n\n /**\n * Returns a random sound from the group.\n * @throws Error if the group is empty.\n */\n randomSound(): Sound {\n if (this.sounds.length === 0) {\n throw new Error(\"Cannot get a random sound from an empty group\");\n }\n const randomIndex = Math.floor(Math.random() * this.sounds.length);\n return this.sounds[randomIndex];\n }\n\n preplay(): Playback[] {\n const playbacks = this.sounds.map((sound) => sound.preplay());\n return playbacks.flat();\n }\n\n /***\n * Plays all sounds in the group.\n * @returns {Playback[]} An array of Playback objects, one for each sound in the group.\n */\n\n play(options?: PlayOptions): Playback[] {\n return this.sounds.flatMap((sound) => sound.play(options));\n }\n\n /**\n * A boolean indicating whether any of the sounds in the group are currently playing.\n * @returns {boolean} True if any sound is playing, false otherwise.\n */\n\n get isPlaying(): boolean {\n return this.sounds.some((sound) => sound.isPlaying);\n }\n\n /**\n * Stops all the sounds in the group.\n */\n\n stop(): void {\n this.sounds.forEach((sound) => sound.stop());\n }\n\n pause(): void {\n this.sounds.forEach((sound) => sound.pause());\n }\n\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeTo(value, duration, type))).then(() => {});\n }\n\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeIn(duration, type))).then(() => {});\n }\n\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.fadeOut(duration, type))).then(() => {});\n }\n\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.sounds.map((sound) => sound.stopWithFade(duration, type))).then(() => {});\n }\n\n loop(loopCount?: LoopCount): LoopCount {\n if (loopCount === undefined) {\n return this.loopCount;\n }\n this.loopCount = loopCount;\n this.sounds.forEach((sound) => sound.loop(loopCount));\n return this.loopCount;\n }\n\n /**\n * Adds a filter to all sounds in the group.\n * Filters are cloned to playbacks, not shared.\n */\n addFilter(filter: BiquadFilterNode): void {\n this.sounds.forEach((sound) => sound.addFilter(filter));\n }\n\n /**\n * Removes a filter from all sounds in the group.\n */\n removeFilter(filter: BiquadFilterNode): void {\n this.sounds.forEach((sound) => sound.removeFilter(filter));\n }\n\n /**\n * Routes every sound in this group to the specified Bus (or back to\n * master). Fans the call out to every member; see {@link Sound.routeTo}\n * for full semantics. With a `sendGain`, adds a per-sound send instead\n * of redirecting primary routing.\n */\n routeTo(target: Bus | string, sendGain?: number): void {\n if (sendGain !== undefined) {\n this.sounds.forEach((sound) => sound.routeTo(target, sendGain));\n } else {\n this.sounds.forEach((sound) => sound.routeTo(target));\n }\n }\n\n set position(position: [number, number, number]) {\n this._position = position;\n this.sounds.forEach((sound) => (sound.position = this._position));\n }\n\n get position(): [number, number, number] {\n return this._position;\n }\n\n get volume(): number {\n if (this.sounds.length === 0) {\n return 1;\n }\n return this.sounds.map((sound) => sound.volume).reduce((a, b) => a + b, 0) / this.sounds.length;\n }\n\n set volume(volume: number) {\n this.sounds.forEach((sound) => (sound.volume = volume));\n }\n\n get playbackRate(): number {\n if (this.sounds.length === 0) {\n return 1;\n }\n return this.sounds[0].playbackRate;\n }\n\n set playbackRate(rate: number) {\n this.sounds.forEach((sound) => (sound.playbackRate = rate));\n }\n}\n","import type Hls from \"hls.js\";\nimport type { ErrorData, Events } from \"hls.js\";\n\nconst HLS_NOT_INSTALLED_MESSAGE = \"hls.js is not installed; install hls.js or use a direct stream URL.\";\nconst HLS_UNSUPPORTED_MESSAGE =\n \"HLS playback is unavailable because Media Source Extensions are not supported in this browser; use a direct stream URL.\";\n\ntype HlsErrorListener = (error: Error, recoverable: boolean) => void;\n\nfunction isMissingHlsModule(cause: unknown): boolean {\n let current = cause;\n const visited = new Set<Error>();\n while (current instanceof Error && !visited.has(current)) {\n visited.add(current);\n const code = (current as Error & { code?: unknown }).code;\n if (current.message.includes(\"hls.js\") && (code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\")) {\n return true;\n }\n current = (current as Error & { cause?: unknown }).cause;\n }\n return false;\n}\n\n/**\n * Owns one optional hls.js instance and its media-element attachment.\n *\n * The module is loaded only for `.m3u8` streams without native HLS support, so\n * consumers that do not install the optional peer never load it.\n */\nexport class HlsAdapter {\n private destroyed = false;\n private errorListenerRegistered = false;\n\n private readonly handleError = (_event: string, data: ErrorData): void => {\n const message = `hls.js ${data.type}: ${data.details}`;\n const error = new Error(message, data.error ? { cause: data.error } : undefined);\n this.onError(error, false);\n };\n\n private constructor(\n private readonly hls: Hls,\n private readonly errorEvent: Events.ERROR,\n private readonly onError: HlsErrorListener,\n ) {}\n\n static async create(onError: HlsErrorListener): Promise<HlsAdapter> {\n let HlsConstructor: typeof import(\"hls.js\").default;\n try {\n ({ default: HlsConstructor } = await import(\"hls.js\"));\n } catch (cause) {\n if (isMissingHlsModule(cause)) {\n throw new Error(HLS_NOT_INSTALLED_MESSAGE, { cause });\n }\n throw cause;\n }\n\n if (!HlsConstructor.isSupported()) {\n throw new Error(HLS_UNSUPPORTED_MESSAGE);\n }\n\n return new HlsAdapter(new HlsConstructor(), HlsConstructor.Events.ERROR, onError);\n }\n\n attach(audio: HTMLAudioElement, url: string): void {\n if (this.destroyed) {\n throw new Error(\"HlsAdapter has been destroyed\");\n }\n if (!this.errorListenerRegistered) {\n this.hls.on(this.errorEvent, this.handleError);\n this.errorListenerRegistered = true;\n }\n this.hls.loadSource(url);\n this.hls.attachMedia(audio);\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n if (this.errorListenerRegistered) {\n this.hls.off(this.errorEvent, this.handleError);\n this.errorListenerRegistered = false;\n }\n this.hls.detachMedia();\n this.hls.destroy();\n }\n}\n","/**\n * Minimal logging seam used by Cacophony for host-side (main-thread) diagnostic\n * output. Injecting a logger via {@link RuntimeOptions.logger} — or silencing\n * all output via {@link RuntimeOptions.quiet} — lets embedders (notably Node /\n * headless / CLI hosts) capture or suppress the `[cacophony/worklet]` chatter\n * without monkey-patching `console`.\n */\nexport interface CacophonyLogger {\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\n/**\n * Default logger: forwards each level to the corresponding `console` method.\n *\n * The methods dispatch through `console.*` dynamically (rather than binding the\n * reference once) so that test spies installed on `console.info` / `console.warn`\n * / `console.error` after this object is created are still observed.\n */\nexport const consoleLogger: CacophonyLogger = {\n info: (...args) => console.info(...args),\n warn: (...args) => console.warn(...args),\n error: (...args) => console.error(...args),\n};\n\n/** No-op logger: discards all output. Used when `quiet` is set. */\nexport const noopLogger: CacophonyLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n","import type { BiquadFilterNode } from \"./context\";\n\nexport type FilterCloneOverrides = {\n filters?: BiquadFilterNode[];\n};\n\nexport abstract class FilterManager {\n _filters: BiquadFilterNode[] = [];\n\n /**\n * Adds a BiquadFilterNode to the filter chain.\n * @throws {Error} If the same filter instance is added twice.\n */\n addFilter(filter: BiquadFilterNode) {\n // Prevent adding the same filter instance twice (would break Web Audio graph topology)\n if (this._filters.includes(filter)) {\n throw new Error(\"Cannot add the same filter instance twice\");\n }\n this._filters.push(filter);\n }\n\n /**\n * Removes a BiquadFilterNode from the filter chain by object identity.\n * @throws {Error} If the filter was never added to this container.\n */\n removeFilter(filter: BiquadFilterNode) {\n // Remove by object identity, not by comparing property values\n // This prevents bugs where filters with identical properties can't be removed\n const initialLength = this._filters.length;\n this._filters = this._filters.filter((f) => f !== filter);\n\n // Error if filter was not in the array (nothing was removed)\n if (this._filters.length === initialLength) {\n throw new Error(\"Cannot remove filter that was never added to this container\");\n }\n }\n\n get filters() {\n return this._filters;\n }\n\n addFilters(filters: BiquadFilterNode[]) {\n filters.forEach((filter) => this.addFilter(filter));\n }\n\n cleanup(): void {\n // Disconnect all filters before removing them\n this._filters.forEach((filter) => filter.disconnect());\n this._filters = [];\n }\n}\n","import type { PanType, Position } from \"./cacophony\";\nimport type { BaseContext, PannerNode, StereoPannerNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\n\n/**\n * The HRTF-relevant subset of {@link PannerOptions}. Derived via `Pick` so the\n * set of fields tracks the lib.dom definition rather than being re-listed.\n */\nexport type HrtfPannerOptions = Pick<\n PannerOptions,\n | \"coneInnerAngle\"\n | \"coneOuterAngle\"\n | \"coneOuterGain\"\n | \"distanceModel\"\n | \"maxDistance\"\n | \"channelCount\"\n | \"channelCountMode\"\n | \"channelInterpretation\"\n | \"panningModel\"\n | \"refDistance\"\n | \"rolloffFactor\"\n | \"positionX\"\n | \"positionY\"\n | \"positionZ\"\n | \"orientationX\"\n | \"orientationY\"\n | \"orientationZ\"\n>;\n\n/**\n * Canonical type describing the 3D-audio configuration for a sound, discriminated\n * by {@link PanType}.\n *\n * - For `panType: \"stereo\"` the only relevant field is `stereoPan` (-1..1). HRTF\n * position/orientation fields are intentionally not present on this variant\n * so that callers cannot accidentally set them on a stereo source.\n * - For `panType: \"HRTF\"` the variant carries the {@link HrtfPannerOptions}\n * subset plus an optional `[x,y,z]` position tuple.\n *\n * Containers / mixins exposing this type guarantee the HRTF variant is fully\n * populated (`Required`) so callers can read fields without further narrowing.\n * Inputs that only carry a partial override (e.g. clone calls) use the looser\n * {@link PanCloneOverrides} shape.\n */\nexport type ThreeDOptions =\n | { panType: \"stereo\"; stereoPan: number }\n | ({ panType: \"HRTF\"; position?: Position } & Required<HrtfPannerOptions>);\n\nexport type PanCloneOverrides = {\n panType?: PanType;\n stereoPan?: number; // -1 (left) to 1 (right)\n threeDOptions?: Partial<HrtfPannerOptions>; // HRTF panning only\n position?: Position; // HRTF panning only, [x, y, z]\n};\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\nexport function PannerMixin<TBase extends Constructor>(Base: TBase) {\n abstract class PannerMixin extends Base {\n panner?: PannerNode | StereoPannerNode;\n _panType: PanType = \"stereo\";\n\n get panType(): PanType {\n return this._panType;\n }\n\n setPanType(panType: PanType, audioContext: BaseContext) {\n if (this._panType === panType && this.panner) {\n // If the pan type is already set and a panner exists, do nothing\n return;\n }\n\n // Clean up existing panner if it exists\n if (this.panner) {\n this.panner.disconnect();\n }\n\n this._panType = panType;\n if (panType === \"stereo\") {\n this.panner = audioContext.createStereoPanner();\n } else {\n this.panner = audioContext.createPanner();\n }\n }\n\n setPannerNode(pannerNode: PannerNode) {\n this.panner = pannerNode;\n }\n\n /**\n * Gets the stereo panning value.\n * @returns {number | null} The current stereo pan value, or null if stereo panning is not applicable.\n */\n\n get stereoPan(): number | null {\n if (this.panType === \"stereo\") {\n return (this.panner as StereoPannerNode).pan.value;\n }\n return null;\n }\n\n /**\n * Sets the stereo panning value.\n * @param {number} value - The stereo pan value to set, between -1 (left) and 1 (right).\n * @throws {Error} Throws an error if stereo panning is not available, if the sound has been cleaned up, or if the value is out of bounds.\n */\n\n set stereoPan(value: number) {\n if (this.panType !== \"stereo\") {\n throw new Error(\"Stereo panning is not available when using HRTF.\");\n }\n if (!this.panner) {\n throw new Error(\"Cannot set stereo pan of a sound that has been cleaned up\");\n }\n if (value < -1 || value > 1) {\n throw new RangeError(\"Stereo pan must be between -1 and 1.\");\n }\n (this.panner as StereoPannerNode).pan.setValueAtTime(value, this.panner.context.currentTime);\n }\n\n /**\n * Gets the 3D audio options if HRTF panning is used.\n * @returns {ThreeDOptions} The current 3D audio options (HRTF variant).\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n get threeDOptions(): ThreeDOptions {\n if (!this.panner) {\n throw new Error(\"Cannot get 3D options of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot get 3D options of a sound that is not using HRTF\");\n }\n const panner = this.panner as PannerNode;\n return {\n panType: \"HRTF\",\n coneInnerAngle: panner.coneInnerAngle,\n coneOuterAngle: panner.coneOuterAngle,\n coneOuterGain: panner.coneOuterGain,\n distanceModel: panner.distanceModel,\n maxDistance: panner.maxDistance,\n channelCount: this.panner.channelCount,\n channelCountMode: panner.channelCountMode,\n channelInterpretation: panner.channelInterpretation,\n panningModel: panner.panningModel,\n refDistance: panner.refDistance,\n rolloffFactor: panner.rolloffFactor,\n positionX: panner.positionX.value,\n positionY: panner.positionY.value,\n positionZ: panner.positionZ.value,\n orientationX: panner.orientationX.value,\n orientationY: panner.orientationY.value,\n orientationZ: panner.orientationZ.value,\n };\n }\n\n /**\n * Sets the 3D audio options for HRTF panning.\n * Accepts either a full {@link ThreeDOptions} value or a partial HRTF override.\n * Any field omitted from the input is left at its current value on the underlying PannerNode.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n set threeDOptions(options: ThreeDOptions | Partial<HrtfPannerOptions>) {\n if (!this.panner) {\n throw new Error(\"Cannot set 3D options of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot set 3D options of a sound that is not using HRTF\");\n }\n // If caller passed a stereo-variant ThreeDOptions to an HRTF panner, reject.\n if (\"panType\" in options && options.panType === \"stereo\") {\n throw new Error(\"Cannot apply stereo ThreeDOptions to an HRTF panner\");\n }\n const panner = this.panner as PannerNode;\n panner.coneInnerAngle = options.coneInnerAngle !== undefined ? options.coneInnerAngle : panner.coneInnerAngle;\n panner.coneOuterAngle = options.coneOuterAngle !== undefined ? options.coneOuterAngle : panner.coneOuterAngle;\n panner.coneOuterGain = options.coneOuterGain !== undefined ? options.coneOuterGain : panner.coneOuterGain;\n panner.distanceModel = options.distanceModel ?? panner.distanceModel;\n panner.maxDistance = options.maxDistance !== undefined ? options.maxDistance : panner.maxDistance;\n panner.channelCount = options.channelCount !== undefined ? options.channelCount : panner.channelCount;\n panner.channelCountMode = options.channelCountMode ?? panner.channelCountMode;\n panner.channelInterpretation = options.channelInterpretation ?? panner.channelInterpretation;\n panner.panningModel = options.panningModel ?? panner.panningModel;\n panner.refDistance = options.refDistance !== undefined ? options.refDistance : panner.refDistance;\n panner.rolloffFactor = options.rolloffFactor !== undefined ? options.rolloffFactor : panner.rolloffFactor;\n panner.positionX.value = options.positionX !== undefined ? options.positionX : panner.positionX.value;\n panner.positionY.value = options.positionY !== undefined ? options.positionY : panner.positionY.value;\n panner.positionZ.value = options.positionZ !== undefined ? options.positionZ : panner.positionZ.value;\n panner.orientationX.value = options.orientationX !== undefined ? options.orientationX : panner.orientationX.value;\n panner.orientationY.value = options.orientationY !== undefined ? options.orientationY : panner.orientationY.value;\n panner.orientationZ.value = options.orientationZ !== undefined ? options.orientationZ : panner.orientationZ.value;\n }\n\n /**\n * Sets the position of the audio source in 3D space (HRTF panning only).\n * @param {Position} position - The [x, y, z] coordinates of the audio source.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n set position(position: Position) {\n if (!this.panner) {\n throw new Error(\"Cannot move a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot move a sound that is not using HRTF\");\n }\n const [x, y, z] = position;\n const panner = this.panner as PannerNode;\n panner.positionX.value = x;\n panner.positionY.value = y;\n panner.positionZ.value = z;\n }\n\n /**\n * Gets the position of the audio source in 3D space (HRTF panning only).\n * @returns {Position} The [x, y, z] coordinates of the audio source.\n * @throws {Error} Throws an error if the sound has been cleaned up or if HRTF panning is not used.\n */\n\n get position(): Position {\n if (!this.panner) {\n throw new Error(\"Cannot get position of a sound that has been cleaned up\");\n }\n if (this.panType !== \"HRTF\") {\n throw new Error(\"Cannot get position of a sound that is not using HRTF\");\n }\n const panner = this.panner as PannerNode;\n return [panner.positionX.value, panner.positionY.value, panner.positionZ.value];\n }\n\n cleanup(): void {\n if (this.panner) {\n this.panner.disconnect();\n this.panner = undefined;\n }\n super.cleanup();\n }\n }\n\n return PannerMixin;\n}\n","import type { FadeType } from \"./cacophony\";\nimport type { GainNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\n\nexport type VolumeCloneOverrides = {\n volume?: number;\n};\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\nexport interface VolumeControls {\n gainNode?: GainNode;\n _isFading: boolean;\n setGainNode(gainNode: GainNode): void;\n cleanup(): void;\n get volume(): number;\n set volume(value: number);\n get isFading(): boolean;\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;\n cancelFade(): void;\n fadeIn(duration: number, type?: FadeType): Promise<void>;\n fadeOut(duration: number, type?: FadeType): Promise<void>;\n}\n\ntype VolumeMixinConstructor<TBase extends Constructor> = TBase & (abstract new (...args: any[]) => VolumeControls);\n\ntype ActiveFade = {\n timeout: ReturnType<typeof setTimeout>;\n resolve: () => void;\n node: GainNode;\n target: number;\n type: FadeType;\n};\n\nexport function VolumeMixin<TBase extends Constructor>(Base: TBase): VolumeMixinConstructor<TBase>;\nexport function VolumeMixin<TBase extends Constructor>(Base: TBase) {\n abstract class VolumeMixin extends Base {\n gainNode?: GainNode;\n _fadeTimeout?: ReturnType<typeof setTimeout>;\n _isFading: boolean = false;\n private _activeFade?: ActiveFade;\n\n setGainNode(gainNode: GainNode) {\n this.gainNode = gainNode;\n }\n\n cleanup(): void {\n // cancelFade resolves any pending fade promise synchronously, so\n // awaiters of an in-flight fade unblock before the gainNode is torn down.\n this.cancelFade();\n if (this.gainNode) {\n this.gainNode.disconnect();\n this.gainNode = undefined;\n }\n super.cleanup();\n }\n\n /**\n * Gets the current volume of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n get volume(): number {\n if (!this.gainNode) {\n throw new Error(\"Cannot get volume of a sound that has been cleaned up\");\n }\n return this.gainNode.gain.value;\n }\n\n /**\n * Sets the volume of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n set volume(v: number) {\n if (!this.gainNode) {\n throw new Error(\"Cannot set volume of a sound that has been cleaned up\");\n }\n this.cancelFade();\n this.gainNode.gain.value = v;\n }\n\n /**\n * Whether a fade is currently in progress.\n */\n get isFading(): boolean {\n return this._isFading;\n }\n\n /**\n * Fades the volume to a target value over a duration.\n * @param {number} value - The target volume (0 to 1).\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type, \"linear\" or \"exponential\". Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes on a live context, or when its automation has been\n * scheduled on an offline context.\n */\n fadeTo(value: number, duration: number, type: FadeType = \"linear\"): Promise<void> {\n if (!this.gainNode) {\n throw new Error(\"Cannot fade a sound that has been cleaned up\");\n }\n this.cancelFade();\n\n // Capture the gainNode at fade start so the timeout closes over the node\n // present when fadeTo was called -- not whichever node (or undefined)\n // this.gainNode happens to point at when the timer fires.\n const node = this.gainNode;\n const now = node.context.currentTime;\n const endTime = now + duration / 1000;\n\n node.gain.setValueAtTime(node.gain.value, now);\n\n if (type === \"exponential\") {\n node.gain.exponentialRampToValueAtTime(value === 0 ? 0.0001 : value, endTime);\n } else {\n node.gain.linearRampToValueAtTime(value, endTime);\n }\n\n if (\"startRendering\" in node.context) {\n if (value === 0 && type === \"exponential\") {\n node.gain.setValueAtTime(0, endTime);\n }\n this._isFading = false;\n return Promise.resolve();\n }\n\n this._isFading = true;\n\n return new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n // If this fade was superseded (cancelled / re-fired) the active-fade\n // slot has already been cleared by cancelFade. Bail without resolving\n // a second time.\n if (this._activeFade?.timeout !== timeout) {\n return;\n }\n this._activeFade = undefined;\n this._fadeTimeout = undefined;\n this._isFading = false;\n // Web Audio's exponentialRampToValueAtTime requires a strict-positive\n // target, so the ramp went to 0.0001. Snap to 0 at completion.\n if (value === 0 && type === \"exponential\") {\n node.gain.value = 0;\n }\n resolve();\n }, duration);\n this._activeFade = { timeout, resolve, node, target: value, type };\n this._fadeTimeout = timeout;\n });\n }\n\n /**\n * Cancels any in-progress fade. The fade's pending promise is resolved\n * (Option A: cancellation is not an error; awaiters learn the outcome by\n * reading the resulting volume). Safe to call repeatedly.\n */\n cancelFade(): void {\n const active = this._activeFade;\n this._activeFade = undefined;\n if (this._fadeTimeout !== undefined) {\n clearTimeout(this._fadeTimeout);\n this._fadeTimeout = undefined;\n }\n if (active && this.gainNode) {\n const node = this.gainNode;\n const now = node.context.currentTime;\n node.gain.cancelScheduledValues(now);\n // A cancelled fade-to-zero exponential would otherwise leave the gain\n // latched at the 0.0001 ramp target. Snap to 0 so the audible state\n // matches the caller's intent at the moment they cancelled.\n if (active.target === 0 && active.type === \"exponential\") {\n node.gain.value = 0;\n } else {\n // For all other cancellations, leave the gain wherever the ramp had\n // reached -- pin it there so no further scheduled values apply.\n node.gain.setValueAtTime(node.gain.value, now);\n }\n }\n this._isFading = false;\n // Resolve LAST: any handler attached to the fade promise will observe\n // the post-cancel volume and isFading === false.\n if (active) {\n active.resolve();\n }\n }\n\n /**\n * Fades in from silence to the current volume.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n if (!this.gainNode) {\n throw new Error(\"Cannot fade a sound that has been cleaned up\");\n }\n const target = this.gainNode.gain.value;\n this.gainNode.gain.setValueAtTime(0.0001, this.gainNode.context.currentTime);\n return this.fadeTo(target, duration, type);\n }\n\n /**\n * Fades out from the current volume to silence.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return this.fadeTo(0, duration, type);\n }\n }\n\n return VolumeMixin;\n}\n","import type { Bus } from \"./bus\";\nimport type { FadeType } from \"./cacophony\";\nimport type { PlaybackContainer } from \"./container\";\nimport type { AudioNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { EffectChain } from \"./effectChain\";\nimport type { CacophonyEffect } from \"./effects\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { PlaybackEvents } from \"./events\";\nimport { FilterManager } from \"./filters\";\nimport { PannerMixin } from \"./pannerMixin\";\nimport { VolumeMixin } from \"./volumeMixin\";\n\nexport type PlaybackState = \"unplayed\" | \"playing\" | \"paused\" | \"stopped\";\n\nexport abstract class BasePlayback extends PannerMixin(VolumeMixin(FilterManager)) {\n public source?: AudioNode;\n protected _effectChain?: EffectChain;\n private _effectContext?: BaseContext;\n /**\n * Per-playback send-gain allocations owned by the shared routing state\n * machine. Cleanup disconnects every allocation deterministically.\n */\n _sendGains: Map<Bus, GainNode> = new Map();\n protected _state: PlaybackState = \"unplayed\";\n public origin: PlaybackContainer;\n public eventEmitter: TypedEventEmitter<PlaybackEvents> = new TypedEventEmitter<PlaybackEvents>();\n\n constructor(origin: PlaybackContainer) {\n super();\n this.origin = origin;\n }\n\n protected setEffectChainEndpoints(input: AudioNode, output: AudioNode): void {\n this._effectContext =\n (input.context as BaseContext | undefined) ??\n (this.origin as PlaybackContainer & { context?: BaseContext }).context;\n if (this._effectChain) {\n this._effectChain.setEndpoints(input, output);\n return;\n }\n this._effectChain = new EffectChain(input, output, this.constructor.name);\n }\n\n /** Build and splice an effect into this live playback's pre-panner chain. */\n addEffect(effect: CacophonyEffect): Promise<AudioNode> {\n try {\n return this.materializeEffect(effect);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /** @internal Source preplay path that preserves synchronous native-build failures. */\n _addSourceEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect);\n }\n\n /** @internal Materialize a source-level biquad recipe and retain filter API compatibility. */\n _addFilterEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect, (handle) => {\n this._filters.push(handle as BiquadFilterNode);\n });\n }\n\n private materializeEffect(effect: CacophonyEffect, onBuilt?: (handle: AudioNode) => void): Promise<AudioNode> {\n if (!this._effectChain || !this._effectContext) {\n throw new Error(\"Cannot add an effect before the playback effect chain is initialized\");\n }\n const chain = this._effectChain;\n const reservation = chain.reserve();\n let built;\n try {\n built = effect.build(this._effectContext);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n if (built instanceof Promise || (typeof built === \"object\" && built !== null && \"then\" in built)) {\n return Promise.resolve(built).then(\n (resolved) => {\n const handle = chain.resolve(reservation, resolved);\n onBuilt?.(handle);\n return handle;\n },\n (error) => {\n chain.cancel(reservation);\n throw error;\n },\n );\n }\n try {\n const handle = chain.resolve(reservation, built);\n onBuilt?.(handle);\n return Promise.resolve(handle);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n }\n\n rampEffectParam(\n handle: AudioNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate an effect before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(handle, paramName, value, options);\n }\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot add a filter before the playback effect chain is initialized\");\n }\n this._effectChain.add(filter, this._filters.length);\n this._filters.push(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot remove a filter before the playback effect chain is initialized\");\n }\n this._effectChain.remove(filter);\n this._filters = this._filters.filter((candidate) => candidate !== filter);\n }\n\n setFilterOrder(filters: readonly BiquadFilterNode[]): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot reorder filters before the playback effect chain is initialized\");\n }\n const nonFilters = this._effectChain.nodes.filter((node) => !this._filters.includes(node as BiquadFilterNode));\n this._effectChain.setOrder([...filters, ...nonFilters]);\n this._filters = [...filters];\n }\n\n setFilterBypassed(filter: BiquadFilterNode, bypassed: boolean): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot bypass a filter before the playback effect chain is initialized\");\n }\n this._effectChain.setBypassed(filter, bypassed);\n }\n\n isFilterBypassed(filter: BiquadFilterNode): boolean {\n return this._effectChain?.isBypassed(filter) ?? false;\n }\n\n rampFilterParam(\n filter: BiquadFilterNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate a filter before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(filter, paramName, value, options);\n }\n\n abstract play(): [this];\n abstract pause(): void;\n abstract stop(): void;\n\n /**\n * Checks if the audio is currently playing.\n */\n\n get isPlaying(): boolean {\n return this._state === \"playing\";\n }\n\n get isPaused(): boolean {\n return this._state === \"paused\";\n }\n\n protected markPlaying(): void {\n this._state = \"playing\";\n }\n\n protected markPaused(): void {\n this._state = \"paused\";\n }\n\n protected markStopped(): void {\n this._state = \"stopped\";\n }\n\n protected emitPlayStarted(isResume: boolean): void {\n this.markPlaying();\n this.emit(\"play\", this);\n if (isResume) {\n this.emit(\"resume\", undefined);\n }\n this.origin.cacophony?.emit(\"globalPlay\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitPaused(): void {\n this.markPaused();\n this.emit(\"pause\", undefined);\n this.origin.cacophony?.emit(\"globalPause\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitStopped(): void {\n this.markStopped();\n this.emit(\"stop\", undefined);\n this.origin.cacophony?.emit(\"globalStop\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n public emit<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n public async emitAsync<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Fades the volume to a target value, emitting fadeStart and fadeEnd events.\n */\n fadeTo(value: number, duration: number, type: FadeType = \"linear\"): Promise<void> {\n this.emit(\"fadeStart\", { target: value, duration, type });\n return super.fadeTo(value, duration, type).then(() => {\n this.emit(\"fadeEnd\", undefined);\n });\n }\n\n /**\n * Cancels any in-progress fade, emitting fadeCancel if a fade was active.\n */\n cancelFade(): void {\n const wasFading = this._isFading;\n super.cancelFade();\n if (wasFading) {\n this.emit(\"fadeCancel\", undefined);\n }\n }\n\n cleanup(): void {\n for (const sendGain of this._sendGains.values()) {\n try {\n sendGain.disconnect();\n } catch {\n // The node may already have been disconnected externally.\n }\n }\n this._sendGains.clear();\n this._effectChain?.destroy();\n this._effectChain = undefined;\n this._effectContext = undefined;\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","import type { BasePlayback } from \"./basePlayback\";\nimport type { Cacophony, FadeType, Position } from \"./cacophony\";\nimport type { BiquadFilterNode } from \"./context\";\nimport type { FilterManager } from \"./filters\";\nimport type { HrtfPannerOptions, ThreeDOptions } from \"./pannerMixin\";\n\ntype Constructor<T = FilterManager> = abstract new (...args: any[]) => T;\n\n/**\n * Fully-populated HRTF defaults used to initialize the container's canonical\n * `_threeDOptions` and to recover a coherent HRTF state when a partial HRTF\n * override is applied to a stereo-variant container.\n */\nconst defaultHrtfThreeDOptions: ThreeDOptions = {\n panType: \"HRTF\",\n coneInnerAngle: 360,\n coneOuterAngle: 360,\n coneOuterGain: 0,\n distanceModel: \"inverse\",\n maxDistance: 10000,\n channelCount: 2,\n channelCountMode: \"clamped-max\",\n channelInterpretation: \"speakers\",\n panningModel: \"HRTF\",\n refDistance: 1,\n rolloffFactor: 1,\n positionX: 0,\n positionY: 0,\n positionZ: 0,\n orientationX: 0,\n orientationY: 0,\n orientationZ: 0,\n};\n\n/**\n * Public structural contract of the class returned by the {@link PlaybackContainer}\n * mixin factory. Describes the surface that consumers can rely on when they hold\n * a reference to a container (for example, `BasePlayback.origin`).\n *\n * `stereoPan` is `number` (centered = `0`); the storage and setter only ever\n * carry a number, so the public read shape matches.\n */\nexport interface PlaybackContainer {\n cacophony?: Cacophony;\n playbacks: BasePlayback[];\n _position: Position;\n _stereoPan: number;\n _threeDOptions: ThreeDOptions;\n _volume: number;\n preplay(): BasePlayback[];\n play(): BasePlayback[];\n stop(): void;\n pause(): void;\n addFilter(filter: BiquadFilterNode): void;\n removeFilter(filter: BiquadFilterNode): void;\n get isPlaying(): boolean;\n get position(): Position;\n set position(position: Position);\n get threeDOptions(): ThreeDOptions;\n set threeDOptions(options: ThreeDOptions | Partial<HrtfPannerOptions>);\n get stereoPan(): number;\n set stereoPan(value: number);\n get volume(): number;\n set volume(volume: number);\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;\n fadeIn(duration: number, type?: FadeType): Promise<void>;\n fadeOut(duration: number, type?: FadeType): Promise<void>;\n stopWithFade(duration: number, type?: FadeType): Promise<void>;\n}\n\ntype PlaybackContainerConstructor<TBase extends Constructor> = TBase &\n (abstract new (\n ...args: any[]\n ) => PlaybackContainer);\n\nexport function PlaybackContainer<TBase extends Constructor>(Base: TBase): PlaybackContainerConstructor<TBase>;\nexport function PlaybackContainer<TBase extends Constructor>(Base: TBase) {\n abstract class PlaybackContainer extends Base {\n playbacks: BasePlayback[] = [];\n _position: Position = [0, 0, 0];\n _stereoPan: number = 0;\n /**\n * Canonical 3D-audio configuration storage. Always holds a fully-populated\n * HRTF variant ({@link ThreeDOptions} with `panType: \"HRTF\"`) so callers\n * reading via the getter never see undefined fields. Setting the stereo\n * variant replaces this with the stereo shape (no stale HRTF fields).\n */\n _threeDOptions: ThreeDOptions = { ...defaultHrtfThreeDOptions };\n _volume: number = 1;\n\n abstract preplay(): BasePlayback[];\n\n /**\n * Starts playback of the sound and returns a Playback instance representing this particular playback.\n * Multiple Playback instances can be created by calling this method multiple times,\n * allowing for the same sound to be played concurrently with different settings.\n */\n\n play(): BasePlayback[] {\n const playback = this.preplay();\n playback.forEach((p) => p.play());\n return playback;\n }\n\n /**\n * Stops all current playbacks of the sound immediately. This will halt the sound regardless of how many times it has been played.\n */\n\n stop() {\n this.playbacks.forEach((p) => {\n p.stop();\n p.cleanup();\n });\n this.playbacks = [];\n }\n\n /**\n * Pauses all current playbacks of the sound.\n */\n\n pause(): void {\n this.playbacks.forEach((playback) => playback.pause());\n }\n\n /**\n * Adds a BiquadFilterNode to the container's filter chain.\n * Filters are cloned when creating new playbacks - existing playbacks are not affected.\n * Each playback gets independent filter instances for isolated processing.\n */\n\n addFilter(filter: BiquadFilterNode): void {\n super.addFilter(filter);\n // Note: Filters are cloned when creating new playbacks.\n // Existing playbacks remain unchanged to maintain independent filter chains.\n }\n\n /**\n * Removes a BiquadFilterNode from the container's filter chain.\n * Only affects future playbacks - existing playbacks retain their cloned filters.\n * @throws {Error} If the filter was never added to this container.\n */\n\n removeFilter(filter: BiquadFilterNode): void {\n super.removeFilter(filter);\n // Note: Existing playbacks have cloned filter instances and are not affected.\n }\n\n /**\n * Returns a boolean indicating whether the object is currently playing.\n * an object is playing if any of its playbacks are currently playing.\n */\n\n get isPlaying(): boolean {\n return this.playbacks.some((p) => p.isPlaying);\n }\n\n /**\n * Retrieves the current 3D spatial position of the sound in the audio context.\n * The position is returned as an array of three values[x, y, z].\n */\n\n get position(): Position {\n if (this._threeDOptions.panType === \"stereo\") {\n return [0, 0, 0];\n }\n return [this._threeDOptions.positionX, this._threeDOptions.positionY, this._threeDOptions.positionZ];\n }\n\n /**\n * Sets the 3D spatial position of the sound in the audio context.\n * The position is an array of three values[x, y, z].\n * This method updates the position of all active playbacks of the sound.\n */\n\n set position(position: Position) {\n if (this._threeDOptions.panType === \"HRTF\") {\n this._threeDOptions.positionX = position[0];\n this._threeDOptions.positionY = position[1];\n this._threeDOptions.positionZ = position[2];\n }\n this.playbacks.forEach((p) => (p.position = position));\n }\n\n /**\n * Returns a fresh copy of the canonical 3D-audio configuration. The clone\n * prevents callers from mutating the container's internal state via the\n * returned reference (previously the raw `_threeDOptions` object leaked).\n */\n get threeDOptions(): ThreeDOptions {\n return { ...this._threeDOptions };\n }\n\n /**\n * Accepts either a full canonical {@link ThreeDOptions} value, or a partial\n * HRTF-options override (legacy ergonomics). When a partial is provided the\n * existing variant is merged in place — and if the existing variant is\n * stereo, the partial is interpreted as a switch back to HRTF defaults plus\n * the override (no stale stereo fields). When a full variant is provided\n * with a different `panType`, the storage is replaced wholesale so stale\n * fields from the previous variant cannot be observed via the getter.\n */\n set threeDOptions(options: ThreeDOptions | Partial<HrtfPannerOptions>) {\n if (\"panType\" in options) {\n // Full canonical variant — replace storage so the discriminant is honored.\n this._threeDOptions = { ...options };\n } else if (this._threeDOptions.panType === \"HRTF\") {\n this._threeDOptions = { ...this._threeDOptions, ...options };\n } else {\n // Was stereo, now receiving HRTF partial — fall back to HRTF defaults + override.\n this._threeDOptions = { ...defaultHrtfThreeDOptions, ...options };\n }\n this.playbacks.forEach((p) => (p.threeDOptions = this._threeDOptions));\n }\n\n get stereoPan(): number {\n return this._stereoPan;\n }\n\n set stereoPan(value: number) {\n this._stereoPan = value;\n this.playbacks.forEach((p) => (p.stereoPan = value));\n }\n\n /***\n * Gets the volume of the sound. This volume level affects all current and future playbacks of this sound instance.\n * The volume is specified as a linear value between 0 (silent) and 1 (full volume).\n */\n\n get volume(): number {\n return this._volume;\n }\n\n /***\n * Sets the volume of the sound. This volume level affects all current and future playbacks of this sound instance.\n * The volume is specified as a linear value between 0 (silent) and 1 (full volume).\n */\n\n set volume(volume: number) {\n this._volume = volume;\n this.playbacks.forEach((p) => (p.volume = volume));\n }\n\n /**\n * Fades the volume of all playbacks to a target value over a duration.\n * @param {number} value - The target volume (0 to 1).\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeTo(value: number, duration: number, type?: FadeType): Promise<void> {\n this._volume = value;\n return Promise.all(this.playbacks.map((p) => p.fadeTo(value, duration, type))).then(() => {});\n }\n\n /**\n * Fades in all playbacks from silence to their current volume.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeIn(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.playbacks.map((p) => p.fadeIn(duration, type))).then(() => {});\n }\n\n /**\n * Fades out all playbacks from their current volume to silence.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when all fades complete.\n */\n fadeOut(duration: number, type?: FadeType): Promise<void> {\n return Promise.all(this.playbacks.map((p) => p.fadeOut(duration, type))).then(() => {});\n }\n\n /**\n * Fades out all playbacks then stops them.\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes and all playbacks are stopped.\n */\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n return this.fadeOut(duration, type).then(() => this.stop());\n }\n }\n return PlaybackContainer;\n}\n","import type { BasePlayback } from \"./basePlayback\";\nimport type { Bus, BusRoutedSource } from \"./bus\";\nimport type { Cacophony } from \"./cacophony\";\nimport { PlaybackContainer } from \"./container\";\nimport type { BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport type { CacophonyEffect } from \"./effects\";\nimport { BiquadRecipeEffect } from \"./effects\";\nimport { FilterManager } from \"./filters\";\n\n/**\n * Shared routing state machine for playback-producing sources.\n *\n * The concrete source supplies its audio context, master output, owning\n * Cacophony instance, and playback list. This mixin owns primary-route state,\n * additive sends, bus registration, live rewiring, drain handling, and route\n * cleanup for every source type.\n */\nexport abstract class RoutableSource extends PlaybackContainer(FilterManager) implements BusRoutedSource {\n protected abstract context: BaseContext;\n protected abstract globalGainNode: GainNode;\n abstract get cacophony(): Cacophony | undefined;\n\n /** Primary route target. `null` is the canonical master route. */\n protected _routeTarget: Bus | null = null;\n /** Additional send target to gain-value mappings. */\n protected _sends: Map<Bus, number> = new Map();\n /** Effect recipes materialized independently for each future playback. */\n private readonly _effects: CacophonyEffect[] = [];\n private readonly _filterEffects = new Map<BiquadFilterNode, CacophonyEffect>();\n private readonly _filterEffectRecipes = new Set<CacophonyEffect>();\n\n addEffect(effect: CacophonyEffect): void {\n if (this._effects.includes(effect)) {\n throw new Error(\"Cannot add the same effect recipe twice\");\n }\n this._effects.push(effect);\n }\n\n removeEffect(effect: CacophonyEffect): void {\n const index = this._effects.indexOf(effect);\n if (index === -1) {\n throw new Error(\"Cannot remove an effect recipe that was never added\");\n }\n this._effects.splice(index, 1);\n }\n\n override addFilter(filter: BiquadFilterNode): void {\n super.addFilter(filter);\n const effect = new BiquadRecipeEffect(filter);\n this._filterEffects.set(filter, effect);\n this._filterEffectRecipes.add(effect);\n this._effects.push(effect);\n }\n\n override removeFilter(filter: BiquadFilterNode): void {\n super.removeFilter(filter);\n const effect = this._filterEffects.get(filter);\n if (effect) {\n this._effects.splice(this._effects.indexOf(effect), 1);\n this._filterEffects.delete(filter);\n this._filterEffectRecipes.delete(effect);\n }\n }\n\n /**\n * Route this source to a bus, or add an independent send when `sendGain`\n * is provided.\n */\n routeTo(target: Bus | string, sendGain?: number): void {\n const bus = this._resolveBusArg(target);\n if (sendGain !== undefined) {\n this._addSend(bus, sendGain);\n return;\n }\n this._setPrimary(bus);\n }\n\n private _resolveBusArg(target: Bus | string): Bus {\n if (typeof target !== \"string\") {\n return target;\n }\n const bus = this.cacophony?.getBus(target);\n if (!bus) {\n throw new Error(`No bus registered with name '${target}'`);\n }\n return bus;\n }\n\n /**\n * Resolve the node used by future playbacks for their primary output.\n * Sources left on a subsequently destroyed bus fall back to master.\n */\n _resolveRouteTargetNode(): GainNode {\n if (!this._routeTarget) {\n return this.globalGainNode;\n }\n if (this._routeTarget.destroyed) {\n console.warn(\n `${this.constructor.name} routed to destroyed bus '${this._routeTarget.name ?? \"<anonymous>\"}'; falling back to master`,\n );\n return this.globalGainNode;\n }\n return this._routeTarget.input;\n }\n\n /**\n * Establish every configured send on a newly created playback.\n */\n protected _wireRouteSends(playback: BasePlayback): void {\n const outputNode = (playback as BasePlayback & { readonly outputNode: GainNode }).outputNode;\n for (const [bus, gainValue] of this._sends) {\n if (bus.destroyed) {\n console.warn(`${this.constructor.name} has a send to destroyed bus '${bus.name ?? \"<anonymous>\"}'; skipping`);\n continue;\n }\n const sendGain = this.context.createGain();\n sendGain.gain.value = gainValue;\n outputNode.connect(sendGain);\n sendGain.connect(bus.input);\n playback._sendGains.set(bus, sendGain);\n }\n }\n\n /** Materialize all declared recipes, then establish this playback's sends. */\n protected _preparePlayback(playback: BasePlayback): void {\n for (const effect of this._effects) {\n const build = this._filterEffectRecipes.has(effect)\n ? playback._addFilterEffect(effect)\n : playback._addSourceEffect(effect);\n void build.catch((error) => {\n console.warn(`${this.constructor.name} could not build a per-playback effect; continuing without it.`, error);\n });\n }\n this._wireRouteSends(playback);\n }\n\n private _setPrimary(bus: Bus): void {\n if (bus.destroyed) {\n throw new Error(`Cannot route to destroyed bus '${bus.name ?? \"<anonymous>\"}'`);\n }\n const collapseToMaster = bus.input === this.globalGainNode;\n const oldTarget = this._routeTarget;\n const oldTargetNode = this._resolveRouteTargetNode();\n this._routeTarget = collapseToMaster ? null : bus;\n const newTarget = this._routeTarget;\n const newTargetNode = this._resolveRouteTargetNode();\n\n // Bus registration follows bus identity, not resolved node identity. A\n // destroyed bus and master can resolve to the same node while still\n // requiring the old bus registration to be removed.\n if (oldTarget !== newTarget) {\n if (oldTarget && !this._sends.has(oldTarget)) {\n oldTarget._unregisterRoutedSource(this);\n }\n if (newTarget) {\n newTarget._registerRoutedSource(this);\n }\n }\n if (oldTargetNode === newTargetNode) {\n return;\n }\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n try {\n playback.outputNode.disconnect(oldTargetNode);\n } catch {\n // A playback may already have been disconnected during cleanup.\n }\n try {\n playback.outputNode.connect(newTargetNode);\n } catch {\n // Live rewiring is best-effort for externally disconnected nodes.\n }\n }\n }\n\n private _addSend(bus: Bus, gainValue: number): void {\n if (bus.destroyed) {\n throw new Error(`Cannot add a send to destroyed bus '${bus.name ?? \"<anonymous>\"}'`);\n }\n this._sends.set(bus, gainValue);\n bus._registerRoutedSource(this);\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n const existing = playback._sendGains.get(bus);\n if (existing) {\n existing.gain.value = gainValue;\n continue;\n }\n const sendGain = this.context.createGain();\n sendGain.gain.value = gainValue;\n try {\n playback.outputNode.connect(sendGain);\n } catch {\n try {\n sendGain.disconnect();\n } catch {\n // The failed connection may leave the fresh node already detached.\n }\n continue;\n }\n sendGain.connect(bus.input);\n playback._sendGains.set(bus, sendGain);\n }\n }\n\n _onBusDrained(bus: Bus, target: Bus): void {\n if (this._routeTarget === bus) {\n this._setPrimary(target);\n }\n if (!this._sends.has(bus)) {\n return;\n }\n const gainValue = this._sends.get(bus) as number;\n this._sends.delete(bus);\n for (const playback of this.playbacks as Array<BasePlayback & { readonly outputNode: GainNode }>) {\n const sendGain = playback._sendGains.get(bus);\n if (!sendGain) {\n continue;\n }\n try {\n sendGain.disconnect();\n } catch {\n // The node may already have been disconnected externally.\n }\n playback._sendGains.delete(bus);\n }\n this._addSend(target, gainValue);\n }\n\n cleanup(): void {\n if (this._routeTarget) {\n this._routeTarget._unregisterRoutedSource(this);\n }\n for (const bus of this._sends.keys()) {\n bus._unregisterRoutedSource(this);\n }\n this._effects.length = 0;\n this._filterEffects.clear();\n this._filterEffectRecipes.clear();\n super.cleanup();\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, Cacophony, LoopCount, PanType } from \"./cacophony\";\nimport type {\n AudioNode,\n AudioParam,\n BaseContext,\n BiquadFilterNode,\n GainNode,\n MediaStreamAudioSourceNode,\n} from \"./context\";\nimport { RoutableSource } from \"./routableSource\";\n\nexport interface MediaStreamSoundOptions {\n panType?: PanType;\n stopTracksOnStop?: boolean;\n /**\n * Attach the stream to a muted `HTMLAudioElement` while it plays.\n *\n * Chromium will not render a remote WebRTC `MediaStreamTrack` that is only\n * connected to the Web Audio graph: packets arrive but the decoder produces\n * zero samples, so a `MediaStreamAudioSourceNode` taps pure silence. Pulling\n * the same stream through a media element \"primes\" the decode pipeline; the\n * Web Audio path then receives real audio. The element is muted so it does\n * not double-play over the (possibly spatialised) Web Audio output. Firefox\n * does not need this, but the priming element is harmless there.\n *\n * Defaults to `true`. Set to `false` for streams that are never routed to a\n * Web Audio graph, or in environments without `HTMLAudioElement`.\n */\n primeWithMediaElement?: boolean;\n}\n\nexport class MediaStreamPlayback extends BasePlayback {\n public declare origin: MediaStreamSound;\n public declare source?: MediaStreamAudioSourceNode;\n private stopTracksOnStop: boolean;\n private tracksStopped = false;\n private pausedTrackStates?: Map<MediaStreamTrack, boolean>;\n /**\n * Muted media element that keeps Chromium's decode pipeline alive for the\n * stream so the Web Audio tap receives real audio. See\n * {@link MediaStreamSoundOptions.primeWithMediaElement}.\n */\n private primeElement?: HTMLAudioElement;\n\n constructor(\n origin: MediaStreamSound,\n source: MediaStreamAudioSourceNode,\n gainNode: GainNode,\n context: BaseContext,\n outputNode: AudioNode,\n panType: PanType,\n stopTracksOnStop: boolean,\n primeWithMediaElement: boolean = true,\n ) {\n super(origin);\n this.stopTracksOnStop = stopTracksOnStop;\n this.source = source;\n this.setPanType(panType, context);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n this.gainNode!.connect(outputNode);\n if (primeWithMediaElement) {\n this.createPrimeElement();\n }\n }\n\n private createPrimeElement(): void {\n if (typeof Audio === \"undefined\" || !this.source) {\n return;\n }\n try {\n const element = new Audio();\n element.muted = true;\n element.srcObject = this.source.mediaStream;\n this.primeElement = element;\n } catch {\n // Environment without media-element support: priming is best-effort.\n this.primeElement = undefined;\n }\n }\n\n private teardownPrimeElement(): void {\n const element = this.primeElement;\n if (!element) {\n return;\n }\n this.primeElement = undefined;\n try {\n element.pause();\n element.srcObject = null;\n } catch {\n // Element may already be torn down by the browser.\n }\n }\n\n get duration(): number {\n return 0;\n }\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a media stream that has been cleaned up\");\n }\n if (this.isPlaying) {\n return [this];\n }\n const tracks = this.source.mediaStream.getTracks();\n if (tracks.length > 0 && tracks.every((track) => track.readyState === \"ended\")) {\n throw new Error(\"Cannot play a media stream whose tracks have ended\");\n }\n const isResume = this.isPaused;\n if (isResume && this.pausedTrackStates) {\n tracks.forEach((track) => {\n const enabled = this.pausedTrackStates?.get(track);\n if (enabled !== undefined) {\n track.enabled = enabled;\n }\n });\n }\n this.pausedTrackStates = undefined;\n // Muted autoplay is always permitted, so this needs no user gesture; the\n // returned promise is ignored because failure only loses Chromium priming.\n this.primeElement?.play().catch(() => {});\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || !this.isPlaying) {\n return;\n }\n const tracks = this.source.mediaStream.getTracks();\n this.pausedTrackStates = new Map(tracks.map((track) => [track, track.enabled]));\n tracks.forEach((track) => (track.enabled = false));\n this.primeElement?.pause();\n this.emitPaused();\n }\n\n resume(): void {\n this.play();\n }\n\n stop(): void {\n if (!this.source) {\n throw new Error(\"Cannot stop a media stream that has been cleaned up\");\n }\n const shouldEmitStop = this._state === \"playing\" || this._state === \"paused\";\n this.stopOwnedTracks();\n this.pausedTrackStates = undefined;\n this.teardownPrimeElement();\n if (shouldEmitStop) {\n this.emitStopped();\n } else {\n this.markStopped();\n }\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot add a filter to a media stream that has been cleaned up\");\n }\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot remove a filter from a media stream that has been cleaned up\");\n }\n super.removeFilter(filter);\n }\n\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a media stream that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n private stopOwnedTracks(): void {\n if (!this.stopTracksOnStop || this.tracksStopped || !this.source) {\n return;\n }\n this.source.mediaStream.getTracks().forEach((track) => track.stop());\n this.tracksStopped = true;\n }\n\n cleanup(): void {\n if (!this.source) {\n return;\n }\n this.stopOwnedTracks();\n this.pausedTrackStates = undefined;\n this.markStopped();\n this.teardownPrimeElement();\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n}\n\nexport class MediaStreamSound extends RoutableSource implements BaseSound {\n public declare playbacks: MediaStreamPlayback[];\n protected context: BaseContext;\n protected globalGainNode: GainNode;\n private stream: MediaStream;\n private stopTracksOnStop: boolean;\n private panType: PanType;\n private primeWithMediaElement: boolean;\n\n constructor(\n stream: MediaStream,\n context: BaseContext,\n globalGainNode: GainNode,\n options: MediaStreamSoundOptions = {},\n private _cacophony?: Cacophony,\n ) {\n super();\n this.stream = stream;\n this.context = context;\n this.globalGainNode = globalGainNode;\n this.panType = options.panType ?? \"HRTF\";\n this.stopTracksOnStop = options.stopTracksOnStop ?? true;\n this.primeWithMediaElement = options.primeWithMediaElement ?? true;\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n private createStreamSource(stream: MediaStream): MediaStreamAudioSourceNode {\n if (!this.context.createMediaStreamSource) {\n throw new Error(\"Media stream sources are not supported on this audio context (e.g. OfflineAudioContext).\");\n }\n return this.context.createMediaStreamSource(stream);\n }\n\n preplay(): MediaStreamPlayback[] {\n if (this.playbacks[0]) {\n return [this.playbacks[0]];\n }\n\n const source = this.createStreamSource(this.stream);\n const gainNode = this.context.createGain();\n const playback = new MediaStreamPlayback(\n this,\n source,\n gainNode,\n this.context,\n this._resolveRouteTargetNode(),\n this.panType,\n this.stopTracksOnStop,\n this.primeWithMediaElement,\n );\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else {\n playback.stereoPan = this.stereoPan ?? 0;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n seek(_time: number): void {}\n\n resume(): void {\n this.playbacks.forEach((playback) => playback.resume());\n }\n\n loop(_loopCount?: LoopCount): LoopCount {\n return 0;\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n cleanup(): void {\n this.stop();\n super.cleanup();\n }\n}\n","/**\n * {@link LoudnessMeter} — a main-thread handle over the `loudness-meter`\n * AudioWorkletProcessor (ITU-R BS.1770-5 momentary / short-term / integrated\n * loudness + true-peak).\n *\n * The meter taps a target node's OUTPUT as a BRANCH: it connects the target to\n * the metering worklet's input without disturbing the target's existing forward\n * edge, so inserting a meter never alters the audible path. The worklet itself\n * is pass-through; its output is routed through an owned zero-gain sink so the\n * branch stays silent while remaining on a live render graph.\n *\n * Readings arrive ~10×/s over the MessagePort and are cached on this handle\n * ({@link momentary} / {@link shortTerm} / {@link integrated} / {@link truePeak});\n * an optional {@link onUpdate} callback fires on each report.\n *\n * @see https://www.itu.int/rec/R-REC-BS.1770-5-202311-I/en\n */\nimport type { AudioNode, AudioWorkletNode, GainNode } from \"../context\";\n\n/** A single set of loudness readings posted by the worklet. */\nexport interface LoudnessReading {\n /** Momentary loudness over the last 400 ms, in LKFS (LUFS). */\n momentary: number;\n /** Short-term loudness over the last 3 s, in LKFS (LUFS). */\n shortTerm: number;\n /** Gated integrated loudness since the last reset, in LKFS (LUFS). */\n integrated: number;\n /** Running true-peak level since the last reset, in dBTP. */\n truePeak: number;\n}\n\ninterface LoudnessMessage extends LoudnessReading {\n type: \"loudness\";\n}\n\nexport class LoudnessMeter {\n private readonly node: AudioWorkletNode;\n private readonly source: AudioNode;\n private readonly silentSink?: GainNode;\n private readonly sinkDestination?: AudioNode;\n private latest: LoudnessReading = {\n momentary: -Infinity,\n shortTerm: -Infinity,\n integrated: -Infinity,\n truePeak: -Infinity,\n };\n\n /**\n * Invoked on every report from the worklet (~10×/s) with the latest readings.\n * Assign a function to observe live loudness; leave undefined to poll the\n * cached getters instead.\n */\n onUpdate?: (reading: LoudnessReading) => void;\n\n /**\n * @param node The constructed `loudness-meter` AudioWorkletNode.\n * @param source The node whose output is being metered. Its output is\n * branched into `node` here; the source's existing connections are\n * untouched (the audible path is preserved).\n * @param silentSink Optional zero-gain node owned by the meter. When supplied,\n * the worklet output is connected through it to `sinkDestination`, keeping\n * the metering worklet on a pulled graph without adding audible output.\n * @param sinkDestination Destination for the owned zero-gain sink.\n */\n constructor(node: AudioWorkletNode, source: AudioNode, silentSink?: GainNode, sinkDestination?: AudioNode) {\n this.node = node;\n this.source = source;\n this.silentSink = silentSink;\n this.sinkDestination = sinkDestination;\n this.node.port.addEventListener(\"message\", this.handleMessage);\n // Some MessagePort implementations require an explicit start when using\n // addEventListener rather than onmessage.\n this.node.port.start?.();\n // Branch tap: source.output → meter (in ADDITION to source's existing edges).\n this.source.connect(this.node);\n if (this.silentSink && this.sinkDestination) {\n this.silentSink.gain.value = 0;\n this.node.connect(this.silentSink);\n this.silentSink.connect(this.sinkDestination);\n }\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n const data = event.data as LoudnessMessage | undefined;\n if (!data || data.type !== \"loudness\") {\n return;\n }\n this.latest = {\n momentary: data.momentary,\n shortTerm: data.shortTerm,\n integrated: data.integrated,\n truePeak: data.truePeak,\n };\n this.onUpdate?.(this.latest);\n };\n\n /** Latest momentary loudness (400 ms) in LKFS. */\n get momentary(): number {\n return this.latest.momentary;\n }\n\n /** Latest short-term loudness (3 s) in LKFS. */\n get shortTerm(): number {\n return this.latest.shortTerm;\n }\n\n /** Latest gated integrated loudness (since last reset) in LKFS. */\n get integrated(): number {\n return this.latest.integrated;\n }\n\n /** Latest true-peak level (since last reset) in dBTP. */\n get truePeak(): number {\n return this.latest.truePeak;\n }\n\n /** A snapshot of all current readings. */\n get reading(): LoudnessReading {\n return { ...this.latest };\n }\n\n /** The underlying metering worklet node (for advanced wiring). */\n get workletNode(): AudioWorkletNode {\n return this.node;\n }\n\n /**\n * Resets the integrated-loudness gate accumulator and the true-peak hold on\n * the worklet (momentary/short-term keep sliding). Use to start a fresh\n * integrated measurement of a new programme.\n */\n reset(): void {\n this.node.port.postMessage({ command: \"reset\" });\n }\n\n /**\n * Stops metering: removes the branch tap from the source and detaches the\n * message listener. The audible path (the source's other edges) is untouched.\n */\n disconnect(): void {\n this.node.port.removeEventListener?.(\"message\", this.handleMessage);\n try {\n this.source.disconnect(this.node);\n } catch {\n // Already disconnected — ignore.\n }\n if (this.silentSink) {\n try {\n this.node.disconnect(this.silentSink);\n } catch {\n // Already disconnected — ignore.\n }\n }\n if (this.silentSink && this.sinkDestination) {\n try {\n this.silentSink.disconnect(this.sinkDestination);\n } catch {\n // Already disconnected — ignore.\n }\n }\n }\n}\n","import type { Cacophony, PanType } from \"./cacophony\";\nimport type { AudioNode, BaseContext, GainNode } from \"./context\";\nimport { MediaStreamPlayback, MediaStreamSound, type MediaStreamSoundOptions } from \"./mediaStream\";\nimport type { HrtfPannerOptions } from \"./pannerMixin\";\n\n/** Options for acquiring and monitoring a microphone stream. */\nexport interface MicrophoneStreamOptions extends MediaStreamSoundOptions {\n /** Constraints forwarded to `navigator.mediaDevices.getUserMedia`. */\n constraints?: MediaStreamConstraints;\n /**\n * Stop the microphone tracks when playback stops or is cleaned up.\n * Defaults to `true`. Set to `false` to keep the stream reusable.\n */\n stopTracksOnStop?: boolean;\n /** Initial left/right pan when `panType` is `\"stereo\"`. */\n stereoPan?: number;\n /** Initial spatial options when `panType` is `\"HRTF\"` (the default). */\n threeDOptions?: Partial<HrtfPannerOptions>;\n}\n\n/**\n * A microphone playback uses the same BasePlayback lifecycle, event, panning,\n * effect, routing, and cleanup implementation as every other MediaStream.\n */\nexport { MediaStreamPlayback as MicrophonePlayback };\n\n/**\n * A live microphone source backed by the current MediaStreamSound architecture.\n * Use {@link MicrophoneStream.request} (or `Cacophony.getMicrophoneStream`) when\n * the stream still needs to be acquired so permission failures remain observable.\n */\nexport class MicrophoneStream extends MediaStreamSound {\n static async request(\n context: BaseContext,\n outputNode?: AudioNode,\n options: MicrophoneStreamOptions = {},\n cacophony?: Cacophony,\n ): Promise<MicrophoneStream> {\n const stream = await navigator.mediaDevices.getUserMedia(options.constraints ?? { audio: true });\n return new MicrophoneStream(context, stream, outputNode, options, cacophony);\n }\n\n constructor(\n context: BaseContext,\n stream: MediaStream,\n outputNode?: AudioNode,\n options: MicrophoneStreamOptions = {},\n cacophony?: Cacophony,\n ) {\n const { constraints: _constraints, stereoPan, threeDOptions, ...mediaStreamOptions } = options;\n const panType: PanType = mediaStreamOptions.panType ?? \"HRTF\";\n super(\n stream,\n context,\n (outputNode ?? context.destination) as GainNode,\n {\n ...mediaStreamOptions,\n panType,\n primeWithMediaElement: mediaStreamOptions.primeWithMediaElement ?? false,\n stopTracksOnStop: mediaStreamOptions.stopTracksOnStop ?? true,\n },\n cacophony,\n );\n\n if (panType === \"stereo\") {\n this.stereoPan = stereoPan ?? 0;\n } else if (threeDOptions) {\n this.threeDOptions = threeDOptions;\n }\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, Cacophony, LoopCount, PanType, SoundType, StreamCapabilities } from \"./cacophony\";\nimport type { AudioNode, AudioParam, AudioWorkletNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport { RoutableSource } from \"./routableSource\";\n\nexport type PcmStreamState = \"idle\" | \"playing\" | \"paused\" | \"stopped\" | \"ended\";\n\nexport interface PcmStreamBufferEvent {\n bufferedDuration: number;\n}\n\nexport type PcmStreamEvents = {\n stateChange: PcmStreamState;\n underrun: PcmStreamBufferEvent;\n drain: PcmStreamBufferEvent;\n ended: undefined;\n error: unknown;\n};\n\nexport interface PcmStreamPullSource {\n seek(time: number): void;\n cleanup(): void;\n}\n\nexport interface PcmStreamSoundOptions {\n /** Number of interleaved channels in every {@link PcmStreamSound.write} call. @default 1 */\n channelCount?: number;\n /** Fixed ring-buffer capacity in seconds. @default 1 */\n bufferDuration?: number;\n /** PCM that must be buffered before initial consumption starts, in seconds. @default 0.05 */\n latency?: number;\n /** Spatial panner implementation used by the playback. @default \"HRTF\" */\n panType?: PanType;\n /** Aborting tears down the worklet and routing graph. */\n signal?: AbortSignal;\n}\n\ntype PcmWorkletMessage =\n | { type: \"consumed\"; frames: number }\n | { type: \"underrun\" }\n | { type: \"ended\" }\n | { type: \"overflow\" };\n\nexport class PcmStreamPlayback extends BasePlayback {\n public declare origin: PcmStreamSound;\n public declare source?: AudioWorkletNode;\n\n constructor(\n origin: PcmStreamSound,\n source: AudioWorkletNode,\n gainNode: GainNode,\n context: BaseContext,\n outputNode: AudioNode,\n panType: PanType,\n ) {\n super(origin);\n this.source = source;\n this.setPanType(panType, context);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n this.gainNode!.connect(outputNode);\n }\n\n get duration(): number {\n return Number.POSITIVE_INFINITY;\n }\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a PCM stream that has been cleaned up\");\n }\n if (this.isPlaying) {\n return [this];\n }\n const isResume = this.isPaused;\n this.source.port.postMessage({ type: \"play\" });\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || !this.isPlaying) {\n return;\n }\n this.source.port.postMessage({ type: \"pause\" });\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source) {\n return;\n }\n const shouldEmitStop = this.isPlaying || this.isPaused;\n this.source.port.postMessage({ type: \"stop\" });\n if (shouldEmitStop) {\n this.emitStopped();\n } else {\n this.markStopped();\n }\n }\n\n _handleEnded(): void {\n if (!this.source) {\n return;\n }\n this.markStopped();\n this.emit(\"ended\", undefined);\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot add a filter to a PCM stream that has been cleaned up\");\n }\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this.source) {\n throw new Error(\"Cannot remove a filter from a PCM stream that has been cleaned up\");\n }\n super.removeFilter(filter);\n }\n\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a PCM stream that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as AudioNode);\n }\n\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as AudioNode);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n cleanup(): void {\n if (!this.source) {\n return;\n }\n this.markStopped();\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n}\n\nexport class PcmStreamSound extends RoutableSource implements BaseSound {\n public declare playbacks: PcmStreamPlayback[];\n public soundType?: SoundType;\n public streamCapabilities?: StreamCapabilities;\n protected context: BaseContext;\n protected globalGainNode: GainNode;\n private readonly workletNode: AudioWorkletNode;\n private readonly channelCount: number;\n private readonly capacityFrames: number;\n private readonly panType: PanType;\n private readonly eventEmitter = new TypedEventEmitter<PcmStreamEvents>();\n private readonly signal?: AbortSignal;\n private bufferedFrames = 0;\n private backpressured = false;\n private inputEnded = false;\n private disposed = false;\n private state: PcmStreamState = \"idle\";\n private pullSource?: PcmStreamPullSource;\n\n private readonly handleWorkletMessage = (event: MessageEvent<PcmWorkletMessage>): void => {\n switch (event.data.type) {\n case \"consumed\":\n this.bufferedFrames = Math.max(0, this.bufferedFrames - event.data.frames);\n if (this.backpressured && this.bufferedFrames < this.capacityFrames) {\n this.backpressured = false;\n this.emit(\"drain\", { bufferedDuration: this.bufferedDuration });\n }\n break;\n case \"underrun\":\n this.emit(\"underrun\", { bufferedDuration: this.bufferedDuration });\n break;\n case \"ended\":\n this.bufferedFrames = 0;\n this.playbacks.forEach((playback) => playback._handleEnded());\n this.setState(\"ended\");\n this.emit(\"ended\", undefined);\n break;\n case \"overflow\":\n this.backpressured = true;\n break;\n }\n };\n\n private readonly handleAbort = (): void => {\n this.cleanup();\n };\n\n constructor(\n workletNode: AudioWorkletNode,\n context: BaseContext,\n globalGainNode: GainNode,\n options: PcmStreamSoundOptions & {\n channelCount: number;\n bufferDuration: number;\n latency: number;\n },\n private readonly _cacophony?: Cacophony,\n ) {\n super();\n this.workletNode = workletNode;\n this.context = context;\n this.globalGainNode = globalGainNode;\n this.channelCount = options.channelCount;\n this.capacityFrames = Math.ceil(context.sampleRate * options.bufferDuration);\n this.panType = options.panType ?? \"HRTF\";\n this.signal = options.signal;\n this.workletNode.port.addEventListener(\"message\", this.handleWorkletMessage as EventListener);\n this.workletNode.port.start?.();\n this.signal?.addEventListener(\"abort\", this.handleAbort, { once: true });\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n get bufferedDuration(): number {\n return this.bufferedFrames / this.context.sampleRate;\n }\n\n get inputChannelCount(): number {\n return this.channelCount;\n }\n\n on<K extends keyof PcmStreamEvents>(event: K, listener: (data: PcmStreamEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n off<K extends keyof PcmStreamEvents>(event: K, listener: (data: PcmStreamEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n private emit<K extends keyof PcmStreamEvents>(event: K, data: PcmStreamEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n private setState(state: PcmStreamState): void {\n if (this.state === state) {\n return;\n }\n this.state = state;\n this.emit(\"stateChange\", state);\n }\n\n attachPullSource(source: PcmStreamPullSource, capabilities: StreamCapabilities): void {\n if (this.disposed || this.pullSource) {\n throw new Error(\"Cannot attach a pull source to this PCM stream\");\n }\n this.pullSource = source;\n this.soundType = \"streaming\";\n this.streamCapabilities = capabilities;\n }\n\n handlePullError(error: unknown): void {\n if (!this.disposed) {\n this.emit(\"error\", error);\n }\n }\n\n /**\n * Enqueue PCM at the context's sample rate.\n *\n * Samples are interleaved according to `channelCount`. `false` means the\n * fixed ring buffer is full (or this accepted write filled it); wait for\n * `drain` before retrying a rejected chunk.\n */\n write(samples: Float32Array): boolean {\n if (this.disposed) {\n throw new Error(\"Cannot write to a PCM stream that has been cleaned up\");\n }\n if (this.inputEnded) {\n throw new Error(\"Cannot write PCM after end()\");\n }\n if (!(samples instanceof Float32Array)) {\n throw new TypeError(\"PCM writes require an interleaved Float32Array\");\n }\n if (samples.length % this.channelCount !== 0) {\n throw new RangeError(`Interleaved PCM length must be divisible by channelCount (${this.channelCount})`);\n }\n const frameCount = samples.length / this.channelCount;\n if (frameCount > this.capacityFrames) {\n throw new RangeError(\"PCM chunk is larger than the configured ring buffer\");\n }\n if (this.bufferedFrames + frameCount > this.capacityFrames) {\n this.backpressured = true;\n return false;\n }\n\n this.workletNode.port.postMessage({\n type: \"write\",\n samples: samples.slice(),\n });\n this.bufferedFrames += frameCount;\n if (this.bufferedFrames === this.capacityFrames) {\n this.backpressured = true;\n return false;\n }\n return true;\n }\n\n end(): void {\n if (this.disposed || this.inputEnded) {\n return;\n }\n this.inputEnded = true;\n this.workletNode.port.postMessage({ type: \"end\" });\n }\n\n preplay(): PcmStreamPlayback[] {\n if (this.disposed) {\n throw new Error(\"Cannot play a PCM stream that has been cleaned up\");\n }\n if (this.playbacks[0]) {\n return [this.playbacks[0]];\n }\n const playback = new PcmStreamPlayback(\n this,\n this.workletNode,\n this.context.createGain(),\n this.context,\n this._resolveRouteTargetNode(),\n this.panType,\n );\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else {\n playback.stereoPan = this.stereoPan;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n play(): PcmStreamPlayback[] {\n if (this.state === \"ended\") {\n throw new Error(\"Cannot play a PCM stream after it has ended\");\n }\n const playbacks = this.preplay();\n playbacks.forEach((playback) => playback.play());\n this.setState(\"playing\");\n return playbacks;\n }\n\n pause(): void {\n if (this.state !== \"playing\") {\n return;\n }\n this.playbacks.forEach((playback) => playback.pause());\n this.setState(\"paused\");\n }\n\n resume(): void {\n if (this.state !== \"paused\") {\n return;\n }\n this.playbacks.forEach((playback) => playback.play());\n this.setState(\"playing\");\n }\n\n stop(): void {\n if (this.disposed) {\n return;\n }\n const hadPlayback = this.playbacks.length > 0;\n super.stop();\n if (!hadPlayback) {\n this.workletNode.port.postMessage({ type: \"stop\" });\n }\n this.bufferedFrames = 0;\n this.backpressured = false;\n this.inputEnded = false;\n this.setState(\"stopped\");\n }\n\n seek(time: number): void {\n if (!this.pullSource) {\n throw new Error(\"PCM streams do not support seeking\");\n }\n this.pullSource.seek(time);\n }\n\n loop(_loopCount?: LoopCount): LoopCount {\n throw new Error(\"PCM streams do not support looping\");\n }\n\n get playbackRate(): number {\n return 1;\n }\n\n set playbackRate(_rate: number) {}\n\n cleanup(): void {\n if (this.disposed) {\n return;\n }\n this.stop();\n this.disposed = true;\n this.pullSource?.cleanup();\n this.pullSource = undefined;\n this.signal?.removeEventListener(\"abort\", this.handleAbort);\n this.workletNode.port.removeEventListener(\"message\", this.handleWorkletMessage as EventListener);\n this.workletNode.disconnect();\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","/*\n * Dynamics processor core — context-free DSP math for a feed-forward dynamic\n * range compressor / limiter / expander / gate.\n *\n * Implements the canonical design from:\n * D. Giannoulis, M. Massberg, J. D. Reiss,\n * \"Digital Dynamic Range Compressor Design — A Tutorial and Analysis\",\n * J. Audio Eng. Soc., Vol. 60, No. 6, pp. 399-408 (2012).\n *\n * Topology (the paper's recommended best practice, p.399 abstract / pp.404-405):\n * - feed-forward (side-chain driven from the INPUT),\n * - gain computer = static dB->dB curve (eqs 2-4),\n * - level detector placed in the LOG domain AFTER the gain computer\n * (eq 23): smooth the gain-reduction control signal, not the raw level,\n * - smooth branching peak detector for the ballistics (eq 16).\n *\n * A SINGLE gain computer, parameterized by threshold T, ratio R and knee W,\n * serves all four behaviours (Giannoulis 2012, p.403; see notes.md\n * \"Expander / Gate from the SAME machinery\"):\n * - R > 1 -> compressor (slope 1/R < 1 above T),\n * - R -> infinity -> limiter (slope -> 0 above T, eqs 18-19),\n * - R < 1 -> downward expander (slope > 1 BELOW T),\n * - extreme R < 1 -> gate (very steep downward expansion below T).\n *\n * This file holds ONLY pure numeric math (operates on plain numbers and\n * Float32Array). It has no AudioWorklet / global dependencies so it can be\n * unit-tested directly (the worklet shell in dynamics.ts delegates to it).\n */\n\n/** Parameters consumed per processing block. All dB values are dBFS. */\nexport interface DynamicsParams {\n /** Threshold T (dB) — level above which compression starts. */\n threshold: number;\n /** Ratio R — reciprocal of slope above T. >1 compress, ->inf limit, <1 expand. */\n ratio: number;\n /** Knee width W (dB) — soft-knee transition centered on T; 0 = hard knee. */\n knee: number;\n /** Attack time tau_A (s) — gain-decrease time constant. */\n attack: number;\n /** Release time tau_R (s) — gain-recovery time constant. */\n release: number;\n /** Make-up gain M (dB) — constant output boost. */\n makeup: number;\n}\n\n/**\n * Default AudioParam values for the dynamics worklet, in the worklet's own\n * units. SINGLE SOURCE OF TRUTH — the worklet's `parameterDescriptors`\n * (dynamics.ts) builds its descriptors from this table, and tests that need the\n * shipped defaults import it here rather than re-typing literals. Living in the\n * worklet-global-free core keeps it importable from plain Node test runners.\n *\n * Defaults: a gentle, audible-but-safe compressor. Ratios cover compressor\n * (ratio >= 1), limiter (ratio -> large) and downward expander/gate (ratio < 1)\n * from one parameter set.\n */\nexport const DYNAMICS_DEFAULTS: Readonly<DynamicsParams> = {\n threshold: -24,\n ratio: 4,\n knee: 6,\n attack: 0.003,\n release: 0.25,\n makeup: 0,\n};\n\n/**\n * Ratio override the `createGate` factory (cacophony.ts) applies to turn the\n * shared dynamics worklet into a gate-like downward expander. SINGLE SOURCE OF\n * TRUTH for that override — `createGate` and the gate regression tests both\n * import it so a drift in the gate's ratio breaks the test.\n */\nexport const GATE_DEFAULT_RATIO = 0.1;\n\n/**\n * Smallest linear level we are willing to take a logarithm of. Below this the\n * input is treated as silence (gain-reduction control = 0), avoiding -Infinity\n * dB from log10(0).\n */\nconst LEVEL_FLOOR = 1e-12;\n\n/**\n * Static gain computer — maps an input level x_G (dB) to an output level\n * y_G (dB) given threshold T, ratio R and knee width W.\n *\n * Hard knee (W = 0), Giannoulis 2012 eq.3:\n * y_G = x_G for x_G <= T\n * y_G = T + (x_G - T) / R for x_G > T\n *\n * Soft knee (W > 0), eq.4 — quadratic interpolation across +/- W/2 around T:\n * y_G = x_G 2(x_G-T) < -W\n * y_G = x_G + (1/R - 1)(x_G - T + W/2)^2 / (2W) |2(x_G-T)| <= W\n * y_G = T + (x_G - T) / R 2(x_G-T) > W\n *\n * A limiter is R -> infinity: 1/R -> 0, so above T the curve flattens to y_G = T\n * (eqs 18-19). We handle the infinite/zero-slope case explicitly to avoid\n * division by infinity.\n *\n * Downward EXPANSION (R < 1, Giannoulis 2012 p.403 \"for ratios < 1 ... the\n * variable simply becomes smaller than -1\"): the slope-altered region is BELOW\n * the threshold rather than above it. With slope 1/R > 1 applied for x_G < T,\n * y_G = T + (x_G - T)/R is more negative than x_G, so quiet signals are pushed\n * further down (increasing dynamic range; an extreme R<1 ratio is a gate).\n * See notes.md \"Expander / Gate from the SAME machinery\". The compressor branch\n * (R >= 1) and the expander branch (R < 1) are mirror images about T, so one\n * function parameterized by T/R/W serves compressor, limiter, expander and gate.\n */\nexport function computeStaticGain(xG: number, threshold: number, ratio: number, knee: number): number {\n const slope = ratio === Number.POSITIVE_INFINITY ? 0 : 1 / ratio; // 1/R; limiter -> 0\n const delta = xG - threshold;\n const isExpander = ratio < 1; // R < 1 => downward expansion below T\n\n if (isExpander) {\n // Downward expander: slope-altered region is BELOW threshold (eqs 2-4\n // mirrored). Soft knee centered on T (eq.4 form, mirrored to the lower\n // side): blend the unity slope (above) into slope 1/R (below).\n if (knee > 0 && Math.abs(2 * delta) <= knee) {\n // eq.4 quadratic interpolation, mirrored about T for the expander.\n // The expander's slope-altered region is BELOW T, so the knee must join\n // the slope line at the LOWER edge (delta = -W/2) and unity at the UPPER\n // edge (delta = +W/2). Anchor the parabola to the upper edge (t = 0 there)\n // and subtract the curvature so y_G meets the slope line below: this\n // yields y_G <= x_G everywhere (never boosts) and C0/C1 continuity at both\n // edges (slope 1/R below, unity above). The MINUS sign is the mirror of\n // the compressor branch's PLUS — same eq.4 form, opposite altered side.\n const t = delta - knee / 2; // 0 at the upper edge (delta = +W/2)\n return xG - ((slope - 1) * (t * t)) / (2 * knee);\n }\n if (delta >= 0) {\n // x_G >= T (above threshold) — unity for a downward expander.\n return xG;\n }\n // x_G < T (below threshold) — slope 1/R > 1 pushes the level down.\n return threshold + delta * slope;\n }\n\n // Compressor / limiter (R >= 1): slope-altered region is ABOVE threshold.\n // Soft knee (eq.4 middle branch): 2|x_G - T| <= W.\n if (knee > 0 && Math.abs(2 * delta) <= knee) {\n // eq.4: y_G = x_G + (1/R - 1)(x_G - T + W/2)^2 / (2W)\n const t = delta + knee / 2;\n return xG + ((slope - 1) * (t * t)) / (2 * knee);\n }\n\n // Hard knee / outside knee region (eq.3 / eq.4 outer branches).\n if (delta <= 0) {\n // x_G <= T (below threshold) — unity. (eq.3 first case.)\n return xG;\n }\n // x_G > T (above threshold) — slope 1/R. (eq.3 second case; eq.4 upper branch.)\n return threshold + delta * slope;\n}\n\n/**\n * Time-constant -> one-pole coefficient mapping, Giannoulis 2012 eq.7:\n * alpha = exp(-1 / (tau * f_s))\n * tau is the time to reach 1 - 1/e of the final value. For tau > 0, f_s > 0\n * the result lies in (0, 1); larger tau -> alpha closer to 1 (slower).\n */\nexport function timeConstantToCoefficient(tau: number, sampleRate: number): number {\n if (tau <= 0) return 0; // degenerate: instantaneous response\n return Math.exp(-1 / (tau * sampleRate));\n}\n\n/**\n * Stateful feed-forward dynamics processor. One instance owns the ballistics\n * state (the smoothed gain-reduction envelope y_L of the eq.16 detector) and is\n * reused block-to-block so the IIR memory is preserved across process() calls —\n * constructing a fresh instance per block would reset the envelope and produce\n * audible transients (same hazard documented for the b-format upmixer).\n */\nexport class DynamicsProcessor {\n /**\n * Smoothed gain-reduction control envelope y_L[n-1] (the state of the eq.16\n * smooth branching peak detector). Operates on the gain-reduction signal\n * x_L = x_G - y_G (eq.23), which is 0 (no reduction) when not compressing, so\n * the envelope rests at 0.\n */\n private envelopeDb = 0;\n\n constructor(private readonly sampleRate: number) {}\n\n /** Reset the ballistics state (e.g. on a discontinuity). */\n reset(): void {\n this.envelopeDb = 0;\n }\n\n /**\n * Process one sample, returning the gain-applied output sample.\n *\n * Signal flow (Giannoulis 2012 recommended feed-forward, log-domain detector\n * after the gain computer):\n * 1. x_G = 20 log10 |x| (eq.23 line 1)\n * 2. y_G = gain computer(x_G; T, R, W) (eqs 3-4)\n * 3. x_L = x_G - y_G (eq.23 line 2)\n * — the instantaneous gain-reduction (>= 0 for compression/limiting,\n * also >= 0 for downward expansion).\n * 4. smooth branching peak detector on x_L (eq.16)\n * 5. c_dB = -y_L + M (eq.23 line 3, eq.1)\n * 6. y[n] = 10^(c_dB / 20) * x[n] (eq.1 linear form)\n *\n * @param alphaAttack precomputed attack coefficient alpha_A (eq.7)\n * @param alphaRelease precomputed release coefficient alpha_R (eq.7)\n */\n private processSample(\n x: number,\n threshold: number,\n ratio: number,\n knee: number,\n alphaAttack: number,\n alphaRelease: number,\n makeup: number,\n ): number {\n const level = Math.abs(x);\n\n // eq.23 line 1: x_G = 20 log10 |x|. Floor silence to avoid -Inf dB.\n const xG = level < LEVEL_FLOOR ? 20 * Math.log10(LEVEL_FLOOR) : 20 * Math.log10(level);\n\n // eqs 3-4: static curve.\n const yG = computeStaticGain(xG, threshold, ratio, knee);\n\n // eq.23 line 2: gain-reduction control signal (log domain, after the gain\n // computer). x_L >= 0 means \"this much reduction is wanted right now\".\n const xL = xG - yG;\n\n // eq.16 smooth branching peak detector — smooth the gain-reduction signal.\n // Attack branch when more reduction is wanted than currently applied;\n // release branch otherwise. The smooth (vs plain) branching form low-passes\n // BOTH branches so there is no discontinuity at the attack<->release switch.\n if (xL > this.envelopeDb) {\n // attack: x_L[n] > y_L[n-1]\n this.envelopeDb = alphaAttack * this.envelopeDb + (1 - alphaAttack) * xL;\n } else {\n // release: x_L[n] <= y_L[n-1]\n this.envelopeDb = alphaRelease * this.envelopeDb + (1 - alphaRelease) * xL;\n }\n\n // eq.23 line 3 + eq.1 make-up: control voltage c_dB = -y_L + M.\n const controlDb = -this.envelopeDb + makeup;\n\n // eq.1 linear form: y[n] = c[n] * x[n], with c[n] = 10^(c_dB/20).\n const controlLinear = 10 ** (controlDb / 20);\n return controlLinear * x;\n }\n\n /**\n * Process a block. `input` and `output` may alias (in-place is fine). The\n * attack/release coefficients are computed once per block (k-rate params);\n * the per-sample side-chain runs the eq.16 detector and eq.1 multiply.\n */\n process(input: Float32Array, output: Float32Array, params: DynamicsParams): void {\n // eq.7: tau -> alpha, once per block (k-rate).\n const alphaAttack = timeConstantToCoefficient(params.attack, this.sampleRate);\n const alphaRelease = timeConstantToCoefficient(params.release, this.sampleRate);\n\n const n = Math.min(input.length, output.length);\n for (let i = 0; i < n; i++) {\n output[i] = this.processSample(\n input[i],\n params.threshold,\n params.ratio,\n params.knee,\n alphaAttack,\n alphaRelease,\n params.makeup,\n );\n }\n }\n}\n","/*\n * Modulated-delay processor core — context-free DSP math for the unified\n * delay-line modulation effect behind delay/echo, chorus, flanger, vibrato and\n * doubling.\n *\n * Implements the canonical unified circuit from:\n * J. Dattorro, \"Effect Design, Part 2: Delay-Line Modulation and Chorus\",\n * J. Audio Eng. Soc., Vol. 45, No. 10, pp. 764-788 (1997).\n *\n * Topology (Dattorro 1997, Fig. 36, p.775) — ONE modulated delay line wrapped\n * in dry/wet/feedback paths:\n *\n * H(z) = (blend + feedforward * z^-i.frac) / (1 + feedback * z^-center)\n *\n * - blend — dry path gain on x[n].\n * - feedforward — gain on the MODULATED wet tap z^-i.frac, where the read\n * pointer i.frac = NOMINAL_DELAY + CHORUS_WIDTH * y[n] sweeps with the LFO.\n * - feedback — gain on a FIXED center tap z^-center. Dattorro p.775: the\n * feedback is taken from a SEPARATE fixed tap at the nominal center, NEVER\n * the modulated tap — feeding back a pitch-modulated signal is objectionable.\n *\n * Knob presets (Table 6, p.775) yield each effect from this one circuit:\n * vibrato blend=0, ff=1, fb=0 (100% wet)\n * flanger blend=0.7071, ff=0.7071, fb=-0.7071 (blend=ff for deepest trough)\n * chorus blend=0.7071, ff=1, fb=0.7071 (white-chorus knobs: blend=fb, ff=1)\n * doubling blend=0.7071, ff=0.7071, fb=0\n * echo blend=1, ff<=1, fb<1 (modulation off)\n * Stability requires |feedback| <= 0.9999999 (Table 6, p.776).\n *\n * LFO: sinusoidal, bipolar [-1, 1] (Dattorro p.767 — a triangular LFO gives an\n * unnatural piecewise-constant instantaneous pitch). Modulated delay in samples\n * is delayTime + depth*sin(phase); phase advances at `rate` Hz. Stereo uses a\n * quadrature LFO (90 deg per channel, p.776), realized by seeding each channel's\n * core with lfoPhase0 = ch * pi/2 at construction (see modulated-delay.ts).\n *\n * Interpolation: the read pointer lands between samples, so the fractional delay\n * is interpolated with a Lagrange FIR filter (Laakso, Valimaki, Karjalainen &\n * Laine, \"Splitting the Unit Delay\", IEEE SP Mag 1996, Eq. 42 / N=1,3 table).\n * Lagrange is FIR (stateless coefficients): updating the coefficients every\n * sample under modulation does NOT excite the recursive transient that plagues\n * an allpass fractional-delay filter (Laakso p.52), and its magnitude stays <= 1\n * for feedback-loop safety (Laakso p.42). Two orders are exposed:\n * - \"linear\" (N=1, Laakso Eq.43): h0 = 1-d, h1 = d.\n * - \"cubic\" (N=3, Laakso table): the 4-tap maximally-flat interpolator, with\n * D = 1 + d so the fraction sits between taps 1 and 2 (M_opt centering).\n * Allpass interpolation is deliberately deferred: under modulation it produces\n * transients and is clean only over ~+/-1 semitone (Dattorro p.770-774).\n *\n * NOTE on \"white chorus\": Dattorro (p.776) defines white chorus as negative\n * feedback AND allpass fractional-delay interpolation. This core uses the\n * white-chorus KNOB settings (the negative-feedback path) but substitutes\n * cubic-Lagrange interpolation (Laakso 1996) for Dattorro's allpass — it is\n * transient-safe under modulation (Laakso p.52) at the cost of some\n * high-frequency trough depth versus a true allpass-interpolated white chorus.\n *\n * This file holds ONLY pure numeric math (plain numbers + Float32Array). It has\n * no AudioWorklet / global dependencies, so it is unit-testable directly; the\n * worklet shell in modulated-delay.ts delegates to it.\n */\n\n/** Fractional-delay interpolation order. Both are Lagrange FIR (Laakso 1996). */\nexport type InterpolationMode = \"cubic\" | \"linear\";\n\n/** Parameters consumed per processing block. */\nexport interface ModulatedDelayParams {\n /** Nominal (center) delay in milliseconds — the fixed feedback tap center. */\n delayTime: number;\n /** Peak LFO delay excursion in milliseconds (CHORUS_WIDTH). 0 = pure delay. */\n depth: number;\n /** LFO frequency f_e in Hz. */\n rate: number;\n /** Feedback gain on the FIXED center tap. |feedback| <= 0.9999999 for stability. */\n feedback: number;\n /** Dry path gain (blend). */\n blend: number;\n /** Wet (modulated tap) gain (feedforward). */\n feedforward: number;\n /** Fractional-delay interpolation order (Lagrange FIR). */\n interpolation: InterpolationMode;\n}\n\n/**\n * The Dattorro Table 6 knob constant 1/sqrt(2), printed VERBATIM as 0.7071 in\n * the paper (p.775 — Dattorro quantized it to 4 decimals for his q23 fixed-point\n * hardware). The factory presets (cacophony.ts) and the white-chorus condition\n * blend = feedback use this exact printed value, NOT the full-precision\n * Math.SQRT1_2, so the shipped knobs match the paper's table digit-for-digit.\n */\n// biome-ignore lint/suspicious/noApproximativeNumericConstant: Dattorro Table 6 prints 0.7071 verbatim; matching the paper digit-for-digit is intentional, not an inexact Math.SQRT1_2.\nexport const DATTORRO_INV_SQRT2 = 0.7071;\n\n/**\n * Default AudioParam values for the modulated-delay worklet, in the worklet's\n * own units. SINGLE SOURCE OF TRUTH — the worklet's `parameterDescriptors`\n * (modulated-delay.ts) builds its descriptors from this table, and tests that\n * need the shipped defaults import it here rather than re-typing literals.\n *\n * Defaults: a short, dry, slow setting (a transparent near-bypass) — the\n * createDelay/createChorus/createFlanger/createVibrato/createDoubling factories\n * (cacophony.ts) spread Dattorro Table 6 knob presets over these.\n */\nexport const MODULATED_DELAY_DEFAULTS: Readonly<ModulatedDelayParams> = {\n delayTime: 5,\n depth: 0,\n rate: 0.5,\n feedback: 0,\n blend: 1,\n feedforward: DATTORRO_INV_SQRT2,\n interpolation: \"cubic\",\n};\n\n/** Maximum nominal delay (ms) the buffer is sized for (delayTime AudioParam max). */\nexport const MAX_DELAY_MS = 1000;\n/** Maximum LFO excursion (ms) the buffer is sized for (depth AudioParam max). */\nexport const MAX_DEPTH_MS = 50;\n/** Stability bound on |feedback| (Dattorro Table 6, q23 max, p.776). */\nexport const MAX_FEEDBACK = 0.9999999;\n\n/**\n * Lagrange linear (N=1) fractional-delay coefficients (Laakso 1996 Eq.43):\n * h0 = 1 - d, h1 = d for fractional delay d in [0, 1).\n * Two-tap FIR; h0 weights the integer tap, h1 the next sample. h0 + h1 = 1\n * (partition of unity, DC gain 1). Exposed pure for isolated testing.\n */\nexport function lagrangeLinear(d: number): [number, number] {\n return [1 - d, d];\n}\n\n/**\n * Lagrange cubic (N=3) fractional-delay coefficients (Laakso 1996, N=3 table,\n * p.41). The 4-tap maximally-flat interpolator evaluated at total delay\n * D = 1 + d, so the desired fraction d in [0, 1) sits between taps 1 and 2\n * (Laakso M_opt centering, Eq.21) — the regime where |H| <= 1:\n *\n * h0 = -(D-1)(D-2)(D-3)/6\n * h1 = D(D-2)(D-3)/2\n * h2 = -D(D-1)(D-3)/2\n * h3 = D(D-1)(D-2)/6\n *\n * The returned taps weight delay-line samples at integer offsets {i-1, i, i+1,\n * i+2} relative to the integer part of the read pointer. Sum = 1 (DC gain 1).\n */\nexport function lagrangeCubic(d: number): [number, number, number, number] {\n const D = 1 + d;\n return [\n (-(D - 1) * (D - 2) * (D - 3)) / 6,\n (D * (D - 2) * (D - 3)) / 2,\n (-D * (D - 1) * (D - 3)) / 2,\n (D * (D - 1) * (D - 2)) / 6,\n ];\n}\n\n/** Convert milliseconds to a (fractional) sample count at the given rate. */\nfunction msToSamples(ms: number, sampleRate: number): number {\n return (ms / 1000) * sampleRate;\n}\n\n/**\n * Stateful modulated-delay processor for ONE channel. Owns the circular delay\n * buffer, the write pointer and the LFO phase, and is reused block-to-block so\n * the delay-line contents, feedback recirculation and LFO continuity all survive\n * across process() calls — constructing a fresh instance per block would clear\n * the buffer and reset the LFO, producing audible clicks (same hazard documented\n * for the dynamics ballistics and ADAA history cores).\n *\n * The buffer is sized ONCE in the constructor for MAX_DELAY_MS + MAX_DEPTH_MS\n * plus a margin for the cubic interpolator's 4-tap window, so the read pointer\n * can never run past the allocation regardless of the (clamped) params.\n */\nexport class ModulatedDelayProcessor {\n /** Circular delay line. */\n private readonly buffer: Float32Array;\n /** Length of the circular buffer (samples). */\n private readonly size: number;\n /** Write head index into `buffer`. */\n private writeIndex = 0;\n /** Current LFO phase (radians). */\n private lfoPhase: number;\n\n constructor(\n private readonly sampleRate: number,\n lfoPhase0 = 0,\n ) {\n // Size for the worst case: max nominal delay + max excursion + cubic margin\n // (the cubic interpolator reads one sample before and two after the integer\n // tap). +4 samples of slack keeps every read index inside the allocation.\n const maxDelaySamples = msToSamples(MAX_DELAY_MS + MAX_DEPTH_MS, sampleRate);\n this.size = Math.ceil(maxDelaySamples) + 4;\n this.buffer = new Float32Array(this.size);\n this.lfoPhase = lfoPhase0;\n }\n\n /** Clear the delay line, write head and LFO phase. */\n reset(): void {\n this.buffer.fill(0);\n this.writeIndex = 0;\n this.lfoPhase = 0;\n }\n\n /** Read the delay line at integer offset `delaySamples` behind the write head. */\n private readInteger(delaySamples: number): number {\n let idx = this.writeIndex - delaySamples;\n idx %= this.size;\n if (idx < 0) idx += this.size;\n return this.buffer[idx];\n }\n\n /**\n * Read the delay line at a fractional delay `delaySamples` behind the write\n * head, interpolating with the selected Lagrange FIR (Laakso 1996).\n */\n private readFractional(delaySamples: number, mode: InterpolationMode): number {\n const intDelay = Math.floor(delaySamples);\n const frac = delaySamples - intDelay;\n\n // Linear (N=1), or cubic degraded to linear near the head: when intDelay < 1\n // the cubic's newest tap (intDelay-1) would be a FUTURE sample, so fall back\n // to the 2-tap linear interpolator. Linear reaches delay 0 exactly (offset 0\n // = the just-written current sample) for true through-zero flange/vibrato.\n if (mode === \"linear\" || intDelay < 1) {\n const [h0, h1] = lagrangeLinear(frac);\n // The desired delay intDelay+frac sits between offset intDelay (newer) and\n // offset intDelay+1 (older). h0 weights the integer tap, h1 the next-OLDER\n // sample, so a larger frac pulls toward the larger (older) delay.\n return h0 * this.readInteger(intDelay) + h1 * this.readInteger(intDelay + 1);\n }\n\n // Cubic: D = 1 + frac centers the fraction between taps 1 and 2, so the four\n // taps run newest->oldest at offsets {intDelay-1, intDelay, intDelay+1,\n // intDelay+2}, weighted by h0..h3 from the Laakso N=3 table in that order.\n const [h0, h1, h2, h3] = lagrangeCubic(frac);\n return (\n h0 * this.readInteger(intDelay - 1) +\n h1 * this.readInteger(intDelay) +\n h2 * this.readInteger(intDelay + 1) +\n h3 * this.readInteger(intDelay + 2)\n );\n }\n\n /**\n * Process a block. `input` and `output` may alias (in-place is fine).\n *\n * Per sample (Dattorro 1997 Fig. 36):\n * 1. center = fixed nominal delay tap (samples) (feedback tap)\n * 2. fb = feedback * delayLine[center] (fixed tap)\n * 3. w[n] = x[n] - feedback*delayLine[center] -> written (recirculation)\n * The summer SUBTRACTS the feedback (Fig. 36's negative-feedback summer,\n * p.775), realizing the paper's denominator 1 + feedback*z^-center (p.776).\n * 4. i.frac = center + depth * sin(lfoPhase) (modulated tap)\n * 5. wet = interpolate delayLine at i.frac (Lagrange FIR)\n * 6. y[n] = blend * w[n] + feedforward * wet (dry + wet mix)\n * The dry (blend) tap reads the recirculation node w[n], NOT the raw input\n * x[n], so both numerator taps share the feedback denominator and the\n * transfer function is exactly H(z) = (blend + feedforward*z^-i)/(1 +\n * feedback*z^-center) (p.776). This is the canonical allpass-comb: with\n * blend = feedback and feedforward = 1 it collapses to a unity-magnitude\n * allpass (the white-chorus condition, p.776). When feedback = 0, w[n] =\n * x[n], so the dry path is the plain input as expected for the no-feedback\n * presets (vibrato/industry chorus/doubling/echo).\n * The LFO phase advances by 2*pi*rate/sampleRate each sample.\n */\n process(input: Float32Array, output: Float32Array, params: ModulatedDelayParams): void {\n // k-rate-style coefficients computed once per block.\n const centerSamples = msToSamples(params.delayTime, this.sampleRate);\n const depthSamples = msToSamples(params.depth, this.sampleRate);\n const feedback = Math.max(-MAX_FEEDBACK, Math.min(MAX_FEEDBACK, params.feedback));\n const blend = params.blend;\n const feedforward = params.feedforward;\n const mode = params.interpolation;\n const phaseInc = (2 * Math.PI * params.rate) / this.sampleRate;\n\n // The fixed feedback tap reads the integer center; clamp to a valid offset.\n const centerTap = Math.max(0, Math.min(this.size - 1, Math.round(centerSamples)));\n\n const n = Math.min(input.length, output.length);\n for (let i = 0; i < n; i++) {\n const x = input[i];\n\n // Feedback from the FIXED center tap (Dattorro p.775: never the modulated\n // tap). Read BEFORE writing this sample so the loop delay is `center`.\n const fb = feedback * this.readInteger(centerTap);\n\n // Write the recirculated sample into the delay line at the write head.\n // The feedback is SUBTRACTED (Dattorro Fig. 36's negative-feedback summer,\n // p.775), so the transfer function denominator is 1 + feedback*z^-center.\n const w = x - fb;\n this.buffer[this.writeIndex] = w;\n\n // Modulated read pointer i.frac = center + depth * LFO (Dattorro eq, p.765).\n let readDelay = centerSamples + depthSamples * Math.sin(this.lfoPhase);\n // Allow the delay down to 0 so flangers/vibrato can sweep through absolute\n // zero (Dattorro p.775; Table 7 vibrato/flange onset 0 ms). Offset 0 is the\n // legitimate just-written current sample (we write before reading the wet\n // tap). readFractional degrades cubic -> linear near the head so no future\n // sample is ever read. The upper clamp keeps the cubic's oldest tap\n // (intDelay+2) inside the allocation.\n if (readDelay < 0) readDelay = 0;\n if (readDelay > this.size - 3) readDelay = this.size - 3;\n const wet = this.readFractional(readDelay, mode);\n\n // Fig. 36 output: dry (blend, off the recirculation node w[n]) + modulated\n // wet (feedforward). Both taps share the feedback denominator, giving\n // H(z) = (blend + feedforward*z^-i)/(1 + feedback*z^-center) (p.776).\n output[i] = blend * w + feedforward * wet;\n\n // Advance write head and LFO phase.\n this.writeIndex = (this.writeIndex + 1) % this.size;\n this.lfoPhase += phaseInc;\n if (this.lfoPhase >= 2 * Math.PI) this.lfoPhase -= 2 * Math.PI;\n }\n }\n}\n","/*\n * Time-stretch core — context-free, unit-testable OFFLINE independent\n * time-scaling (change tempo WITHOUT changing pitch).\n *\n * Algorithm: Zdeněk Průša & Nicki Holighaus, \"Phase Vocoder Done Right\",\n * Proc. EUSIPCO 2017 / arXiv:2202.07382 (2022). The synthesis phase is\n * reconstructed from the analysis-phase gradients via Phase Gradient Heap\n * Integration (PGHI / RTPGHI), originally Průša, Balazs & Søndergaard 2017\n * (\"A noniterative method for reconstruction of phase from STFT magnitude\")\n * and Průša & Søndergaard 2016 (RTPGHI, DAFx-16). No peak picking, no partial\n * tracking, no transient detection — phase coherence (both horizontal/time and\n * vertical/frequency) is enforced automatically by integrating along the\n * spectrogram's magnitude ridges (Průša 2022, p.2-3, Fig. 2).\n *\n * WHY THIS IS OFFLINE, NOT A REAL-TIME WORKLET\n * --------------------------------------------\n * The project's OLA worklet base (`src/processors/ola.ts`) hardwires the hop to\n * the 128-sample Web Audio render quantum and runs analysis hop == synthesis\n * hop, i.e. it is a UNITY-RATE analysis/synthesis loop and cannot change the\n * time base in real time (see reports/scout-resurrection.md §B3). Genuine\n * independent time-stretch requires analysis hop ≠ synthesis hop\n * (a_s = α·a_a). So this is implemented as a whole-buffer offline transform:\n * STFT the entire signal at analysis hop a_a → reconstruct synthesis phase with\n * PGHI heap integration → ISTFT/overlap-add at synthesis hop a_s = round(α·a_a)\n * → return a new buffer of length ≈ input·α at the SAME pitch. (Průša 2022,\n * Eq.7: α = a_s/a_a; output length = αL.)\n *\n * The spectrum/phase manipulation that carries the testable invariants lives\n * here, context-free, mirroring the project's core/shell split\n * (phase-vocoder-core.ts, waveshaper-core.ts, dynamics-core.ts).\n */\n\nimport FFT from \"fft.js\";\n\n/** Options for {@link timeStretch}. */\nexport interface TimeStretchOptions {\n /**\n * FFT length / window length `M` (samples), power of two (fft.js radix-4\n * constraint). Průša 2022 evaluation used M=8192; for general short cacophony\n * buffers a smaller window keeps frequency-resolution-vs-time-resolution\n * balanced. Default 2048 (matches the project's phase-vocoder block size).\n */\n fftSize?: number;\n /**\n * Analysis hop `a_a` (samples). Synthesis hop is `a_s = round(a_a·factor)`\n * (Průša 2022, Eq.7). Default = fftSize/4 (75% overlap, COLA-exact for Hann).\n */\n analysisHop?: number;\n /**\n * Relative tolerance `tol` for the significant-bin set; `abstol = tol·max(s)`\n * (Průša 2022, p.3, Algorithm 1 line 1). Default 1e-6 (paper value).\n */\n tol?: number;\n /**\n * Deterministic RNG seed for the random phase assigned to insignificant bins\n * (Algorithm 1 line 3). Default 1 — deterministic so tests are reproducible.\n */\n randomSeed?: number;\n}\n\nconst TWO_PI = 2 * Math.PI;\n\n/** Hann window of `length` (periodic / DFT-even form), matching phase-vocoder.ts. */\nfunction hannWindow(length: number): Float32Array {\n const win = new Float32Array(length);\n for (let i = 0; i < length; i++) {\n win[i] = 0.5 * (1 - Math.cos((TWO_PI * i) / length));\n }\n return win;\n}\n\n/**\n * Principal-argument operator `[x]_2π = x − 2π·round(x/2π)` — wraps to the\n * nearest interval of length 2π (Průša 2022, p.2, the `[·]_2π` operator with\n * round-to-closest-integer `⌈·⌋`). Used by every finite-difference scheme.\n */\nfunction princarg(x: number): number {\n return x - TWO_PI * Math.round(x / TWO_PI);\n}\n\n/** Mulberry32 — tiny deterministic PRNG so insignificant-bin phase is reproducible. */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Flat typed-array binary MAX-heap keyed by a Float64 magnitude, carrying an\n * Int32 payload (here: an encoded (bin, frameSelector) tuple). No per-pop\n * allocation — the backing arrays are sized once to the worst case and reused\n * by `clear()`. This is the \"self-sorting max-heap\" of Průša 2022 Algorithm 1\n * (always pops the globally-highest-magnitude known coefficient).\n */\nclass MaxHeap {\n private keys: Float64Array;\n private vals: Int32Array;\n private size = 0;\n\n constructor(capacity: number) {\n this.keys = new Float64Array(capacity);\n this.vals = new Int32Array(capacity);\n }\n\n clear(): void {\n this.size = 0;\n }\n\n get length(): number {\n return this.size;\n }\n\n push(key: number, val: number): void {\n let i = this.size++;\n this.keys[i] = key;\n this.vals[i] = val;\n // sift up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (this.keys[parent] >= this.keys[i]) break;\n this.swap(i, parent);\n i = parent;\n }\n }\n\n /** Pop the payload of the max-key element. Caller must check `length > 0`. */\n pop(): number {\n const topVal = this.vals[0];\n const last = --this.size;\n this.keys[0] = this.keys[last];\n this.vals[0] = this.vals[last];\n // sift down\n let i = 0;\n for (;;) {\n const l = 2 * i + 1;\n const r = l + 1;\n let largest = i;\n if (l < this.size && this.keys[l] > this.keys[largest]) largest = l;\n if (r < this.size && this.keys[r] > this.keys[largest]) largest = r;\n if (largest === i) break;\n this.swap(i, largest);\n i = largest;\n }\n return topVal;\n }\n\n private swap(a: number, b: number): void {\n const k = this.keys[a];\n this.keys[a] = this.keys[b];\n this.keys[b] = k;\n const v = this.vals[a];\n this.vals[a] = this.vals[b];\n this.vals[b] = v;\n }\n}\n\n/**\n * OFFLINE independent time-stretch of a single channel.\n *\n * @param input Mono signal.\n * @param factor Stretch factor `α = a_s/a_a` (Průša 2022, Eq.7). `>1` lengthens\n * (slower tempo), `<1` shortens (faster tempo); pitch unchanged.\n * @returns A new `Float32Array` of length ≈ round(input.length·factor) at the\n * SAME pitch as the input.\n *\n * Pipeline (Průša 2022, the STFT analysis-modify-synthesis loop, p.2-3):\n * 1. Whole-signal STFT (Hann window, analysis hop a_a) → magnitude s + phase φ_a.\n * 2. Phase gradients: CENTERED time derivative Δt φ_a (Eqs 13-15, heterodyned)\n * and the directional forward/backward frequency difference Δf φ_a (Eqs\n * 16-17). See the gradient block below for why the directional (rather than\n * the centered+trapezoidal) frequency integration is used.\n * 3. Per frame: PGHI heap integration (Algorithm 1) → synthesis phase φ_s,\n * using synthesis hop a_s for the trapezoidal TIME update (Eq.8) and\n * synthesis freq step b_s = α·b_a for the directional FREQUENCY update.\n * 4. ISTFT each synthesis frame (s·e^{iφ_s}) + Hann window, overlap-add at\n * synthesis hop a_s, normalized by the COLA window-energy envelope.\n */\nexport function timeStretch(input: Float32Array, factor: number, opts: TimeStretchOptions = {}): Float32Array {\n if (!(factor > 0)) {\n throw new Error(`timeStretch: factor must be > 0, got ${factor}`);\n }\n\n // FFT length M must be a power of two (fft.js radix constraint) and large\n // enough that the default hop M/4 is a positive integer with real overlap.\n // M < 4 would make the default hop fractional (M=2 → 0.5); require M ≥ 16 so\n // a Hann window has a meaningful main lobe and the default 75%-overlap hop is\n // a sensible integer.\n const M = opts.fftSize ?? 2048;\n if (!Number.isInteger(M) || M < 16 || (M & (M - 1)) !== 0) {\n throw new Error(`timeStretch: fftSize must be a power of two ≥ 16, got ${M}`);\n }\n const aA = opts.analysisHop ?? M / 4; // analysis hop a_a (M/4 is integer since M is a power of two ≥ 16)\n // The analysis hop must be a positive INTEGER: the frame loop reads\n // padded[n*aA + i], and a fractional hop reads off-grid (undefined → NaN).\n // It must also not exceed M (hops larger than the window leave gaps with no\n // COLA coverage).\n if (!Number.isInteger(aA) || aA < 1 || aA > M) {\n throw new Error(`timeStretch: analysisHop must be a positive integer in [1, fftSize=${M}], got ${aA}`);\n }\n const aS = Math.max(1, Math.round(aA * factor)); // synthesis hop a_s = round(α·a_a), Eq.7\n const tol = opts.tol ?? 1e-6;\n const rand = mulberry32(opts.randomSeed ?? 1);\n\n const nyq = M / 2; // index of the Nyquist bin\n const nBins = nyq + 1; // non-redundant bins 0..M/2\n\n // Analysis (b_a) and synthesis (b_s = α·b_a) frequency steps. The per-bin\n // angular spacing on the unwrapped axis is 2π·bin/M; the frequency update\n // accumulates the directional Δf gradient scaled by the synthesis step.\n // We carry the ratio (b_s/b_a) = a_s/a_a directly via the gradient scaling\n // below (see freq-update comment), so b_a/b_s never need explicit values.\n\n const win = hannWindow(M);\n\n // ---- Framing geometry -------------------------------------------------\n // Pad the front and back by one window so the first and last samples get\n // full window coverage (standard STFT zero-padding). Number of analysis\n // frames covers the whole padded signal at hop a_a.\n const pad = M;\n const paddedLen = input.length + 2 * pad;\n const padded = new Float32Array(paddedLen);\n padded.set(input, pad);\n\n const nFrames = Math.max(1, Math.floor((paddedLen - M) / aA) + 1);\n\n // ---- FFT scratch ------------------------------------------------------\n const fft = new FFT(M);\n // fft.js returns its complex arrays typed as any[]; the runtime is a flat\n // interleaved Float32Array [re,im,re,im,...] of length 2*M.\n const frameTime = new Float32Array(M);\n const freq = fft.createComplexArray() as unknown as Float32Array;\n\n // Per-frame magnitude `s` and analysis phase `φ_a`, bins 0..nyq.\n const mag: Float32Array[] = new Array(nFrames);\n const phase: Float32Array[] = new Array(nFrames);\n\n for (let n = 0; n < nFrames; n++) {\n const start = n * aA;\n for (let i = 0; i < M; i++) {\n frameTime[i] = padded[start + i] * win[i];\n }\n fft.realTransform(freq, frameTime);\n const s = new Float32Array(nBins);\n const ph = new Float32Array(nBins);\n for (let m = 0; m < nBins; m++) {\n const re = freq[2 * m];\n const im = freq[2 * m + 1];\n s[m] = Math.hypot(re, im);\n ph[m] = Math.atan2(im, re);\n }\n mag[n] = s;\n phase[n] = ph;\n }\n\n // ---- Phase gradients (Průša 2022, centered finite differences) --------\n // Time-direction derivative Δt φ_a (Eqs 13-15), HETERODYNED: the linear\n // phase advance 2π·m·a_a/M of a stationary sinusoid is removed before the\n // principal-argument wrap, then added back. We store Δt already MULTIPLIED\n // BY a_a (i.e. the per-analysis-hop phase increment Δt·a_a), because the\n // synthesis time update multiplies by a_s and the only thing that matters is\n // the ratio a_s/a_a — storing the per-hop increment lets the trapezoidal\n // update (Eq.8) just scale by (a_s/a_a)/... See the heap loop below.\n //\n // dtInc[n][m] = a_a · (Δt,cent φ_a)(m,n) (radians advanced per analysis hop)\n // dfFwd[n][m] = [φ_a(m+1,n) − φ_a(m,n)]_2π — the FORWARD wrapped frequency-\n // direction difference (Eq.17 numerator, b_a units). Used\n // DIRECTIONALLY: forward (Δf,fwd) for up-propagation, backward\n // (Δf,back(m) = Δf,fwd(m-1)) for down-propagation.\n //\n // FREQUENCY-INTEGRATION SCHEME — why forward/backward (rectangle) rather than\n // the centered+trapezoidal form of Algorithm 1 lines 17/22. The paper presents\n // backward (Eq.16), forward (Eq.17) AND centered (Eq.18) finite-difference\n // schemes for the frequency-direction gradient (it makes the explicit \"any of\n // the schemes can be used\" remark for the time-direction derivative and applies\n // the same backward/forward/centered choice to frequency). Algorithm 1 as written (lines 17/22)\n // itself uses trapezoidal frequency integration; we DELIBERATELY do not, for\n // the measured reason documented below. Directional forward/backward\n // integration telescopes EXACTLY to the analysis phase along a ridge:\n // up: φ_s(m+1) = φ_s(m) + Δf,fwd(m) ⇒ φ_a(m+1) − φ_a(m)\n // down: φ_s(m-1) = φ_s(m) − Δf,fwd(m-1) ⇒ φ_a(m-1) − φ_a(m)\n // so it preserves both spectral identity at α=1 and the off-bin tone's peak\n // location. The centered+trapezoidal variant was implemented faithfully and\n // MEASURED to be strictly worse here: it lowers tonal spectral purity in every\n // tested case (e.g. 440 Hz factor=1: 0.94 → 0.56) and SHIFTS the dominant bin\n // of an off-bin tone at factor≠1 (440 Hz, bin 41 → 45 at factor=2), breaking\n // pitch preservation, while improving chirp spectral spread only marginally\n // (≈8% at factor 2, ≈1% at factor 3) and single-impulse temporal spread ≈11%.\n // See reports/fix-B3-timestretch.md for the full measurements. This is a\n // DELIBERATE, MEASURED deviation from Algorithm 1's trapezoidal frequency\n // propagation (NOT the paper's baseline), chosen because trapezoidal broke\n // pitch preservation in our tests; the forward/backward directional schemes\n // are themselves paper-sanctioned gradient estimates (Eq.16/17).\n const dtInc: Float32Array[] = new Array(nFrames);\n const dfFwd: Float32Array[] = new Array(nFrames);\n\n for (let n = 0; n < nFrames; n++) {\n dtInc[n] = new Float32Array(nBins);\n dfFwd[n] = new Float32Array(nBins);\n }\n\n for (let n = 0; n < nFrames; n++) {\n const ph = phase[n];\n const phPrev = n > 0 ? phase[n - 1] : null;\n const phNext = n < nFrames - 1 ? phase[n + 1] : null;\n\n for (let m = 0; m < nBins; m++) {\n // expected linear advance of bin m over one analysis hop\n const expected = (TWO_PI * m * aA) / M;\n\n // backward & forward heterodyned time differences (Eqs 13-14):\n // Δt,back·a_a = [φ(n) − φ(n−1) − expected]_2π + expected\n // Δt,fwd·a_a = [φ(n+1) − φ(n) − expected]_2π + expected\n let dt: number;\n if (phPrev && phNext) {\n const back = princarg(ph[m] - phPrev[m] - expected) + expected;\n const fwd = princarg(phNext[m] - ph[m] - expected) + expected;\n dt = 0.5 * (back + fwd); // centered (Eq.15)\n } else if (phPrev) {\n dt = princarg(ph[m] - phPrev[m] - expected) + expected; // backward only at last frame\n } else if (phNext) {\n dt = princarg(phNext[m] - ph[m] - expected) + expected; // forward only at first frame\n } else {\n dt = expected; // single frame: assume stationary\n }\n dtInc[n][m] = dt;\n\n // frequency-direction FORWARD wrapped difference (Eq.17): the directed\n // increment from bin m to bin m+1. No heterodyning (a pure sinusoid has\n // Δf φ ≈ 0, the paper's testable property, p.2). The last bin has no m+1.\n dfFwd[n][m] = m < nBins - 1 ? princarg(ph[m + 1] - ph[m]) : 0;\n }\n }\n\n // ---- PGHI heap integration (Průša 2022, Algorithm 1) ------------------\n // Produce the synthesis phase φ_s frame by frame. The frequency update uses\n // synthesis freq step b_s = α·b_a; since dfFwd holds the per-bin directional\n // (forward/backward, Eq.16/17) difference (already in \"per b_a\" units of\n // radians/binstep), scaling it by the factor α = a_s/a_a converts the b_a\n // step to the b_s step (b_s = α·b_a).\n const ratio = aS / aA; // = α, the time-scaling factor (Eq.7)\n const synthPhase: Float32Array[] = new Array(nFrames);\n for (let n = 0; n < nFrames; n++) synthPhase[n] = new Float32Array(nBins);\n\n // Heap payload encodes (bin << 1 | sel): sel=0 -> previous frame (n-1),\n // sel=1 -> current frame (n). Capacity 2·nBins (prev seeds + current bins).\n const heap = new MaxHeap(2 * nBins);\n const assigned = new Uint8Array(nBins); // whether φ_s(m,n) computed this frame\n const inSet = new Uint8Array(nBins); // significant set I membership this frame\n\n for (let n = 0; n < nFrames; n++) {\n const s = mag[n];\n const sPrev = n > 0 ? mag[n - 1] : null;\n const phiS = synthPhase[n];\n const phiSPrev = n > 0 ? synthPhase[n - 1] : null;\n\n // Algorithm 1 line 1: abstol = tol · max(s(·,n) ∪ s(·,n-1)).\n let maxS = 0;\n for (let m = 0; m < nBins; m++) {\n if (s[m] > maxS) maxS = s[m];\n if (sPrev && sPrev[m] > maxS) maxS = sPrev[m];\n }\n const abstol = tol * maxS;\n\n // Lines 2-3: significant set I = { m : s(m,n) > abstol }; insignificant\n // bins get random phase and do not propagate.\n heap.clear();\n let remaining = 0;\n for (let m = 0; m < nBins; m++) {\n assigned[m] = 0;\n if (s[m] > abstol) {\n inSet[m] = 1;\n remaining++;\n } else {\n inSet[m] = 0;\n phiS[m] = TWO_PI * rand(); // random phase, Algorithm 1 line 3\n assigned[m] = 1; // resolved (will not be propagated to)\n }\n }\n\n if (n === 0 || !phiSPrev || !sPrev) {\n // First frame (or no previous synthesis phase): there is no prior frame to\n // time-propagate from, so the column is integrated purely in frequency.\n // PGHI (Průša 2022, Algorithm 1) requires EVERY significant bin be assigned\n // through the integration. The significant set may be several DISCONNECTED\n // ridges (islands separated by insignificant bins): seeding only the single\n // global-max bin and frequency-spreading reaches just ITS island, leaving\n // every other island's bins at the Float32Array default (phase 0) — a\n // violation of the PGHI guarantee. So seed each connected component in\n // turn: anchor the highest-magnitude UNASSIGNED significant bin to its\n // analysis phase, frequency-spread the heap through its component, then\n // re-seed the next component until no significant bin is unassigned.\n while (remaining > 0) {\n let seed = -1;\n let seedMag = -1;\n for (let m = 0; m < nBins; m++) {\n if (inSet[m] && !assigned[m] && s[m] > seedMag) {\n seedMag = s[m];\n seed = m;\n }\n }\n if (seed < 0) break; // safety: no significant bin left\n phiS[seed] = phase[n][seed];\n assigned[seed] = 1;\n remaining--;\n heap.push(s[seed], (seed << 1) | 1);\n\n // Frequency-only spreading within this component (b_s = α·b_a folded into\n // `ratio`). Drains the heap to the component boundary; the outer loop then\n // re-seeds the next disconnected component.\n // up: φ_s(m+1)=φ_s(m)+b_s·Δf,fwd(m)\n // down: φ_s(m-1)=φ_s(m)−b_s·Δf,fwd(m-1) [= Δf,back(m)]\n while (heap.length > 0) {\n const top = heap.pop();\n const mh = top >> 1;\n if (mh + 1 < nBins && inSet[mh + 1] && !assigned[mh + 1]) {\n phiS[mh + 1] = phiS[mh] + ratio * dfFwd[n][mh];\n assigned[mh + 1] = 1;\n remaining--;\n heap.push(s[mh + 1], ((mh + 1) << 1) | 1);\n }\n if (mh - 1 >= 0 && inSet[mh - 1] && !assigned[mh - 1]) {\n phiS[mh - 1] = phiS[mh] - ratio * dfFwd[n][mh - 1];\n assigned[mh - 1] = 1;\n remaining--;\n heap.push(s[mh - 1], ((mh - 1) << 1) | 1);\n }\n }\n }\n continue;\n }\n\n // Lines 4-5: seed the heap with all significant PREVIOUS-frame coefficients\n // (their synthesis phase is already known from frame n-1).\n for (let m = 0; m < nBins; m++) {\n if (inSet[m]) {\n // key on the previous frame's magnitude (the known coefficient's mag)\n heap.push(sPrev[m], (m << 1) | 0); // sel=0 -> previous frame\n }\n }\n\n // Lines 6-27: integrate until all significant current-frame bins assigned.\n while (heap.length > 0 && remaining > 0) {\n const top = heap.pop();\n const mh = top >> 1;\n const sel = top & 1;\n\n if (sel === 0) {\n // Top is a previous-frame coefficient (n-1): propagate in TIME into the\n // current frame (Algorithm 1 lines 8-13). Trapezoidal time update\n // (Průša 2022, Eq.8):\n // φ_s(m,n) = φ_s(m,n-1) + (a_s/2)·[Δt(m,n-1) + Δt(m,n)]\n // dtInc holds the per-analysis-hop increment (Δt·a_a); the synthesis\n // step a_s = α·a_a, so a_s·Δt = α·(Δt·a_a) = ratio·dtInc.\n if (inSet[mh] && !assigned[mh]) {\n phiS[mh] = phiSPrev[mh] + 0.5 * ratio * (dtInc[n - 1][mh] + dtInc[n][mh]);\n assigned[mh] = 1;\n remaining--;\n heap.push(s[mh], (mh << 1) | 1); // now a current-frame known coeff\n }\n } else {\n // Top is a current-frame coefficient (n) with known synthesis phase:\n // propagate in FREQUENCY to m±1 (Algorithm 1 lines 15-26) using the\n // directed forward/backward gradient Δf,fwd with synthesis freq step\n // b_s = α·b_a (folded into `ratio`):\n // φ_s(m+1,n) = φ_s(m,n) + b_s·Δf,fwd(m,n)\n // φ_s(m-1,n) = φ_s(m,n) − b_s·Δf,fwd(m-1,n) [= Δf,back(m,n)]\n if (mh + 1 < nBins && inSet[mh + 1] && !assigned[mh + 1]) {\n phiS[mh + 1] = phiS[mh] + ratio * dfFwd[n][mh];\n assigned[mh + 1] = 1;\n remaining--;\n heap.push(s[mh + 1], ((mh + 1) << 1) | 1);\n }\n if (mh - 1 >= 0 && inSet[mh - 1] && !assigned[mh - 1]) {\n phiS[mh - 1] = phiS[mh] - ratio * dfFwd[n][mh - 1];\n assigned[mh - 1] = 1;\n remaining--;\n heap.push(s[mh - 1], ((mh - 1) << 1) | 1);\n }\n }\n }\n\n // Any significant bins the heap could not reach (disconnected ridge): fall\n // back to a pure time update so they still advance coherently rather than\n // being left at zero. This never double-assigns (guarded by `assigned`).\n if (remaining > 0) {\n for (let m = 0; m < nBins; m++) {\n if (inSet[m] && !assigned[m]) {\n phiS[m] = phiSPrev[m] + 0.5 * ratio * (dtInc[n - 1][m] + dtInc[n][m]);\n assigned[m] = 1;\n remaining--;\n }\n }\n }\n }\n\n // ---- ISTFT + overlap-add resynthesis at synthesis hop a_s -------------\n // Per-frame inverse STFT of s·e^{iφ_s} (Průša 2022, the per-frame inverse,\n // p.2), Hann-windowed again, overlap-added at synthesis hop a_s, then divided\n // by the accumulated squared-window envelope (canonical COLA normalization /\n // dual synthesis window, p.2). Output length = round(input.length·factor).\n const outLen = Math.max(1, Math.round(input.length * factor));\n const synthPad = M;\n const synthBufLen = (nFrames - 1) * aS + M + synthPad;\n const acc = new Float32Array(synthBufLen);\n const winEnergy = new Float32Array(synthBufLen);\n\n const specOut = fft.createComplexArray() as unknown as Float32Array;\n const timeOut = fft.createComplexArray() as unknown as Float32Array;\n\n for (let n = 0; n < nFrames; n++) {\n const s = mag[n];\n const phiS = synthPhase[n];\n // Build the half-spectrum (bins 0..nyq) as re/im, then mirror.\n for (let m = 0; m < nBins; m++) {\n const re = s[m] * Math.cos(phiS[m]);\n const im = s[m] * Math.sin(phiS[m]);\n specOut[2 * m] = re;\n specOut[2 * m + 1] = im;\n }\n // Nyquist and DC must be real for a real signal; imag already ~0 since we\n // mirror with conjugate symmetry next.\n fft.completeSpectrum(specOut);\n fft.inverseTransform(timeOut, specOut);\n\n const start = n * aS;\n for (let i = 0; i < M; i++) {\n const w = win[i];\n // timeOut is interleaved; real part at even indices.\n acc[start + i] += timeOut[2 * i] * w;\n winEnergy[start + i] += w * w;\n }\n }\n\n // Normalize by the COLA window-energy envelope (canonical dual window). The\n // analysis front-pad was `pad`; the synthesis frames begin at index 0 here,\n // and the analysis pad maps to a synthesis-side offset of round(pad·factor).\n for (let i = 0; i < synthBufLen; i++) {\n if (winEnergy[i] > 1e-8) acc[i] /= winEnergy[i];\n }\n\n const offset = Math.round(pad * factor);\n const out = new Float32Array(outLen);\n for (let i = 0; i < outLen; i++) {\n const src = offset + i;\n out[i] = src < synthBufLen ? acc[src] : 0;\n }\n return out;\n}\n\n/**\n * Multichannel convenience: time-stretch each channel independently with the\n * same factor/options. Returns one new `Float32Array` per channel, all the same\n * length (≈ round(channelLength·factor)).\n */\nexport function timeStretchChannels(\n channels: Float32Array[],\n factor: number,\n opts: TimeStretchOptions = {},\n): Float32Array[] {\n return channels.map((ch) => timeStretch(ch, factor, opts));\n}\n","/**\n * The Playback class encapsulates the functionality for playing audio in a web application.\n * It integrates with the repo's audio-context abstractions so playback works across native and mocked contexts.\n * This class allows for the manipulation of audio playback through various features such as:\n * - Playing and stopping audio\n * - Looping audio a specific number of times or infinitely\n * - Adjusting volume and playback rate\n * - Applying stereo or 3D (HRTF) panning\n * - Adding and removing filters to modify the audio output\n * - Handling audio looping with custom logic\n * - Fading audio in and out linearly or exponentially\n * - Seeking to specific points in the audio\n * - Checking if the audio is currently playing\n * - Cleaning up resources when the audio is no longer needed\n *\n * The class is designed to be flexible and can be used with different types of audio sources,\n * including buffer sources and media elements. It also provides detailed control over the audio's\n * spatial characteristics when using 3D audio.\n */\n\nimport { BasePlayback } from \"./basePlayback\";\nimport type { BaseSound, FadeType, LoopCount, PanType } from \"./cacophony\";\nimport type {\n AudioBuffer,\n AudioNode,\n AudioParam,\n AudioWorkletNode,\n BaseContext,\n BiquadFilterNode,\n GainNode,\n SourceNode,\n} from \"./context\";\nimport type { Sound } from \"./sound\";\nimport { WORKLETS } from \"./worklets\";\n\ntype PlaybackCloneOverrides = {\n loopCount: LoopCount;\n panType: PanType;\n};\n\nexport class Playback extends BasePlayback implements BaseSound {\n private context: BaseContext;\n public declare source?: SourceNode;\n loopCount: LoopCount = 0;\n currentLoop: number = 0;\n private buffer?: AudioBuffer;\n private _offset: number = 0;\n private _startTime: number = 0;\n private _playbackRate: number = 1;\n /**\n * Promise that tracks an in-flight media-element `.play()` settlement.\n * Set when the media element's `play()` returns a pending promise; cleared\n * once that promise settles. Used by `loopEnded` to avoid racing source-state\n * mutations (`seek`/`play`) against the deferred state transition driven by\n * this promise.\n */\n private _mediaPlayPromise?: Promise<void>;\n _fadeInConfig?: { duration: number; type: FadeType; perLoop: boolean; targetVolume: number };\n _fadeOutConfig?: { duration: number; type: FadeType };\n _loopEndCallback?: () => void;\n /**\n * Desired pitch-shift factor (1 = no shift, 2 = +1 octave, 0.5 = -1 octave).\n * Stored even before the worklet node exists so a value set on a cleaned-up /\n * not-yet-built playback is honoured once {@link setPitchShift} builds the node.\n */\n private _pitchFactor: number = 1;\n /**\n * Creates an instance of the Playback class.\n * @throws {Error} Throws an error if an invalid pan type is provided.\n */\n\n constructor(\n public origin: Sound,\n source: SourceNode,\n gainNode: GainNode,\n ) {\n super(origin);\n this.context = origin.context;\n this.loopCount = origin.loopCount;\n this.setPanType(origin.panType, origin.context);\n this.source = source;\n if (\"buffer\" in source && source.buffer) {\n this.buffer = source.buffer;\n }\n this.setupSourceNode(source);\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(this.source, this.panner!);\n this.panner!.connect(this.gainNode!);\n }\n\n override setPanType(panType: PanType, audioContext: BaseContext): void {\n const previousPanner = this.panner;\n super.setPanType(panType, audioContext);\n\n if (this.panner === previousPanner || !this.panner || !this.source || !this.gainNode) {\n return;\n }\n\n this.setEffectChainEndpoints(this.source, this.panner);\n this.panner.connect(this.gainNode);\n }\n\n private setupSourceNode(source: SourceNode) {\n if (\"mediaElement\" in source && source.mediaElement) {\n source.mediaElement.onended = this.loopEnded;\n } else if (\"onended\" in source) {\n source.onended = this.loopEnded;\n } else {\n throw new Error(\"Unsupported source type\");\n }\n }\n\n /**\n * Gets the duration of the audio in seconds.\n * @returns {number} The duration of the audio or NaN if the duration is unknown.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n get duration() {\n if (!this.source) {\n throw new Error(\"Cannot get duration of a sound that has been cleaned up\");\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n return this.source.mediaElement.duration;\n }\n if (!this.buffer) {\n return NaN;\n }\n return this.buffer.duration || NaN;\n }\n\n /**\n * Gets the current playback rate of the audio.\n */\n\n get playbackRate() {\n return this._playbackRate;\n }\n\n get volume(): number {\n return super.volume;\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n /**\n * Sets the playback rate of the audio.\n * @throws {Error} Throws an error if the sound has been cleaned up or if the source type is unsupported.\n */\n\n set playbackRate(rate: number) {\n if (rate <= 0) {\n throw new Error(\"Playback rate must be greater than 0\");\n }\n if (this._state === \"playing\") {\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n this._offset += elapsed;\n this._startTime = this.context.currentTime;\n }\n this._playbackRate = rate;\n if (!this.source) {\n return;\n }\n if (\"playbackRate\" in this.source) {\n this.source.playbackRate.value = rate;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.playbackRate = rate;\n }\n }\n\n /**\n * Handles the loop event when the audio ends.\n * This method is bound to the 'onended' event of the audio source.\n * It manages looping logic and restarts playback if necessary.\n */\n loopEnded = (): void => {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n // For media-element sources, a previous play() may still have a pending\n // settlement promise -- the deferred state transition races with the\n // seek+play we are about to perform. Defer the loop work until the\n // pending play resolves so source mutations don't interleave.\n if (\"mediaElement\" in this.source && this._mediaPlayPromise) {\n void this._mediaPlayPromise.then(() => this._runLoopEnded());\n return;\n }\n\n this._runLoopEnded();\n };\n\n private _runLoopEnded(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n this.currentLoop++;\n\n if (this.loopCount !== \"infinite\" && this.currentLoop > this.loopCount) {\n // Final iteration -- emit ended, then either fade out or stop immediately\n this.emit(\"ended\", undefined);\n if (this._fadeOutConfig) {\n // Guard the chained stop: an external stop()/cleanup() between the\n // fade resolving and this .then would otherwise run stop()/source\n // access against torn-down state. cancelFade() in stop()/cleanup()\n // resolves the fade promise synchronously, which is what makes this\n // .then fire at all after a teardown.\n void this.fadeOut(this._fadeOutConfig.duration, this._fadeOutConfig.type).then(() => {\n if (this._state !== \"stopped\" && this.source) {\n this.stop();\n this.removeFromOrigin();\n }\n });\n } else {\n this.stop();\n this.removeFromOrigin();\n }\n } else {\n this._loopEndCallback?.();\n this.seek(0); // Resets offset and handles play/pause state internally.\n // seek() calls pause() then play() when wasPlaying is true,\n // so play() handles both media-element and buffer sources correctly.\n if (this._state !== \"playing\" && this.source && !(\"mediaElement\" in this.source)) {\n // For AudioBufferSourceNode only: if seek() didn't restart playback\n // (e.g., state wasn't Playing before seek), start it now.\n this.play();\n }\n\n // Re-trigger fade-in if perLoop config is set\n if (this._fadeInConfig?.perLoop && this.gainNode) {\n this.gainNode.gain.setValueAtTime(0.0001, this.gainNode.context.currentTime);\n this.fadeTo(this._fadeInConfig.targetVolume, this._fadeInConfig.duration, this._fadeInConfig.type);\n }\n }\n }\n\n /**\n * Starts playing the audio.\n * @returns {[this]} Returns the instance of the Playback class for chaining.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n play(): [this] {\n if (!this.source) {\n throw new Error(\"Cannot play a sound that has been cleaned up\");\n }\n\n if (this._state === \"playing\") {\n return [this];\n }\n\n const isResume = this._state === \"paused\";\n\n try {\n let mediaPlayPromise: Promise<void> | undefined;\n\n if (this._state === \"paused\") {\n // If we're resuming from a paused state\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n mediaPlayPromise = this.source.mediaElement.play();\n } else {\n // For non-mediaElement sources, we need to recreate and start the source\n this.recreateSource();\n if (\"start\" in this.source && this.source.start) {\n this.source.start(0, this._offset);\n }\n }\n } else {\n // If we're starting from the beginning or a stopped state\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.currentTime = this._offset;\n mediaPlayPromise = this.source.mediaElement.play();\n } else if (\"start\" in this.source && this.source.start) {\n this.recreateSource();\n this.source.start(0, this._offset);\n }\n }\n\n if (mediaPlayPromise) {\n // For media-element sources, defer state and events until browser accepts playback\n // Stash the pending promise so loopEnded (and any future caller that\n // needs to sequence after the deferred state transition) can await it\n // rather than racing source-state mutations against it.\n const tracked = mediaPlayPromise\n .then(\n () => {\n this._startTime = this.context.currentTime;\n this.emitPlayStarted(isResume);\n },\n (error: Error) => {\n void this.emitAsync(\"error\", {\n error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: true,\n });\n },\n )\n .finally(() => {\n if (this._mediaPlayPromise === tracked) {\n this._mediaPlayPromise = undefined;\n }\n });\n this._mediaPlayPromise = tracked;\n } else {\n // For buffer sources, state transition is immediate (start() is synchronous)\n this._startTime = this.context.currentTime;\n this.emitPlayStarted(isResume);\n }\n\n return [this];\n } catch (error) {\n this.emitAsync(\"error\", {\n error: error as Error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: true,\n });\n throw error;\n }\n }\n\n pause(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n this._offset += elapsed;\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.pause();\n } else if (\"stop\" in this.source) {\n // For AudioBufferSourceNode and OscillatorNode, stop the source.\n // It cannot be restarted; a new one will be created on play().\n this.source.stop();\n }\n\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source) {\n throw new Error(\"Cannot stop a sound that has been cleaned up\");\n }\n if (this._state === \"stopped\" || this._state === \"unplayed\") {\n return;\n }\n\n this.cancelFade();\n\n if (\"stop\" in this.source && this._state === \"playing\") {\n this.source.stop();\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.pause();\n this.source.mediaElement.currentTime = 0;\n }\n\n this._offset = 0;\n this._startTime = 0;\n this.emitStopped();\n }\n\n /**\n * Fades in from silence to the current volume.\n * Optionally stores config to re-trigger the fade on each loop iteration.\n * @param {number} duration - The fade duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @param {object} options - Optional. Set perLoop: true to re-trigger fadeIn on each loop.\n * @returns {Promise<void>} Resolves when the fade completes.\n */\n fadeIn(duration: number, type?: FadeType, options?: { perLoop?: boolean }): Promise<void> {\n if (options?.perLoop) {\n this._fadeInConfig = {\n duration,\n type: type ?? \"linear\",\n perLoop: true,\n targetVolume: this.gainNode!.gain.value,\n };\n }\n return super.fadeIn(duration, type);\n }\n\n /**\n * Configures a fade-out to be applied when the playback ends naturally (last loop iteration).\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n */\n configureFadeOut(duration: number, type?: FadeType): void {\n this._fadeOutConfig = { duration, type: type ?? \"linear\" };\n }\n\n /**\n * Fades out then stops the playback.\n * @param {number} duration - The fade-out duration in milliseconds.\n * @param {FadeType} type - The fade curve type. Defaults to \"linear\".\n * @returns {Promise<void>} Resolves when the fade completes and playback is stopped.\n */\n stopWithFade(duration: number, type?: FadeType): Promise<void> {\n // Guard the chained stop: if an external stop()/cleanup() runs between\n // the fade resolving and this .then, the chained stop() would otherwise\n // throw against torn-down state (\"Cannot stop a sound that has been\n // cleaned up\"). cancelFade() inside stop()/cleanup() resolves the fade\n // promise synchronously, so this .then will still fire -- the guard\n // makes it a no-op when the playback is already stopped or cleaned up.\n return this.fadeOut(duration, type).then(() => {\n if (this._state !== \"stopped\" && this.source) {\n this.stop();\n }\n });\n }\n\n seek(time: number): void {\n if (!this.source || !this.gainNode || !this.panner) {\n throw new Error(\"Cannot seek a sound that has been cleaned up\");\n }\n if (!Number.isFinite(time) || time < 0) {\n throw new Error(\"Invalid time value for seek\");\n }\n\n if (\n this.origin.soundType === \"streaming\" &&\n \"mediaElement\" in this.source &&\n (this.source.mediaElement.seekable?.length ?? 0) === 0\n ) {\n if (this.source.mediaElement.duration === Number.POSITIVE_INFINITY) {\n throw new Error(\"Live media-element streams do not expose a seekable range\");\n }\n throw new Error(\"Media-element stream does not currently expose a seekable range\");\n }\n\n const wasPlaying = this._state === \"playing\";\n if (wasPlaying) {\n this.pause();\n }\n\n this._offset = time;\n\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.currentTime = time;\n }\n // For non-media elements, play() will handle recreating the source if needed.\n\n if (wasPlaying) {\n this.play();\n }\n }\n\n get currentTime(): number {\n if (this._state === \"playing\") {\n const elapsed = (this.context.currentTime - this._startTime) * this._playbackRate;\n return this._offset + elapsed;\n } else {\n return this._offset;\n }\n }\n\n private removeFromOrigin(): void {\n const idx = this.origin.playbacks.indexOf(this);\n if (idx !== -1) {\n this.origin.playbacks.splice(idx, 1);\n }\n }\n\n private recreateSource() {\n if (!this.buffer || !this.panner || !this.context || !this.gainNode) {\n throw new Error(\"Cannot recreate source of a sound that has been cleaned up\");\n }\n try {\n if (this.source) {\n // It's crucial to nullify onended of the old source if it's an AudioBufferSourceNode (or similar non-restartable source),\n // as its onended event could otherwise interfere with the new source created for seek/resume.\n // MediaElementAudioSourceNode is handled differently as its underlying element can be paused/played.\n if (!(\"mediaElement\" in this.source) && \"onended\" in this.source) {\n this.source.onended = null;\n }\n this.source.disconnect();\n }\n this.source = this.context.createBufferSource();\n this.source.buffer = this.buffer;\n this.setEffectChainEndpoints(this.source, this.panner);\n this.source.onended = this.loopEnded;\n this.playbackRate = this._playbackRate;\n } catch (error) {\n this.emitAsync(\"error\", {\n error: error as Error,\n errorType: \"source\",\n timestamp: Date.now(),\n recoverable: false,\n });\n throw error;\n }\n }\n\n /**\n * Sets whether the audio source should loop.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n set sourceLoop(loop: boolean) {\n if (!this.source) {\n throw new Error(\"Cannot set loop on a sound that has been cleaned up\");\n }\n if (\"loop\" in this.source) {\n this.source.loop = loop;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n this.source.mediaElement.loop = loop;\n }\n }\n\n /**\n * Cleans up resources used by the Playback instance.\n * This method should be called when the audio is no longer needed to free up resources.\n */\n\n cleanup(): void {\n if (!this.source) {\n return; // Already cleaned up\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n const mediaElement = this.source.mediaElement;\n mediaElement.pause();\n try {\n mediaElement.currentTime = 0;\n } catch {\n // Live media without a seekable range can reject currentTime writes.\n }\n mediaElement.loop = false;\n mediaElement.onended = null;\n mediaElement.src = \"\";\n mediaElement.load();\n } else if (\"onended\" in this.source) {\n this.source.onended = null;\n }\n this._offset = 0;\n this._startTime = 0;\n this.markStopped();\n this.currentLoop = 0;\n this.source.disconnect();\n this.source = undefined;\n super.cleanup();\n }\n\n private assertNotCleanedUp(): void {\n if (!this.source || !this.gainNode || !this.panner) {\n throw new Error(\"Cannot perform operation on a sound that has been cleaned up\");\n }\n }\n\n addFilter(filter: BiquadFilterNode): void {\n this.assertNotCleanedUp();\n super.addFilter(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n this.assertNotCleanedUp();\n super.removeFilter(filter);\n }\n\n /**\n * Sets or gets the loop count for the audio.\n * @param {LoopCount} loopCount - The number of times the audio should loop. 'infinite' for endless looping.\n * @returns {LoopCount} The loop count if no parameter is provided.\n * @throws {Error} Throws an error if the sound has been cleaned up or if the source type is unsupported.\n */\n\n loop(loopCount?: LoopCount): LoopCount {\n if (!this.source) {\n throw new Error(\"Cannot loop a sound that has been cleaned up\");\n }\n if (loopCount !== undefined) {\n this.loopCount = loopCount === \"infinite\" ? \"infinite\" : Math.max(0, loopCount);\n this.currentLoop = 0;\n }\n if (\"mediaElement\" in this.source && this.source.mediaElement) {\n const mediaElement = this.source.mediaElement;\n mediaElement.loop = this.loopCount === \"infinite\";\n } else if (\"loop\" in this.source) {\n this.source.loop = this.loopCount === \"infinite\";\n if (this.source.buffer) {\n this.source.loopEnd = this.source.buffer.duration;\n this.source.loopStart = 0;\n }\n } else {\n throw new Error(\"Unsupported source type\");\n }\n return this.loopCount;\n }\n\n /**\n * Sets the pitch-shift factor for this playback, resurrecting the dormant\n * phase-vocoder worklet (Jean Laroche & Mark Dolson, \"New Phase-Vocoder\n * Techniques for Pitch-Shifting, Harmonizing and Other Exotic Effects\",\n * 1999 IEEE WASPAA — peak-based pitch shift with Identity Phase-Locking).\n *\n * On first use (factor !== 1) the phase-vocoder AudioWorkletNode is built and\n * added as an ordinary entry at the tail of the pre-panner effect chain:\n * source → [filters] → phase vocoder → panner → gainNode. The factor is\n * forwarded to the node's `pitchFactor` AudioParam (1 = no shift, 2 = +1\n * octave, 0.5 = -1 octave).\n *\n * @param factor Pitch multiplier (> 0).\n * @throws {Error} if the playback has been cleaned up or factor <= 0.\n */\n async setPitchShift(factor: number): Promise<void> {\n this.assertNotCleanedUp();\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(\"Pitch-shift factor must be greater than 0\");\n }\n this._pitchFactor = factor;\n const effectChain = this._effectChain;\n if (!effectChain) {\n throw new Error(\"Cannot pitch-shift a playback before its effect chain is initialized\");\n }\n let pitchShiftNode = effectChain.nodes.find((node) => {\n const parameters = (node as AudioWorkletNode).parameters;\n return parameters && typeof parameters.get === \"function\" && parameters.get(\"pitchFactor\") !== undefined;\n }) as AudioWorkletNode | undefined;\n\n // factor === 1 is the documented \"no shift\" contract and MUST be a genuine\n // passthrough. The peak/region phase-vocoder pipeline does NOT guarantee\n // identity for peakless / edge-bin / broadband content (it zero-fills and\n // only repopulates detected peak regions), so we cannot leave the node in\n // the chain at factor 1. Remove its ordinary chain entry so the signal\n // bypasses the worklet entirely (source → [filters] → panner). A later\n // non-unity factor rebuilds a fresh node.\n if (factor === 1) {\n if (pitchShiftNode) {\n effectChain.remove(pitchShiftNode);\n }\n return;\n }\n\n if (!pitchShiftNode) {\n const cacophony = this.origin.cacophony;\n if (!cacophony) {\n throw new Error(\"Cannot pitch-shift a playback whose Sound has no Cacophony instance\");\n }\n pitchShiftNode = await cacophony.buildWorkletEffect(WORKLETS.phaseVocoder, {}, this.context);\n effectChain.add(pitchShiftNode);\n }\n\n const pitchParam = pitchShiftNode.parameters?.get(\"pitchFactor\");\n if (pitchParam) {\n pitchParam.value = factor;\n }\n }\n\n /**\n * The current pitch-shift factor (1 = no shift). See {@link setPitchShift}.\n */\n get pitchShift(): number {\n return this._pitchFactor;\n }\n\n /**\n * Gets the output node of this playback's audio graph.\n * This is the final node in the internal chain before connection to destination.\n * Use this to manually wire the playback into custom audio graphs.\n *\n * @returns {GainNode} The gain node that serves as the output of this playback.\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Manual routing through custom effects\n * const playback = sound.play()[0];\n * playback.disconnect(); // Disconnect from default destination\n * playback.connect(reverbNode).connect(context.destination);\n */\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a playback that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n /**\n * Connects this playback's output to an AudioNode or AudioParam.\n * Follows the Web Audio API connection pattern.\n *\n * @returns {AudioNode} The destination node (for chaining).\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Chain multiple effects\n * playback.connect(delay).connect(reverb).connect(context.destination);\n */\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n /**\n * Disconnects this playback's output from a specific destination or from all destinations.\n *\n * @param {AudioNode | AudioParam} [destination] - Optional specific destination to disconnect from.\n * If omitted, disconnects from all destinations.\n * @throws {Error} Throws an error if the playback has been cleaned up.\n *\n * @example\n * // Disconnect from all\n * playback.disconnect();\n *\n * @example\n * // Disconnect from specific node\n * playback.disconnect(reverbNode);\n */\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n /**\n * Creates a clone of the current Playback instance with optional overrides for certain properties.\n * This method allows for the creation of a new Playback instance that shares the same audio context\n * and source node but can have different settings such as loop count or pan type.\n * The clone is connected to the origin Sound's primary route and registered with the Sound.\n * @throws {Error} Throws an error if the sound has been cleaned up.\n */\n\n clone(overrides: Partial<PlaybackCloneOverrides> = {}): Playback {\n if (!this.source || !this.gainNode || !this.context) {\n throw new Error(\"Cannot clone a sound that has been cleaned up\");\n }\n const panType = overrides.panType ?? this.panType;\n // we'll need to create a new gain node\n const gainNode = this.context.createGain();\n gainNode.connect(this.origin._resolveRouteTargetNode());\n // clone the source node\n let source: SourceNode;\n if (\"buffer\" in this.source && this.source.buffer) {\n source = this.context.createBufferSource();\n source.buffer = this.source.buffer;\n } else if (\"mediaElement\" in this.source && this.source.mediaElement) {\n if (!this.context.createMediaElementSource) {\n throw new Error(\"Media element sources are not supported on this audio context.\");\n }\n // The Web Audio spec forbids creating a second MediaElementAudioSourceNode\n // from the same HTMLMediaElement -- the second call throws\n // InvalidStateError. Clone the underlying element first so the new\n // playback owns an independent element that hasn't been bound yet.\n const originalElement = this.source.mediaElement;\n const clonedElement = originalElement.cloneNode(true) as HTMLMediaElement;\n source = this.context.createMediaElementSource(clonedElement);\n } else {\n throw new Error(\"Unsupported source type\");\n }\n const loopCount = overrides.loopCount !== undefined ? overrides.loopCount : this.loopCount;\n const clone = new Playback(this.origin, source, gainNode);\n\n // Copy all relevant properties\n clone.loopCount = loopCount;\n clone.currentLoop = this.currentLoop;\n clone.setPanType(panType, this.context);\n clone.volume = this.volume;\n clone.playbackRate = this._playbackRate;\n clone._offset = this._offset;\n if (this._fadeInConfig) {\n clone._fadeInConfig = { ...this._fadeInConfig };\n }\n if (this._fadeOutConfig) {\n clone._fadeOutConfig = { ...this._fadeOutConfig };\n }\n if (this._state === \"paused\") {\n clone.markPaused();\n } else if (this._state === \"stopped\") {\n clone.markStopped();\n }\n\n // Deep clone filters\n this._filters.forEach((filter) => {\n const clonedFilter = this.context.createBiquadFilter();\n clonedFilter.type = filter.type;\n clonedFilter.frequency.value = filter.frequency.value;\n clonedFilter.Q.value = filter.Q.value;\n clonedFilter.gain.value = filter.gain.value;\n clone.addFilter(clonedFilter as unknown as BiquadFilterNode);\n });\n\n if (this._pitchFactor !== 1) {\n void clone.setPitchShift(this._pitchFactor).catch(() => {\n /* worklet unavailable on this context — clone proceeds unshifted */\n });\n }\n\n // If the original is playing, start the clone\n if (this._state === \"playing\") {\n clone.play();\n }\n\n this.origin.playbacks.push(clone);\n return clone;\n }\n}\n","/**\n * The Sound class represents an audio asset within a web application, providing a high-level interface\n * for loading, manipulating, and playing audio. It supports both buffer-based and media element-based audio,\n * allowing for efficient playback and manipulation of sound resources.\n *\n * A Sound instance can manage multiple Playback instances, which represent individual playbacks of the sound.\n * This allows for the same sound to be played multiple times simultaneously or with different settings (e.g., volume,\n * playback rate, spatial positioning). The Sound class provides methods to control these playbacks collectively or individually.\n *\n * Key features include:\n * - Loading audio from a URL or using a pre-loaded buffer.\n * - Playing, pausing, resuming, and stopping audio playback.\n * - Looping audio a specific number of times or infinitely.\n * - Adjusting volume, playback rate, and spatial positioning (for 3D audio).\n * - Applying audio filters for effects like reverb, equalization, etc.\n * - Cloning the Sound instance for independent manipulation and playback.\n *\n * The relationship between Sound and Playback is central to the design of the audio system. A Sound object acts as a container\n * and manager for one or more Playback objects. Each Playback object represents a single instance of the sound being played,\n * and can be controlled individually. This architecture allows for complex audio behaviors, such as playing multiple overlapping\n * instances of a sound with different settings, without requiring the user to manually manage each playback instance.\n */\n\nimport type {\n BaseSound,\n Cacophony,\n LoopCount,\n PanType,\n PlayOptions,\n SoundCleanupHoldings,\n SoundType,\n StreamCapabilities,\n} from \"./cacophony\";\nimport type { AudioBuffer, BaseContext, BiquadFilterNode, GainNode, SourceNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { SoundEvents } from \"./events\";\nimport type { PanCloneOverrides } from \"./pannerMixin\";\nimport { Playback } from \"./playback\";\nimport type { TimeStretchOptions } from \"./processors/timestretch-core\";\nimport { RoutableSource } from \"./routableSource\";\nimport type { VolumeCloneOverrides } from \"./volumeMixin\";\n\ntype SoundCloneOverrides = PanCloneOverrides &\n VolumeCloneOverrides & {\n loopCount?: LoopCount;\n playbackRate?: number;\n filters?: BiquadFilterNode[];\n };\n\nexport class Sound extends RoutableSource implements BaseSound {\n public declare playbacks: Playback[];\n buffer?: AudioBuffer;\n streamCapabilities?: StreamCapabilities;\n context: BaseContext;\n loopCount: LoopCount = 0;\n private _playbackRate: number = 1;\n /**\n * Pitch-shift factor applied to every playback of this Sound (1 = no shift).\n * Fanned out across live playbacks by {@link setPitchShift} and applied to\n * future playbacks at {@link preplay}. Drives the phase-vocoder worklet\n * (Laroche & Dolson 1999 peak-based pitch shift).\n */\n private _pitchFactor: number = 1;\n private eventEmitter: TypedEventEmitter<SoundEvents> = new TypedEventEmitter<SoundEvents>();\n private _holdings: SoundCleanupHoldings = { sources: [], gainNodes: [], mediaElements: [] };\n private _unregisterToken: object = {};\n private _preparedMediaElement?: HTMLAudioElement;\n /**\n * Per-playback unsubscribe functions for the listeners that Sound attaches\n * to each playback in {@link preplay}. Tracked so the Sound→playback\n * closure cycle (each listener captures `this`) can be broken explicitly\n * when the playback ends, stops, or is cleaned up — not only when the\n * playback's own eventEmitter is torn down. A WeakMap keeps the entry GC-\n * tied to the playback identity.\n */\n private _playbackUnsubscribes: WeakMap<Playback, Array<() => void>> = new WeakMap();\n constructor(\n public url: string,\n buffer: AudioBuffer | undefined,\n context: BaseContext,\n protected globalGainNode: GainNode,\n public soundType: SoundType = \"buffer\",\n public panType: PanType = \"HRTF\",\n private _cacophony?: Cacophony,\n preparedMediaElement?: HTMLAudioElement,\n private _preparedMediaElementCleanup?: () => void,\n ) {\n super();\n this.buffer = buffer;\n this.context = context;\n this._preparedMediaElement = preparedMediaElement;\n this._cacophony?.registerSoundForCleanup(this, this._holdings, this._unregisterToken);\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n get volume(): number {\n return super.volume;\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof SoundEvents>(event: K, listener: (data: SoundEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof SoundEvents>(event: K, listener: (data: SoundEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n protected emit<K extends keyof SoundEvents>(event: K, data: SoundEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n protected async emitAsync<K extends keyof SoundEvents>(event: K, data: SoundEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Routes a media-loader failure through the Sound's existing typed event\n * system. Used by optional media transports after the Sound has loaded.\n */\n reportLoadError(error: Error, recoverable: boolean): void {\n void this.emitAsync(\"soundError\", {\n url: this.url,\n error,\n errorType: \"load\",\n timestamp: Date.now(),\n recoverable,\n });\n }\n\n /**\n * Clones the current Sound instance, creating a deep copy with the option to override specific properties.\n * This method allows for the creation of a new, independent Sound instance based on the current one, with the\n * flexibility to modify certain attributes through the `overrides` parameter. This is particularly useful for\n * creating variations of a sound without affecting the original instance. The cloned instance includes all properties,\n * playback settings, and filters of the original, unless explicitly overridden.\n *\n * @param {SoundCloneOverrides} overrides - An object specifying properties to override in the cloned instance.\n * This can include audio settings like volume, playback rate, and spatial positioning, as well as\n * more complex configurations like 3D audio options and filter adjustments.\n */\n\n clone(overrides: Partial<SoundCloneOverrides> = {}): Sound {\n const panType = overrides.panType ?? this.panType;\n const stereoPan = overrides.stereoPan !== undefined ? overrides.stereoPan : this.stereoPan;\n const loopCount = overrides.loopCount !== undefined ? overrides.loopCount : this.loopCount;\n const playbackRate = overrides.playbackRate ?? this.playbackRate;\n const volume = overrides.volume !== undefined ? overrides.volume : this.volume;\n const position = overrides.position !== undefined ? overrides.position : this.position;\n const filters = overrides.filters?.length ? overrides.filters : this._filters;\n\n const clone = new Sound(\n this.url,\n this.buffer,\n this.context,\n this.globalGainNode,\n this.soundType,\n panType,\n this.cacophony,\n );\n clone.loop(loopCount);\n clone.playbackRate = playbackRate;\n clone.volume = volume;\n if (panType === \"HRTF\") {\n // Apply HRTF override or inherit from source.\n if (overrides.threeDOptions !== undefined) {\n clone.threeDOptions = overrides.threeDOptions;\n } else if (this.panType === \"HRTF\") {\n clone.threeDOptions = this.threeDOptions;\n }\n clone.position = position;\n } else {\n clone.stereoPan = stereoPan;\n }\n clone.addFilters(filters);\n return clone;\n }\n\n /**\n * Generates a Playback instance for the sound without starting playback.\n * This allows for pre-configuration of playback properties such as volume and position before the sound is actually played.\n */\n\n preplay(): Playback[] {\n // Capture array lengths at entry so a throw mid-construction can truncate\n // back to exactly what was here before — this preplay call's pushes get\n // rolled back without touching prior entries.\n const sourcesLen = this._holdings.sources.length;\n const gainNodesLen = this._holdings.gainNodes.length;\n const mediaElementsLen = this._holdings.mediaElements.length;\n const playbacksLen = this.playbacks.length;\n // Track audio nodes created during this call so we can disconnect them on\n // rollback (the finalization registry won't see them since holdings are\n // truncated below).\n let createdSource: SourceNode | undefined;\n let createdGainNode: GainNode | undefined;\n try {\n let source: SourceNode;\n if (this.buffer) {\n source = this.context.createBufferSource();\n source.buffer = this.buffer;\n } else {\n if (!this.context.createMediaElementSource) {\n throw new Error(\n \"Media element sources are not supported on this audio context (e.g. OfflineAudioContext). Use buffer-based sounds instead.\",\n );\n }\n const audio = this._preparedMediaElement ?? new Audio();\n audio.crossOrigin = \"anonymous\";\n audio.src = this.url;\n audio.preload = \"auto\";\n // we have the audio, let's make a buffer source node out of it\n source = this.context.createMediaElementSource(audio);\n if (audio === this._preparedMediaElement) {\n this._preparedMediaElement = undefined;\n }\n }\n createdSource = source;\n const gainNode = this.context.createGain();\n createdGainNode = gainNode;\n // Resolve the primary route target. A null `_routeTarget` means master\n // — which is structurally `globalGainNode`. A destroyed bus falls back\n // to master with a warning.\n const primaryTargetNode = this._resolveRouteTargetNode();\n gainNode.connect(primaryTargetNode);\n const playback = new Playback(this, source, gainNode);\n this._preparePlayback(playback);\n this._holdings.sources.push(source);\n this._holdings.gainNodes.push(gainNode);\n if (\"mediaElement\" in source && source.mediaElement) {\n this._holdings.mediaElements.push(source.mediaElement);\n }\n playback.setGainNode(gainNode);\n playback.volume = this.volume;\n playback.playbackRate = this.playbackRate;\n playback.loop(this.loopCount);\n // Carry the Sound's pitch-shift factor onto the new playback. Building the\n // phase-vocoder worklet node is async; preplay is sync, so this is a\n // fire-and-forget that splices the node in once the worklet resolves\n // (no-op when no shift is set).\n if (this._pitchFactor !== 1) {\n void playback.setPitchShift(this._pitchFactor).catch(() => {\n /* worklet unavailable on this context — playback proceeds unshifted */\n });\n }\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else if (this.panType === \"stereo\") {\n playback.stereoPan = this.stereoPan;\n }\n // Set up event propagation from playback to sound. Each listener captures\n // `this` and is registered on the playback's own emitter — so the\n // playback holds a reference back to the Sound for the lifetime of the\n // listener. Capture the unsubscribe functions so we can break the cycle\n // explicitly when the playback ends (naturally) or is cleaned up.\n const unsubEnded = playback.on(\"ended\", () => {\n this.emit(\"ended\", undefined);\n // Natural end: the playback has fired its terminal event; tear down\n // our subscriptions so it can be GC'd without waiting for cleanup().\n this._unsubscribeFromPlayback(playback);\n });\n playback._loopEndCallback = () => {\n this.emit(\"loopEnd\", undefined);\n };\n const unsubError = playback.on(\"error\", (errorEvent) => {\n this.emitAsync(\"soundError\", {\n url: this.url,\n error: errorEvent.error,\n errorType: \"playback\",\n timestamp: errorEvent.timestamp,\n recoverable: errorEvent.recoverable,\n });\n });\n // Clear-the-callback step counts as an \"unsubscribe\" for the closure\n // _loopEndCallback holds (it captures `this`). Combine all three.\n const clearLoopCallback = () => {\n if (playback._loopEndCallback) {\n playback._loopEndCallback = undefined;\n }\n };\n this._playbackUnsubscribes.set(playback, [unsubEnded, unsubError, clearLoopCallback]);\n\n this.playbacks.push(playback);\n return [playback];\n } catch (error) {\n // Roll back any state pushed during THIS call so a failed preplay does\n // not leave zombie holdings/playbacks behind. Truncate to entry lengths\n // first (safe even if we never reached the pushes), then disconnect the\n // audio nodes we actually created.\n this._holdings.sources.length = sourcesLen;\n this._holdings.gainNodes.length = gainNodesLen;\n this._holdings.mediaElements.length = mediaElementsLen;\n this.playbacks.length = playbacksLen;\n if (createdGainNode) {\n try {\n createdGainNode.disconnect();\n } catch {\n // Disconnect can throw if the node was never connected; the rollback\n // intent is satisfied regardless.\n }\n }\n if (createdSource) {\n try {\n createdSource.disconnect();\n } catch {\n // Same — best-effort cleanup on a failed-construction path.\n }\n }\n const errorEvent = {\n url: this.url,\n error: error as Error,\n errorType: \"playback\" as const,\n timestamp: Date.now(),\n recoverable: true,\n };\n this.emitAsync(\"soundError\", errorEvent);\n throw error;\n }\n }\n\n /**\n * Detaches the Sound-side listeners that were registered on a playback in\n * {@link preplay}. Idempotent — safe to call on a playback whose\n * subscriptions were already torn down. Breaks the Sound↔playback closure\n * cycle described on the entries in {@link _playbackUnsubscribes}.\n */\n private _unsubscribeFromPlayback(playback: Playback): void {\n const unsubs = this._playbackUnsubscribes.get(playback);\n if (!unsubs) {\n return;\n }\n this._playbackUnsubscribes.delete(playback);\n for (const unsub of unsubs) {\n unsub();\n }\n }\n\n play(options?: PlayOptions): ReturnType<this[\"preplay\"]> {\n const playbacks = this.preplay() as ReturnType<this[\"preplay\"]>;\n\n for (const playback of playbacks) {\n const emitSoundPlay = (acceptedPlayback: SoundEvents[\"play\"]) => {\n cleanupPlayListeners();\n this.emit(\"play\", acceptedPlayback);\n };\n const cleanupPlayListeners = () => {\n playback.off(\"play\", emitSoundPlay);\n playback.off(\"error\", cleanupPlayListeners);\n };\n\n playback.on(\"play\", emitSoundPlay);\n playback.on(\"error\", cleanupPlayListeners);\n\n try {\n playback.play();\n } catch (error) {\n cleanupPlayListeners();\n // preplay() already committed `playback` to `this.playbacks` and the\n // Sound→playback listener subscriptions. Remove both so the failed\n // playback is not re-touched by subsequent resume()/loop() iterations\n // and so the closure cycle is broken on the throw path too.\n const idx = this.playbacks.indexOf(playback);\n if (idx !== -1) {\n this.playbacks.splice(idx, 1);\n }\n this._unsubscribeFromPlayback(playback);\n throw error;\n }\n }\n\n if (options) {\n for (const playback of playbacks) {\n if (options.fadeIn !== undefined) {\n playback.fadeIn(options.fadeIn, options.fadeType, { perLoop: options.fadeInPerLoop });\n }\n if (options.fadeOut !== undefined) {\n playback.configureFadeOut(options.fadeOut, options.fadeType);\n }\n }\n }\n\n return playbacks;\n }\n\n stop(): void {\n this._holdings.sources.length = 0;\n this._holdings.gainNodes.length = 0;\n this._holdings.mediaElements.length = 0;\n super.stop();\n this.emit(\"stop\", undefined);\n }\n\n pause(): void {\n super.pause();\n this.emit(\"pause\", undefined);\n }\n\n resume(): void {\n this.playbacks.filter((playback) => playback.isPaused).forEach((playback) => playback.play());\n this.emit(\"resume\", undefined);\n }\n\n /**\n * Seeks to a specific time within the sound's playback.\n * @param { number } time - The time in seconds to seek to.\n * This method iterates through all active `Playback` instances and calls their `seek()` method with the specified time.\n */\n\n seek(time: number): void {\n this.playbacks.forEach((playback) => playback.seek(time));\n }\n\n /**\n * Retrieves the duration of the sound in seconds.\n * If the sound is based on an AudioBuffer, it returns the duration of the buffer.\n * Otherwise, if the sound has not been played and is a MediaElementSource, it returns NaN, indicating that the duration is unknown or not applicable.\n * @returns { number } The duration of the sound in seconds.\n */\n\n get duration() {\n if (this.playbacks.length > 0) {\n return this.playbacks[0].duration;\n }\n return this.buffer?.duration || NaN;\n }\n\n /**\n * OFFLINE independent time-stretch: produce a NEW buffer-based Sound whose\n * tempo is changed by `factor` WITHOUT changing pitch (Phase Gradient Heap\n * Integration, Průša & Holighaus, \"Phase Vocoder Done Right\", EUSIPCO 2017 /\n * arXiv:2202.07382). `factor > 1` is slower/longer, `factor < 1` is\n * faster/shorter. Only valid for buffer-based sounds (a loaded AudioBuffer);\n * streaming / media-element sounds have no decoded buffer to transform.\n *\n * The original Sound is left untouched; a fresh Sound wrapping the stretched\n * buffer is returned. Delegates to {@link Cacophony.timeStretchBuffer}.\n *\n * @param factor Stretch factor (`> 0`); pitch is preserved.\n * @param options Optional PGHI parameters (fftSize, analysisHop, tol, seed).\n * @returns A new buffer-based Sound at the stretched tempo.\n * @throws If this Sound has no AudioBuffer (e.g. streaming/HTML5 audio).\n */\n timeStretch(factor: number, options?: TimeStretchOptions): Sound {\n if (!this.buffer) {\n throw new Error(\"Sound.timeStretch requires a buffer-based sound (a loaded AudioBuffer).\");\n }\n if (!this._cacophony) {\n throw new Error(\"Sound.timeStretch requires the owning Cacophony instance.\");\n }\n const stretched = this._cacophony.timeStretchBuffer(this.buffer, factor, options);\n return new Sound(this.url, stretched, this.context, this.globalGainNode, \"buffer\", this.panType, this._cacophony);\n }\n\n /**\n * Sets or retrieves the loop behavior for the sound.\n * If loopCount is provided, the sound will loop the specified number of times.\n * If loopCount is 'infinite', the sound will loop indefinitely until stopped.\n * If no argument is provided, the method returns the current loop count setting.\n * @param { LoopCount } [loopCount] - The number of times to loop or 'infinite' for indefinite looping.\n * @returns { LoopCount } The current loop count setting if no argument is provided.\n */\n\n loop(loopCount?: LoopCount): LoopCount {\n if (loopCount === undefined) {\n return this.loopCount;\n }\n this.loopCount = loopCount;\n this.playbacks.forEach((p) => p.loop(loopCount));\n return this.loopCount;\n }\n\n get playbackRate(): number {\n return this._playbackRate;\n }\n\n set playbackRate(rate: number) {\n this._playbackRate = rate;\n this.playbacks.forEach((p) => (p.playbackRate = rate));\n this.emit(\"rateChange\", rate);\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n /**\n * Current pitch-shift factor (1 = no shift). See {@link setPitchShift}.\n */\n get pitchShift(): number {\n return this._pitchFactor;\n }\n\n /**\n * Pitch-shifts every live playback of this Sound, resurrecting the dormant\n * phase-vocoder worklet (Jean Laroche & Mark Dolson, 1999 IEEE WASPAA —\n * peak-based pitch shift with Identity Phase-Locking). Mirrors how\n * {@link playbackRate} fans a per-playback control out across `this.playbacks`.\n * The factor is also stored so future playbacks pick it up at preplay.\n *\n * @param factor Pitch multiplier (> 0; 2 = +1 octave, 0.5 = -1 octave).\n * @returns Resolves once every live playback's worklet node has been built\n * and its `pitchFactor` param updated.\n */\n async setPitchShift(factor: number): Promise<void> {\n // Validate UP FRONT, before storing. With no live playbacks `Promise.all([])`\n // resolves immediately, so a missing guard here would let setPitchShift(0)\n // / NaN / negative \"succeed\" and leave an invalid pitchShift on the Sound\n // that future preplay() would silently swallow. Mirrors the per-playback\n // guard in Playback.setPitchShift.\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(\"Pitch-shift factor must be greater than 0\");\n }\n this._pitchFactor = factor;\n await Promise.all(this.playbacks.map((p) => p.setPitchShift(factor)));\n }\n\n cleanup(): void {\n this._cacophony?.unregisterSoundCleanup(this._unregisterToken);\n this._preparedMediaElementCleanup?.();\n this._preparedMediaElementCleanup = undefined;\n if (this._preparedMediaElement) {\n this._preparedMediaElement.pause();\n this._preparedMediaElement.src = \"\";\n this._preparedMediaElement.load();\n this._preparedMediaElement = undefined;\n }\n this._holdings.sources.length = 0;\n this._holdings.gainNodes.length = 0;\n this._holdings.mediaElements.length = 0;\n // Explicitly tear down the Sound→playback listener subscriptions before\n // calling p.cleanup() — p.cleanup()'s removeAllListeners() also clears\n // them, but doing it here keeps the closure cycle's break point local to\n // Sound and ensures the WeakMap entries are released promptly.\n this.playbacks.forEach((p) => {\n this._unsubscribeFromPlayback(p);\n p.cleanup();\n });\n this.playbacks = [];\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n","import { BasePlayback } from \"./basePlayback\";\nimport type { OscillatorNode } from \"./context\";\n\nexport type OscillatorCloneOverrides = {\n oscillatorOptions?: Partial<OscillatorOptions>;\n};\n\nexport abstract class OscillatorMixin extends BasePlayback {\n _oscillatorOptions: Partial<OscillatorOptions> = {};\n public declare source?: OscillatorNode;\n\n get oscillatorOptions(): Partial<OscillatorOptions> {\n return this._oscillatorOptions;\n }\n\n set oscillatorOptions(options: Partial<OscillatorOptions>) {\n this._oscillatorOptions = options;\n if (this.source) {\n if (this.oscillatorOptions.detune !== undefined) this.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined)\n this.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) this.source.type = this.oscillatorOptions.type;\n }\n }\n\n get frequency(): number {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.frequency.value;\n }\n\n set frequency(frequency: number) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.frequency.value = frequency;\n this.oscillatorOptions.frequency = frequency;\n }\n\n get detune(): number {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.detune.value;\n }\n\n set detune(detune: number) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.detune.value = detune;\n this.oscillatorOptions.detune = detune;\n }\n\n get type(): OscillatorType {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n return this.source.type;\n }\n\n set type(type: OscillatorType) {\n if (!this.source) {\n throw new Error(\"No source node found\");\n }\n this.source.type = type;\n this.oscillatorOptions.type = type;\n }\n}\n","import type { BaseSound } from \"./cacophony\";\nimport type { AudioNode, AudioParam, BaseContext, GainNode, OscillatorNode } from \"./context\";\nimport { OscillatorMixin } from \"./oscillatorMixin\";\nimport type { Synth } from \"./synth\";\n\nexport class SynthPlayback extends OscillatorMixin implements BaseSound {\n context: BaseContext;\n /**\n * Live oscillator node. Set in the constructor; cleared to `undefined` in\n * {@link cleanup} (matches Playback's `source?` shape so post-cleanup\n * checks of `!this.source` work uniformly).\n */\n public declare source?: OscillatorNode;\n constructor(\n public origin: Synth,\n source: OscillatorNode,\n gainNode: GainNode,\n ) {\n super(origin);\n this.context = origin.context;\n this.source = source;\n this.setPanType(origin.panType, origin.context);\n // setPanType synchronously assigns this.panner; capture locally so TS\n // narrows to non-undefined for the connect calls below.\n const panner = this.panner;\n if (!panner) {\n throw new Error(\"setPanType did not produce a panner node\");\n }\n this.setGainNode(gainNode);\n this.setEffectChainEndpoints(source, panner);\n panner.connect(gainNode);\n this.oscillatorOptions = {\n detune: source.detune.value,\n frequency: source.frequency.value,\n type: source.type,\n };\n }\n\n play(): [this] {\n if (!this.source || !this.panner) {\n throw new Error(\"Cannot play a synth that has been cleaned up\");\n }\n\n if (this._state === \"playing\") {\n return [this];\n }\n\n const isResume = this._state === \"paused\";\n if (isResume || this._state === \"stopped\") {\n this.recreateSource();\n }\n\n if (this.oscillatorOptions.detune !== undefined) this.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) this.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) this.source.type = this.oscillatorOptions.type;\n\n this.source.start();\n this.emitPlayStarted(isResume);\n return [this];\n }\n\n pause(): void {\n if (!this.source || this._state !== \"playing\") {\n return;\n }\n\n this.source.stop();\n this.emitPaused();\n }\n\n stop(): void {\n if (!this.source || this._state === \"unplayed\" || this._state === \"stopped\") {\n return;\n }\n\n if (this._state === \"playing\") {\n this.source.stop();\n }\n\n this.emitStopped();\n }\n\n cleanup(): void {\n if (!this.source) {\n // Already cleaned up — same idempotency guard as Playback.cleanup.\n return;\n }\n if (this.panner && this.gainNode) {\n try {\n this.panner.disconnect();\n } catch {}\n }\n this.source = undefined;\n this.panner = undefined;\n this.markStopped();\n this.eventEmitter.removeAllListeners();\n // super.cleanup() (VolumeMixin) disconnects + clears `gainNode`; after\n // this call `outputNode` throws (parity with Playback.cleanup).\n super.cleanup();\n }\n\n /**\n * Gets the output node of this synth playback's audio graph — the final\n * GainNode before connection to the destination. Use this to manually\n * wire the playback into custom audio graphs.\n *\n * @throws if the playback has been cleaned up.\n */\n get outputNode(): GainNode {\n if (!this.gainNode) {\n throw new Error(\"Cannot access output node of a synth playback that has been cleaned up\");\n }\n return this.gainNode;\n }\n\n /**\n * Connects this synth playback's output to an AudioNode or AudioParam. Mirrors\n * Playback.connect.\n *\n * @returns The destination node (for chaining).\n * @throws if the playback has been cleaned up.\n */\n connect(destination: AudioNode | AudioParam): AudioNode {\n return this.outputNode.connect(destination as any);\n }\n\n /**\n * Disconnects this synth playback's output from a specific destination\n * or from all destinations. Mirrors Playback.disconnect.\n *\n * @throws if the playback has been cleaned up.\n */\n disconnect(destination?: AudioNode | AudioParam): void {\n if (destination) {\n this.outputNode.disconnect(destination as any);\n } else {\n this.outputNode.disconnect();\n }\n }\n\n private recreateSource(): void {\n if (!this.panner || !this.source) {\n throw new Error(\"Cannot recreate source of a synth that has been cleaned up\");\n }\n\n this.source.disconnect();\n this.source = this.context.createOscillator();\n this.setEffectChainEndpoints(this.source, this.panner);\n }\n}\n","import type { BaseSound, Cacophony, PanType, Position, SoundType } from \"./cacophony\";\nimport type { BaseContext, GainNode, OscillatorNode } from \"./context\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { SynthEvents } from \"./events\";\nimport type { FilterCloneOverrides } from \"./filters\";\nimport type { OscillatorCloneOverrides } from \"./oscillatorMixin\";\nimport type { PanCloneOverrides } from \"./pannerMixin\";\nimport { RoutableSource } from \"./routableSource\";\nimport { SynthPlayback } from \"./synthPlayback\";\nimport type { VolumeCloneOverrides } from \"./volumeMixin\";\n\ntype SynthCloneOverrides = FilterCloneOverrides & OscillatorCloneOverrides & PanCloneOverrides & VolumeCloneOverrides;\n\nexport class Synth extends RoutableSource implements BaseSound {\n _oscillatorOptions: Partial<OscillatorOptions>;\n playbacks: SynthPlayback[] = [];\n private eventEmitter: TypedEventEmitter<SynthEvents> = new TypedEventEmitter<SynthEvents>();\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof SynthEvents>(event: K, listener: (data: SynthEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof SynthEvents>(event: K, listener: (data: SynthEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n protected emit<K extends keyof SynthEvents>(event: K, data: SynthEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n protected async emitAsync<K extends keyof SynthEvents>(event: K, data: SynthEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n constructor(\n public context: BaseContext,\n protected globalGainNode: GainNode,\n public soundType: SoundType = \"oscillator\",\n public panType: PanType = \"HRTF\",\n oscillatorOptions: Partial<OscillatorOptions> = {},\n private _cacophony?: Cacophony,\n ) {\n super();\n this.context = context;\n this._oscillatorOptions = oscillatorOptions;\n }\n\n get cacophony(): Cacophony | undefined {\n return this._cacophony;\n }\n\n /**\n * Clones the current Synth instance, creating a deep copy with the option to override specific properties.\n * This method allows for the creation of a new, independent Synth instance based on the current one, with the\n * flexibility to modify certain attributes through the `overrides` parameter. This is particularly useful for\n * creating variations of a synth without affecting the original instance. The cloned instance includes all properties,\n * playback settings, and filters of the original, unless explicitly overridden.\n *\n * @param {SynthCloneOverrides} overrides - An object specifying properties to override in the cloned instance.\n * This can include audio settings like volume, playback rate, and spatial positioning, as well as\n * more complex configurations like 3D audio options and filter adjustments.\n */\n clone(overrides: Partial<SynthCloneOverrides> = {}): Synth {\n const panType = overrides.panType ?? this.panType;\n const stereoPan = overrides.stereoPan !== undefined ? overrides.stereoPan : this.stereoPan;\n const volume = overrides.volume !== undefined ? overrides.volume : this.volume;\n const position = overrides.position !== undefined ? overrides.position : this.position;\n const filters = overrides.filters !== undefined ? overrides.filters : this._filters;\n const oscillatorOptions = { ...(overrides.oscillatorOptions ?? this._oscillatorOptions) };\n\n const clone = new Synth(\n this.context,\n this.globalGainNode,\n this.soundType,\n panType,\n oscillatorOptions,\n this._cacophony,\n );\n clone._volume = volume;\n clone._position = [...position] as Position;\n clone._stereoPan = stereoPan;\n // Apply HRTF override (if provided) through the canonical setter so the\n // discriminated `_threeDOptions` invariant is preserved. Without an override\n // the new clone keeps its default HRTF storage initialized in the container.\n if (overrides.threeDOptions !== undefined) {\n clone.threeDOptions = overrides.threeDOptions;\n } else if (this.panType === \"HRTF\") {\n clone.threeDOptions = this.threeDOptions;\n }\n if (panType === \"HRTF\") {\n clone.position = [...position] as Position;\n }\n clone.addFilters(filters);\n return clone;\n }\n\n /**\n * Generates a Playback instance for the synth without starting playback.\n * This allows for pre-configuration of playback properties such as volume and position before the synth is actually played.\n */\n preplay(): SynthPlayback[] {\n const oscillator = this.context.createOscillator();\n const playbacks = this.createPlayback(oscillator);\n return playbacks;\n }\n\n private createPlayback(oscillator: OscillatorNode): SynthPlayback[] {\n if (this.oscillatorOptions.detune !== undefined) oscillator.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) oscillator.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) oscillator.type = this.oscillatorOptions.type;\n\n const gainNode = this.context.createGain();\n const primaryTargetNode = this._resolveRouteTargetNode();\n gainNode.connect(primaryTargetNode);\n const playback = new SynthPlayback(this, oscillator, gainNode);\n this._preparePlayback(playback);\n playback.volume = this.volume;\n if (this.panType === \"HRTF\") {\n playback.threeDOptions = this.threeDOptions;\n playback.position = this.position;\n } else if (this.panType === \"stereo\") {\n playback.stereoPan = this.stereoPan;\n }\n this.playbacks.push(playback);\n return [playback];\n }\n\n play(): ReturnType<this[\"preplay\"]> {\n const playbacks = super.play() as ReturnType<this[\"preplay\"]>;\n this.emit(\"play\", playbacks[0]);\n return playbacks;\n }\n\n stop(): void {\n super.stop();\n this.emit(\"stop\", undefined);\n }\n\n pause(): void {\n super.pause();\n this.emit(\"pause\", undefined);\n }\n\n resume(): void {\n this.playbacks.filter((playback) => playback.isPaused).forEach((playback) => playback.play());\n this.emit(\"resume\", undefined);\n }\n\n get volume(): number {\n return super.volume;\n }\n\n set volume(volume: number) {\n super.volume = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n get oscillatorOptions(): Partial<OscillatorOptions> {\n return this._oscillatorOptions;\n }\n\n set oscillatorOptions(options: Partial<OscillatorOptions>) {\n this._oscillatorOptions = options;\n this.playbacks.forEach((p) => {\n if (p.source) {\n if (this.oscillatorOptions.detune !== undefined) p.source.detune.value = this.oscillatorOptions.detune;\n if (this.oscillatorOptions.frequency !== undefined) p.source.frequency.value = this.oscillatorOptions.frequency;\n if (this.oscillatorOptions.type) p.source.type = this.oscillatorOptions.type;\n }\n });\n }\n\n get frequency(): number {\n return (this.oscillatorOptions.frequency as number) ?? 440;\n }\n\n set frequency(frequency: number) {\n this._oscillatorOptions.frequency = frequency;\n this.playbacks.forEach((p) => (p.frequency = frequency));\n this.emit(\"frequencyChange\", frequency);\n }\n\n get detune(): number {\n return this.oscillatorOptions.detune ?? 0;\n }\n\n set detune(detune: number) {\n this._oscillatorOptions.detune = detune;\n this.playbacks.forEach((p) => (p.detune = detune));\n this.emit(\"detuneChange\", detune);\n }\n\n get type(): OscillatorType {\n return (this.oscillatorOptions.type as OscillatorType) ?? \"sine\";\n }\n\n set type(type: OscillatorType) {\n this._oscillatorOptions.type = type;\n this.playbacks.forEach((p) => (p.type = type));\n this.emit(\"typeChange\", type);\n }\n}\n","import { ADTS, AudioSampleSink, FLAC, Input, type InputAudioTrack, MP3, MP4, OGG, UrlSource, WAVE } from \"mediabunny\";\nimport type { StreamCapabilities } from \"./cacophony\";\nimport type { PcmStreamPullSource, PcmStreamSound } from \"./pcmStream\";\n\ninterface DecoderSession {\n channelCount: number;\n duration: number;\n input: Input;\n live: boolean;\n sampleRate: number;\n seekable: boolean;\n sink: AudioSampleSink;\n}\n\ninterface OpenSessionResult {\n session: DecoderSession | null;\n unsupported: boolean;\n}\n\nexport type WebCodecsStreamSound = PcmStreamSound & {\n readonly soundType: \"streaming\";\n readonly streamCapabilities: StreamCapabilities;\n};\n\n/**\n * Pulls encoded URL media through Mediabunny's demuxers and WebCodecs-backed\n * audio sink, then writes interleaved float PCM into the existing ring buffer.\n */\nexport class WebCodecsPullAdapter implements PcmStreamPullSource {\n private currentRun?: AbortController;\n private disposed = false;\n private pendingInput?: Input;\n private session?: DecoderSession;\n private sound?: PcmStreamSound;\n\n private readonly handleAbort = (): void => {\n this.cleanup();\n };\n\n private constructor(\n private readonly url: string,\n private readonly targetSampleRate: number,\n private readonly signal?: AbortSignal,\n ) {\n this.signal?.addEventListener(\"abort\", this.handleAbort, { once: true });\n }\n\n static async open(url: string, targetSampleRate: number, signal?: AbortSignal): Promise<WebCodecsPullAdapter | null> {\n signal?.throwIfAborted();\n const adapter = new WebCodecsPullAdapter(url, targetSampleRate, signal);\n try {\n const result = await adapter.openSession();\n if (result.unsupported || !result.session) {\n adapter.cleanup();\n return null;\n }\n adapter.session = result.session;\n return adapter;\n } catch (error) {\n adapter.cleanup();\n signal?.throwIfAborted();\n if ((error as { name?: string } | null)?.name === \"UnsupportedInputFormatError\") {\n return null;\n }\n throw error;\n }\n }\n\n get channelCount(): number {\n if (!this.session) {\n throw new Error(\"WebCodecs stream adapter is not initialized\");\n }\n return this.session.channelCount;\n }\n\n get capabilities(): StreamCapabilities {\n if (!this.session) {\n throw new Error(\"WebCodecs stream adapter is not initialized\");\n }\n return {\n duration: this.session.duration,\n live: this.session.live,\n seekable: this.session.seekable,\n transport: \"webcodecs\",\n };\n }\n\n attach(sound: PcmStreamSound): WebCodecsStreamSound {\n if (this.disposed || !this.session) {\n throw new Error(\"Cannot attach a disposed WebCodecs stream adapter\");\n }\n this.sound = sound;\n sound.attachPullSource(this, this.capabilities);\n this.startPump(this.session, 0);\n return sound as WebCodecsStreamSound;\n }\n\n seek(time: number): void {\n if (!Number.isFinite(time) || time < 0) {\n throw new RangeError(\"Stream seek time must be a finite non-negative number\");\n }\n if (this.disposed) {\n throw new Error(\"Cannot seek a WebCodecs stream that has been cleaned up\");\n }\n if (!this.sound) {\n throw new Error(\"Cannot seek a WebCodecs stream before it has been attached\");\n }\n if (!this.session) {\n throw new Error(\"Cannot seek while another WebCodecs seek is pending\");\n }\n if (this.session.live) {\n throw new Error(\"Live streams do not support seeking\");\n }\n if (!this.session.seekable) {\n throw new Error(\"This stream's server does not support range seeking\");\n }\n\n const wasPlaying = this.sound.isPlaying;\n this.sound.stop();\n if (wasPlaying) {\n this.sound.play();\n }\n this.currentRun?.abort();\n this.session.input.dispose();\n this.session = undefined;\n void this.reopenAndPump(time);\n }\n\n cleanup(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.signal?.removeEventListener(\"abort\", this.handleAbort);\n this.currentRun?.abort();\n this.pendingInput?.dispose();\n this.pendingInput = undefined;\n this.session?.input.dispose();\n this.session = undefined;\n }\n\n private async reopenAndPump(time: number): Promise<void> {\n try {\n const result = await this.openSession();\n if (this.disposed) {\n result.session?.input.dispose();\n return;\n }\n if (!result.session) {\n return;\n }\n const soundChannelCount = this.channelCountForSound();\n if (soundChannelCount !== undefined && result.session.channelCount !== soundChannelCount) {\n result.session.input.dispose();\n throw new Error(\"The audio channel count changed while seeking\");\n }\n this.session = result.session;\n this.startPump(result.session, time);\n } catch (error) {\n if (!this.disposed && !this.signal?.aborted) {\n this.sound?.handlePullError(error);\n }\n }\n }\n\n private channelCountForSound(): number | undefined {\n return this.sound?.inputChannelCount;\n }\n\n private startPump(session: DecoderSession, startTime: number): void {\n this.currentRun?.abort();\n const run = new AbortController();\n this.currentRun = run;\n void this.pump(session, startTime, run.signal).catch((error: unknown) => {\n if (!run.signal.aborted && !this.disposed && !this.signal?.aborted) {\n this.sound?.handlePullError(error);\n }\n });\n }\n\n private async pump(session: DecoderSession, startTime: number, runSignal: AbortSignal): Promise<void> {\n const sound = this.sound;\n if (!sound) {\n return;\n }\n const resampler = new StreamingPcmResampler(session.sampleRate, this.targetSampleRate, session.channelCount);\n\n for await (const sample of session.sink.samples(startTime)) {\n if (runSignal.aborted || this.disposed) {\n sample.close();\n return;\n }\n try {\n const options = { format: \"f32\" as const, planeIndex: 0 };\n const data = new ArrayBuffer(sample.allocationSize(options));\n sample.copyTo(data, options);\n const output = resampler.add(new Float32Array(data));\n await this.enqueue(sound, output, session.channelCount, runSignal);\n } finally {\n sample.close();\n }\n }\n\n const tail = resampler.finalize();\n await this.enqueue(sound, tail, session.channelCount, runSignal);\n if (!runSignal.aborted && !this.disposed) {\n sound.end();\n }\n }\n\n private async enqueue(\n sound: PcmStreamSound,\n samples: Float32Array,\n channelCount: number,\n signal: AbortSignal,\n ): Promise<void> {\n const maximumFrames = Math.max(1, Math.floor(this.targetSampleRate / 4));\n const maximumSamples = maximumFrames * channelCount;\n for (let offset = 0; offset < samples.length; offset += maximumSamples) {\n const chunk = samples.slice(offset, Math.min(samples.length, offset + maximumSamples));\n let written = false;\n while (!written) {\n signal.throwIfAborted();\n const before = sound.bufferedDuration;\n const hasCapacity = sound.write(chunk);\n written = sound.bufferedDuration > before;\n if (!hasCapacity) {\n await waitForDrain(sound, signal);\n }\n }\n }\n }\n\n private async openSession(): Promise<OpenSessionResult> {\n this.signal?.throwIfAborted();\n let sawRangeResponse = false;\n const source = new UrlSource(this.url, {\n fetchFn: async (input, init) => {\n const response = await globalThis.fetch(input, init);\n if (response.status === 206 || response.headers.get(\"Accept-Ranges\")?.toLowerCase() === \"bytes\") {\n sawRangeResponse = true;\n }\n return response;\n },\n });\n const input = new Input({ formats: [MP3, ADTS, MP4, OGG, FLAC, WAVE], source });\n this.pendingInput = input;\n try {\n const track = await input.getPrimaryAudioTrack();\n if (!track || !(await track.canDecode())) {\n this.pendingInput = undefined;\n input.dispose();\n return { session: null, unsupported: true };\n }\n const session = await this.describeSession(input, track, sawRangeResponse);\n this.signal?.throwIfAborted();\n if (this.disposed) {\n this.pendingInput = undefined;\n input.dispose();\n return { session: null, unsupported: false };\n }\n this.pendingInput = undefined;\n return { session, unsupported: false };\n } catch (error) {\n if (this.pendingInput === input) {\n this.pendingInput = undefined;\n input.dispose();\n }\n throw error;\n }\n }\n\n private async describeSession(\n input: Input,\n track: InputAudioTrack,\n sawRangeResponse: boolean,\n ): Promise<DecoderSession> {\n const [channelCount, sampleRate, live, metadataDuration] = await Promise.all([\n track.getNumberOfChannels(),\n track.getSampleRate(),\n track.isLive(),\n track.getDurationFromMetadata({ skipLiveWait: true }),\n ]);\n return {\n channelCount,\n duration: live || metadataDuration === null ? Number.POSITIVE_INFINITY : metadataDuration,\n input,\n live,\n sampleRate,\n seekable: !live && sawRangeResponse,\n sink: new AudioSampleSink(track),\n };\n }\n}\n\nfunction waitForDrain(sound: PcmStreamSound, signal: AbortSignal): Promise<void> {\n signal.throwIfAborted();\n return new Promise((resolve, reject) => {\n const unsubscribe = sound.on(\"drain\", () => {\n cleanup();\n resolve();\n });\n const handleAbort = () => {\n cleanup();\n reject(signal.reason);\n };\n const cleanup = () => {\n unsubscribe();\n signal.removeEventListener(\"abort\", handleAbort);\n };\n signal.addEventListener(\"abort\", handleAbort, { once: true });\n });\n}\n\n/**\n * Stateful linear-interpolation resampler for decoded stream chunks.\n *\n * This v1 implementation has no anti-aliasing filter. Downsampling can fold\n * source frequencies above the target Nyquist frequency into the audible band;\n * use a band-limited resampler if that quality trade-off is unacceptable.\n */\nclass StreamingPcmResampler {\n private inputFrames = 0;\n private nextSourcePosition = 0;\n private previousFrame?: Float32Array;\n\n constructor(\n private readonly sourceRate: number,\n private readonly targetRate: number,\n private readonly channelCount: number,\n ) {}\n\n add(input: Float32Array): Float32Array {\n if (this.sourceRate === this.targetRate) {\n this.inputFrames += input.length / this.channelCount;\n return input;\n }\n const frameCount = input.length / this.channelCount;\n const firstFrame = this.inputFrames;\n const lastFrame = firstFrame + frameCount - 1;\n const sourceFramesPerOutput = this.sourceRate / this.targetRate;\n const outputFrameCount =\n this.nextSourcePosition > lastFrame\n ? 0\n : Math.floor((lastFrame - this.nextSourcePosition) / sourceFramesPerOutput) + 1;\n const output = new Float32Array(outputFrameCount * this.channelCount);\n let outputOffset = 0;\n\n for (let outputFrame = 0; outputFrame < outputFrameCount; outputFrame += 1) {\n const leftFrame = Math.floor(this.nextSourcePosition);\n const fraction = this.nextSourcePosition - leftFrame;\n for (let channel = 0; channel < this.channelCount; channel += 1) {\n const left = this.sampleAt(input, firstFrame, leftFrame, channel);\n const right = this.sampleAt(input, firstFrame, Math.min(leftFrame + 1, lastFrame), channel);\n output[outputOffset] = left + (right - left) * fraction;\n outputOffset += 1;\n }\n this.nextSourcePosition += sourceFramesPerOutput;\n }\n\n this.inputFrames += frameCount;\n this.previousFrame = input.slice(input.length - this.channelCount);\n return output;\n }\n\n finalize(): Float32Array {\n if (this.sourceRate === this.targetRate || !this.previousFrame) {\n return new Float32Array();\n }\n const sourceFramesPerOutput = this.sourceRate / this.targetRate;\n const remainingSourceFrames = this.inputFrames - this.nextSourcePosition;\n const outputFrameCount = remainingSourceFrames > 0 ? Math.ceil(remainingSourceFrames / sourceFramesPerOutput) : 0;\n const output = new Float32Array(outputFrameCount * this.channelCount);\n for (let outputFrame = 0; outputFrame < outputFrameCount; outputFrame += 1) {\n output.set(this.previousFrame, outputFrame * this.channelCount);\n this.nextSourcePosition += sourceFramesPerOutput;\n }\n return output;\n }\n\n private sampleAt(input: Float32Array, firstFrame: number, frame: number, channel: number): number {\n if (frame < firstFrame) {\n return this.previousFrame?.[channel] ?? input[channel] ?? 0;\n }\n return input[(frame - firstFrame) * this.channelCount + channel] ?? 0;\n }\n}\n","import foaHrirUrl from \"./assets/sh_hrir_order_1.wav?url\";\nimport { installAutoplayUnlock } from \"./autoplayUnlock\";\nimport { Bus } from \"./bus\";\nimport { AudioCache, type ICache } from \"./cache\";\nimport type {\n AudioBuffer,\n AudioListener,\n AudioNode,\n AudioParam,\n AudioWorkletNode,\n BaseContext,\n BiquadFilterNode,\n ChannelMergerNode,\n ChannelSplitterNode,\n GainNode,\n PannerNode,\n} from \"./context\";\nimport {\n BarberpoleEffect,\n type BarberpoleOptions,\n type CacophonyEffect,\n DynamicsEffect,\n type DynamicsOptions,\n FdnReverbEffect,\n type FdnReverbOptions,\n FoaDecoder,\n FoaDecoderEffect,\n type FoaDecoderOptions,\n FrequencyShifterEffect,\n type FrequencyShifterOptions,\n HarmonizerEffect,\n type HarmonizerOptions,\n ImpulseResponseEffect,\n type ImpulseResponseOptions,\n type ImpulseResponseSource,\n ModulatedDelayEffect,\n type ModulatedDelayOptions,\n markAsCacophonyBiquad,\n PhaserEffect,\n type PhaserOptions,\n ReverbEffect,\n type ReverbOptions,\n ShareEffect,\n SpectralFreezeEffect,\n type SpectralFreezeOptions,\n StereoWidenerEffect,\n type StereoWidenerOptions,\n TremoloEffect,\n type TremoloOptions,\n WaveshaperEffect,\n type WaveshaperOptions,\n} from \"./effects\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { CacophonyEvents } from \"./events\";\nimport { Group } from \"./group\";\nimport { HlsAdapter } from \"./hlsAdapter\";\nimport { type CacophonyLogger, consoleLogger, noopLogger } from \"./logger\";\nimport { MediaStreamSound, type MediaStreamSoundOptions } from \"./mediaStream\";\nimport { LoudnessMeter } from \"./meters/loudness-meter\";\nimport { MicrophoneStream, type MicrophoneStreamOptions } from \"./microphone\";\nimport type { ThreeDOptions } from \"./pannerMixin\";\nimport { PcmStreamSound, type PcmStreamSoundOptions } from \"./pcmStream\";\nimport { GATE_DEFAULT_RATIO } from \"./processors/dynamics-core\";\nimport { DATTORRO_INV_SQRT2 } from \"./processors/modulated-delay-core\";\nimport { type TimeStretchOptions, timeStretch } from \"./processors/timestretch-core\";\nimport { Sound } from \"./sound\";\nimport { Synth } from \"./synth\";\nimport { WebCodecsPullAdapter, type WebCodecsStreamSound } from \"./webCodecsStream\";\nimport { ALL_WORKLETS, WORKLETS, type WorkletModule } from \"./worklets\";\n\nexport type SoundType = \"html\" | \"streaming\" | \"buffer\" | \"oscillator\";\n\nexport interface StreamCapabilities {\n transport: \"webcodecs\" | \"media-element\";\n seekable: boolean;\n live: boolean;\n duration: number;\n}\n\n/**\n * Represents a 3D position in space.\n * @typedef {Array<number>} Position - An array of three numbers representing the x, y, and z coordinates.\n */\nexport type Position = [x: number, y: number, z: number];\n\n/**\n * Represents the orientation of an object in 3D space.\n * @typedef {Object} Orientation - An object containing two positions: forward and up.\n * @property {Position} forward - The forward direction of the object.\n * @property {Position} up - The up direction of the object.\n */\nexport type Orientation = {\n forward: Position;\n up: Position;\n};\n\n/**\n * Represents the number of times a sound should loop.\n * @typedef {number | 'infinite'} LoopCount - The number of loops, or 'infinite' for endless looping.\n */\nexport type LoopCount = number | \"infinite\";\n\n/**\n * Represents the type of fade effect to apply.\n * @typedef {'linear' | 'exponential'} FadeType - The fade type, either 'linear' or 'exponential'.\n */\nexport type FadeType = \"linear\" | \"exponential\";\n\n/**\n * Represents the type of panning effect to apply.\n * @typedef {'HRTF' | 'stereo'} PanType - The pan type, either 'HRTF' for 3D audio or 'stereo' for traditional stereo panning.\n */\nexport type PanType = \"HRTF\" | \"stereo\";\n\n/**\n * Options for configuring fade behavior when starting playback via Sound.play().\n * @interface PlayOptions\n */\nexport interface PlayOptions {\n fadeIn?: number; // duration in ms\n fadeOut?: number; // duration in ms\n fadeType?: FadeType; // applies to both fadeIn and fadeOut\n fadeInPerLoop?: boolean; // re-trigger fadeIn on each loop iteration\n}\n\n/**\n * The base interface for any sound-producing entity, including individual sounds, groups, and playbacks.\n * @interface BaseSound\n */\nexport interface BaseSound {\n isPlaying: boolean;\n streamCapabilities?: StreamCapabilities;\n play(): BaseSound[];\n seek?(time: number): void;\n stop(): void;\n pause(): void;\n addFilter(filter: BiquadFilterNode): void;\n removeFilter(filter: BiquadFilterNode): void;\n volume: number;\n position?: Position;\n threeDOptions?: ThreeDOptions;\n fadeTo?(value: number, duration: number, type?: FadeType): Promise<void>;\n fadeIn?(duration: number, type?: FadeType): Promise<void>;\n fadeOut?(duration: number, type?: FadeType): Promise<void>;\n stopWithFade?(duration: number, type?: FadeType): Promise<void>;\n}\n\n/**\n * Options for creating an offline audio context.\n */\nexport interface OfflineOptions {\n numberOfChannels: number;\n length: number;\n sampleRate: number;\n context?: BaseContext & { startRendering(): Promise<AudioBuffer> };\n}\n\nexport interface RuntimeOptions {\n createAudioWorkletNode?: (context: BaseContext, name: string, options?: AudioWorkletNodeOptions) => any;\n /**\n * Optional hook to remap a worklet module URL just before it is handed to\n * `audioWorklet.addModule`. Receives the worklet's {@link WorkletModule.name}\n * and its default {@link WorkletModule.url}; return the URL to load (return\n * `url` unchanged to keep the default).\n *\n * The library build inlines every worklet bundle as a base64 `data:` URL,\n * which the browser loads directly but `node-web-audio-api` cannot resolve.\n * The `cacophony/node` adapter installs a resolver here that points\n * `addModule` at the bundle file on disk instead, so worklet-backed effects\n * work headless. Has no effect in the browser, where the default is used.\n */\n resolveWorkletUrl?: (name: string, url: string) => string | Promise<string>;\n /**\n * If `true` (the default), Cacophony installs one-time `touchend` / `click` /\n * `keydown` listeners on `document.body` whenever the audio context is\n * constructed in `suspended` state. The first user gesture resumes the\n * context, plays a silent primer buffer (required by iOS Safari for the\n * context to truly unlock), removes the listeners, and emits the `unlock`\n * event on the Cacophony instance.\n *\n * Set to `false` to opt out — you are then responsible for calling\n * `cacophony.resume()` yourself in response to a user gesture.\n *\n * Has no effect when the context is already running, on offline contexts,\n * or in non-browser environments (`document === undefined`).\n *\n * @default true\n */\n autoUnlock?: boolean;\n /**\n * Optional logger for Cacophony's host-side diagnostic output (the\n * `[cacophony/worklet]` messages emitted while loading AudioWorklet\n * modules, plus the \"AudioWorklet not supported\" warning).\n *\n * When provided, all such output is routed through this object instead of\n * the global `console`. Useful for capturing or redirecting logs in Node /\n * headless / CLI hosts. Takes precedence over {@link RuntimeOptions.quiet}.\n *\n * @default consoleLogger (forwards to `console`)\n */\n logger?: CacophonyLogger;\n /**\n * If `true`, suppresses all of Cacophony's host-side diagnostic output by\n * installing a no-op logger. Equivalent to passing `logger: noopLogger`.\n *\n * Ignored when an explicit {@link RuntimeOptions.logger} is also provided.\n *\n * @default false\n */\n quiet?: boolean;\n}\n\n/**\n * Resources belonging to a Sound that must be released when the Sound is\n * garbage collected without an explicit cleanup() call. The record is a plain\n * data bag with no back-references to the Sound or its Playbacks — a\n * FinalizationRegistry strongly retains its held value, so holding the Sound\n * itself would prevent the target from ever becoming collectable.\n */\nexport interface SoundCleanupHoldings {\n sources: Array<{ disconnect(): void }>;\n gainNodes: GainNode[];\n mediaElements: HTMLMediaElement[];\n}\n\nconst WORKLET_LOG_PREFIX = \"[cacophony/worklet]\";\n\n/**\n * Extension → MIME type map used by `createSound` format fallback to query\n * `HTMLAudioElement.canPlayType`. Keys are lower-cased extensions without\n * the leading dot.\n */\nconst EXTENSION_MIME_MAP: Readonly<Record<string, string>> = Object.freeze({\n webm: \"audio/webm\",\n mp3: \"audio/mpeg\",\n ogg: \"audio/ogg\",\n wav: \"audio/wav\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n opus: \"audio/ogg; codecs=opus\",\n});\n\n/**\n * Returns the MIME type for a URL based on its file extension, or `null` if\n * the extension is not recognised. Query strings and fragments are ignored.\n */\nfunction mimeTypeForUrl(url: string): string | null {\n const withoutQuery = url.split(/[?#]/, 1)[0];\n const dotIndex = withoutQuery.lastIndexOf(\".\");\n if (dotIndex < 0) {\n return null;\n }\n const ext = withoutQuery.slice(dotIndex + 1).toLowerCase();\n return EXTENSION_MIME_MAP[ext] ?? null;\n}\n\n/** Web Audio default listener pose when AudioParams are absent. */\nconst DEFAULT_LISTENER_POSITION: Position = [0, 0, 0];\nconst DEFAULT_LISTENER_FORWARD: Position = [0, 0, -1];\nconst DEFAULT_LISTENER_UP: Position = [0, 1, 0];\n\ntype ListenerOrientationParams = {\n readonly forwardX: AudioParam;\n readonly forwardY: AudioParam;\n readonly forwardZ: AudioParam;\n readonly upX: AudioParam;\n readonly upY: AudioParam;\n readonly upZ: AudioParam;\n};\n\ntype ListenerPositionParams = {\n readonly positionX: AudioParam;\n readonly positionY: AudioParam;\n readonly positionZ: AudioParam;\n};\n\nfunction isAudioParam(param: unknown): param is AudioParam {\n return typeof param === \"object\" && param !== null && \"value\" in param && typeof param.value === \"number\";\n}\n\nfunction isSchedulableAudioParam(param: unknown): param is AudioParam {\n return isAudioParam(param) && \"setValueAtTime\" in param && typeof param.setValueAtTime === \"function\";\n}\n\n/**\n * Real hosts are all-or-nothing: Chrome/Safari/SAC expose the full orientation\n * param set, Firefox exposes none. One predicate covers that split.\n */\nfunction hasListenerOrientationParams(listener: AudioListener): listener is AudioListener & ListenerOrientationParams {\n return (\n isAudioParam(listener.forwardX) &&\n isAudioParam(listener.forwardY) &&\n isAudioParam(listener.forwardZ) &&\n isAudioParam(listener.upX) &&\n isAudioParam(listener.upY) &&\n isAudioParam(listener.upZ)\n );\n}\n\nfunction hasListenerPositionParams(listener: AudioListener): listener is AudioListener & ListenerPositionParams {\n return (\n isSchedulableAudioParam(listener.positionX) &&\n isSchedulableAudioParam(listener.positionY) &&\n isSchedulableAudioParam(listener.positionZ)\n );\n}\n\nfunction readListenerOrientation(listener: ListenerOrientationParams): { forward: Position; up: Position } {\n return {\n forward: [listener.forwardX.value, listener.forwardY.value, listener.forwardZ.value],\n up: [listener.upX.value, listener.upY.value, listener.upZ.value],\n };\n}\n\nfunction readListenerPosition(listener: ListenerPositionParams): Position {\n return [listener.positionX.value, listener.positionY.value, listener.positionZ.value];\n}\n\n/**\n * Write listener orientation through AudioParams, or `setOrientation` when\n * those params are missing.\n */\nfunction writeListenerOrientation(listener: AudioListener, forward: Position, up: Position): void {\n const [forwardX, forwardY, forwardZ] = forward;\n const [upX, upY, upZ] = up;\n if (hasListenerOrientationParams(listener)) {\n listener.forwardX.value = forwardX;\n listener.forwardY.value = forwardY;\n listener.forwardZ.value = forwardZ;\n listener.upX.value = upX;\n listener.upY.value = upY;\n listener.upZ.value = upZ;\n return;\n }\n if (typeof listener.setOrientation === \"function\") {\n listener.setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ);\n return;\n }\n throw new Error(\"AudioListener does not support orientation updates\");\n}\n\n/**\n * Write listener position through AudioParams, or `setPosition` when those\n * params are missing.\n */\nfunction writeListenerPosition(listener: AudioListener, position: Position, currentTime: number): void {\n const [x, y, z] = position;\n if (hasListenerPositionParams(listener)) {\n listener.positionX.setValueAtTime(x, currentTime);\n listener.positionY.setValueAtTime(y, currentTime);\n listener.positionZ.setValueAtTime(z, currentTime);\n return;\n }\n if (typeof listener.setPosition === \"function\") {\n listener.setPosition(x, y, z);\n return;\n }\n throw new Error(\"AudioListener does not support position updates\");\n}\n\nexport class Cacophony {\n context: BaseContext;\n globalGainNode: GainNode;\n /**\n * The master bus — `master.input` IS `globalGainNode` (literally the same\n * GainNode), so every existing `.connect(globalGainNode)` call in the\n * codebase remains correct. `cacophony.volume` and `cacophony.mute` still\n * read and write `master.input.gain.value` through that alias.\n */\n master: Bus;\n listener: AudioListener;\n /**\n * Last-known listener pose. Used by getters when AudioParams are unreadable\n * (seeded from params or Web Audio defaults in the constructor).\n */\n private cachedListenerPosition: Position = [...DEFAULT_LISTENER_POSITION];\n private cachedListenerForward: Position = [...DEFAULT_LISTENER_FORWARD];\n private cachedListenerUp: Position = [...DEFAULT_LISTENER_UP];\n private prevVolume: number = 1;\n private isMuted: boolean = false;\n /**\n * Per-context cache of \"module-name has been loaded on this BaseContext\".\n * Keyed on the context itself because `AudioWorklet.addModule()` registers\n * the module against ONE context — a module loaded on context A is NOT\n * loaded on context B. A previous host-scoped `Set<string>` short-circuited\n * `loadAudioWorkletModule()` on name alone, so cross-context\n * {@link ReverbEffect.build} would skip `addModule` on the new context and\n * the second construct would throw with no module registered.\n */\n private loadedAudioWorklets: WeakMap<BaseContext, Set<string>> = new WeakMap();\n /**\n * Per-context cache of the decoded order-1 SH-HRIR `AudioBuffer` used by\n * {@link FoaDecoder}. Keyed on the context (like\n * {@link loadedAudioWorklets}) because `decodeAudioData` produces a buffer\n * bound to ONE context's sample rate; a buffer decoded on context A must not\n * be reused on context B. Stores the in-flight Promise so concurrent\n * `loadFoaHrir` calls share a single fetch/decode.\n */\n private foaHrirCache: WeakMap<BaseContext, Promise<AudioBuffer>> = new WeakMap();\n /**\n * Per-context, per-URL decoded impulse-response cache. Stores in-flight\n * promises so concurrent effect builds for the same context/URL share the\n * same fetch/decode. Rejected promises are evicted so the caller can retry.\n */\n private impulseResponseCache: WeakMap<BaseContext, Map<string, Promise<AudioBuffer>>> = new WeakMap();\n private finalizationRegistry: FinalizationRegistry<SoundCleanupHoldings>;\n private eventEmitter: TypedEventEmitter<CacophonyEvents> = new TypedEventEmitter<CacophonyEvents>();\n private cache: ICache;\n private createAudioWorkletNode: (context: BaseContext, name: string, options?: AudioWorkletNodeOptions) => any;\n private resolveWorkletUrl?: (name: string, url: string) => string | Promise<string>;\n private logger: CacophonyLogger;\n /**\n * Named-bus registry. Populated by {@link createBus} when a name is\n * supplied; entries are removed by the bus's onDestroy hook.\n */\n private _busRegistry: Map<string, Bus> = new Map();\n // Tracks the lifecycle state we have explicitly transitioned to. This avoids\n // duplicate wrapper-driven suspend calls, but resume() still delegates because\n // the underlying AudioContext can be suspended externally.\n private suspendState: \"unknown\" | \"running\" | \"suspended\" = \"unknown\";\n // Cleanup function for the autoplay-unlock state watcher and gesture\n // listeners. No-op when autoUnlock is disabled or unsupported.\n private autoplayUnlockCleanup: () => void = () => {};\n\n /**\n * Constructs a new Cacophony instance.\n *\n * If the supplied (or auto-constructed) audio context is in `suspended`\n * state and `runtimeOptions.autoUnlock` is `true` (default), one-time\n * `touchend` / `click` / `keydown` listeners are installed on\n * `document.body` so the first user gesture transparently unlocks audio\n * — matching Howler's `autoUnlock` behavior. See\n * {@link RuntimeOptions.autoUnlock} for the opt-out and the iOS primer\n * rationale.\n *\n * @param context - Audio context to use. If omitted, a fresh `AudioContext`\n * is constructed (which on mobile will be `suspended` until a user\n * gesture, triggering the auto-unlock path described above).\n * @param cache - Optional cache implementation. Defaults to `AudioCache`.\n * @param runtimeOptions - Optional runtime configuration including the\n * `autoUnlock` opt-out and a `createAudioWorkletNode` factory override.\n */\n constructor(context?: BaseContext, cache?: ICache, runtimeOptions: RuntimeOptions = {}) {\n this.context = context ?? new AudioContext();\n this.listener = this.context.listener;\n if (hasListenerPositionParams(this.listener)) {\n this.cachedListenerPosition = readListenerPosition(this.listener);\n }\n if (hasListenerOrientationParams(this.listener)) {\n const orientation = readListenerOrientation(this.listener);\n this.cachedListenerForward = orientation.forward;\n this.cachedListenerUp = orientation.up;\n }\n this.globalGainNode = this.context.createGain();\n // master bus wraps globalGainNode as its input — same node, two accessors.\n // master is exempt from the named-bus registry (its name 'master' is\n // reserved and not user-creatable via createBus).\n // The Bus constructor wires input → output. We then connect master.output\n // to context.destination so the full audible path is:\n // master.input (= globalGainNode) → [filters] → master.output → destination.\n // Critical: connect master.output AFTER constructing the Bus, NOT\n // globalGainNode directly. If globalGainNode were pre-connected to\n // destination, adding a master filter would call _refreshFilters() which\n // disconnects master.input's outgoing edges, severing the audible path.\n this.master = new Bus(this.context, \"master\", this.globalGainNode, undefined, false);\n this.master.output.connect(this.context.destination);\n this.cache = cache ?? new AudioCache();\n this.createAudioWorkletNode =\n runtimeOptions.createAudioWorkletNode ??\n ((workletContext, name, options) => new AudioWorkletNode(workletContext as any, name, options));\n this.resolveWorkletUrl = runtimeOptions.resolveWorkletUrl;\n this.logger = runtimeOptions.logger ?? (runtimeOptions.quiet ? noopLogger : consoleLogger);\n\n this.finalizationRegistry = new FinalizationRegistry((holdings) => {\n for (const source of holdings.sources) {\n source.disconnect();\n }\n for (const gainNode of holdings.gainNodes) {\n gainNode.disconnect();\n }\n for (const media of holdings.mediaElements) {\n media.pause();\n media.removeAttribute(\"src\");\n media.load();\n }\n });\n\n // Install autoplay unlock — guarded against offline contexts here and\n // unsupported/non-browser environments inside installAutoplayUnlock.\n // Offline contexts cannot suspend in the same way, so unlock makes no\n // sense for them.\n const autoUnlock = runtimeOptions.autoUnlock !== false;\n if (autoUnlock && !this.isOffline) {\n this.autoplayUnlockCleanup = installAutoplayUnlock({\n context: this.context,\n onUnlock: () => {\n this.suspendState = \"running\";\n this.emit(\"unlock\", undefined);\n },\n });\n }\n }\n\n /**\n * Returns `true` while the audio context is suspended — i.e. the library\n * cannot produce sound until the context is resumed (typically by a user\n * gesture). After the auto-unlock fires (or `resume()` is called manually),\n * this becomes `false`.\n *\n * Mirrors Howler's `Howler.ctx.state === 'suspended'` check via the\n * `Howler` global's `_audioUnlocked`. Use this to gate UI that hints to\n * the user that an interaction is required to start audio.\n */\n get locked(): boolean {\n const state = (this.context as unknown as { state?: string }).state;\n return state === \"suspended\";\n }\n\n /** @internal */\n registerSoundForCleanup(sound: object, holdings: SoundCleanupHoldings, unregisterToken: object): void {\n this.finalizationRegistry.register(sound, holdings, unregisterToken);\n }\n\n /** @internal */\n unregisterSoundCleanup(unregisterToken: object): void {\n this.finalizationRegistry.unregister(unregisterToken);\n }\n\n /**\n * Creates a Cacophony instance backed by an OfflineAudioContext.\n * Use this for rendering, bouncing, precomputing processed output,\n * or non-realtime scenarios.\n *\n * @param options - Offline context configuration (channels, length, sampleRate)\n * @param cache - Optional cache implementation\n * @param runtimeOptions - Optional runtime configuration\n * @returns A Cacophony instance backed by OfflineAudioContext\n */\n static createOffline(options: OfflineOptions, cache?: ICache, runtimeOptions?: RuntimeOptions): Cacophony {\n const offlineContext =\n options.context ?? new OfflineAudioContext(options.numberOfChannels, options.length, options.sampleRate);\n return new Cacophony(offlineContext, cache, runtimeOptions);\n }\n\n /**\n * Returns true if this instance is backed by an offline audio context\n * (i.e., the context has a startRendering method).\n */\n get isOffline(): boolean {\n return typeof this.context.startRendering === \"function\";\n }\n\n /**\n * Renders the offline audio graph to a buffer.\n * Only available when the context has a startRendering method.\n *\n * @returns Promise that resolves to the rendered AudioBuffer\n * @throws Error if the context does not support offline rendering\n */\n async startRendering(): Promise<AudioBuffer> {\n if (typeof this.context.startRendering !== \"function\") {\n throw new Error(\n \"startRendering() is only available on offline audio contexts. Use Cacophony.createOffline() to create one.\",\n );\n }\n return this.context.startRendering();\n }\n\n /**\n * OFFLINE independent time-stretch: change an AudioBuffer's tempo WITHOUT\n * changing its pitch, returning a NEW AudioBuffer of length ≈\n * `round(buffer.length · factor)` at the same sample rate.\n *\n * Algorithm: Phase Gradient Heap Integration (PGHI) per Zdeněk Průša & Nicki\n * Holighaus, \"Phase Vocoder Done Right\" (EUSIPCO 2017 / arXiv:2202.07382).\n * The signal is STFT'd, the synthesis phase is reconstructed by integrating\n * the analysis-phase time/frequency gradients along the magnitude ridges\n * (max-heap), then overlap-added at the stretched synthesis hop. No peak\n * picking and no transient detection. Each channel is processed independently.\n *\n * This is an OFFLINE buffer transform, NOT a real-time worklet: the project's\n * OLA worklet base is unity-rate (analysis hop == synthesis hop, fixed to the\n * 128-sample render quantum) and cannot change the time base in real time. A\n * genuine independent stretch needs analysis hop ≠ synthesis hop, which only\n * the whole-buffer offline path provides.\n *\n * @param buffer The source AudioBuffer to time-stretch.\n * @param factor Stretch factor (`> 0`). `> 1` lengthens (slower tempo), `< 1`\n * shortens (faster tempo); pitch is preserved either way.\n * @param options Optional PGHI parameters (fftSize, analysisHop, tol, seed).\n * @returns A new, time-stretched AudioBuffer.\n * @throws If the context cannot create buffers, or `factor <= 0`.\n */\n timeStretchBuffer(buffer: AudioBuffer, factor: number, options?: TimeStretchOptions): AudioBuffer {\n if (typeof this.context.createBuffer !== \"function\") {\n throw new Error(\"timeStretchBuffer requires an audio context that supports createBuffer().\");\n }\n if (!(factor > 0)) {\n throw new Error(`timeStretchBuffer: factor must be > 0, got ${factor}`);\n }\n\n const numberOfChannels = buffer.numberOfChannels;\n const outLength = Math.max(1, Math.round(buffer.length * factor));\n const output = this.context.createBuffer(numberOfChannels, outLength, buffer.sampleRate);\n\n for (let ch = 0; ch < numberOfChannels; ch++) {\n // Copy out of the (possibly mocked) AudioBuffer into a plain Float32Array\n // the pure core can consume, then write the stretched result back.\n const inData = buffer.getChannelData(ch);\n const input = inData instanceof Float32Array ? inData : Float32Array.from(inData);\n const stretched = timeStretch(input, factor, options);\n // The core returns exactly round(input.length·factor) samples; copy what\n // fits the output buffer (lengths match by construction).\n output.copyToChannel(stretched.subarray(0, outLength), ch);\n }\n\n return output;\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n emit<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n async emitAsync<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Eagerly registers every bundled AudioWorklet module on this context. This is\n * OPTIONAL — effects load their own worklet lazily in `build`, and the\n * pitch-shift path loads the phase-vocoder on first use. Call this up front to\n * pay the registration cost ahead of time. Idempotent per context (each module\n * short-circuits via the per-context {@link loadedAudioWorklets} set).\n */\n async loadWorklets(signal?: AbortSignal): Promise<void> {\n if (!this.context.audioWorklet) {\n this.logger.warn(\"AudioWorklet not supported\");\n return;\n }\n for (const worklet of ALL_WORKLETS) {\n await this.loadAudioWorkletModule(worklet.name, worklet.url, signal);\n }\n }\n\n async loadStereoToBFormatWorklet(signal?: AbortSignal): Promise<void> {\n await this.loadAudioWorkletModule(WORKLETS.stereoToBFormat.name, WORKLETS.stereoToBFormat.url, signal);\n }\n\n /**\n * The single worklet-effect construction seam ({@link WorkletEffectHost}).\n * Idempotently registers `worklet`'s module on `context` — or this host's own\n * context when omitted, honoring the cross-context contract that effects'\n * `build(context)` promises — then constructs the AudioWorkletNode with\n * `parameterData`. Every worklet-backed {@link CacophonyEffect} routes through\n * here, as does the phase-vocoder pitch-shift path ({@link Playback.setPitchShift}).\n */\n async buildWorkletEffect(\n worklet: WorkletModule,\n parameterData: Record<string, number>,\n context?: BaseContext,\n nodeOptions?: AudioWorkletNodeOptions,\n ): Promise<AudioWorkletNode> {\n await this.loadAudioWorkletModule(worklet.name, worklet.url, undefined, context);\n return this.createWorkletNode(\n worklet.name,\n worklet.url,\n undefined,\n { ...nodeOptions, parameterData: { ...(nodeOptions?.parameterData ?? {}), ...parameterData } },\n context,\n );\n }\n\n /**\n * Fetches and decodes the bundled order-1 SH-HRIR\n * (`sh_hrir_order_1.wav`, from Omnitone, Apache-2.0 — see\n * `src/assets/NOTICE`) into an `AudioBuffer`, memoized per context. The\n * buffer is the 4-channel (ACN rows W,Y,Z,X) SH-domain HRIR consumed by\n * {@link FoaDecoder} to drive its WY/ZX stereo ConvolverNodes\n * (Ahrens 2022 eq.31 decode).\n *\n * The WAV is 48 kHz; `decodeAudioData` resamples it to the context's sample\n * rate automatically when they differ (standard Web Audio behavior, which\n * Omnitone relies on).\n *\n * @param context Optional BaseContext to decode on. Defaults to this host's\n * own `context`. Supplied so a {@link FoaDecoder} added to a bus on\n * a different context decodes the HRIR on the right context.\n */\n async loadFoaHrir(context?: BaseContext): Promise<AudioBuffer> {\n const ctx = context ?? this.context;\n const cached = this.foaHrirCache.get(ctx);\n if (cached) {\n return cached;\n }\n const pending = (async () => {\n const response = await fetch(foaHrirUrl);\n const encoded = await response.arrayBuffer();\n return ctx.decodeAudioData(encoded);\n })();\n this.foaHrirCache.set(ctx, pending);\n try {\n return await pending;\n } catch (err) {\n // Drop the rejected promise so a later call can retry the fetch/decode.\n this.foaHrirCache.delete(ctx);\n throw err;\n }\n }\n\n /**\n * Fetches and decodes an impulse response URL on the requested audio context,\n * memoized per context and URL. Buffers decoded by one context are not reused\n * on another context, matching Web Audio's context-bound decode behavior.\n *\n * @param url Impulse-response audio URL.\n * @param context Optional decode context. Defaults to this Cacophony instance.\n * @param signal Optional abort signal for the fetch. Decode itself is not\n * abortable in Web Audio, but an already-aborted signal is honored before\n * decode starts.\n */\n async loadImpulseResponseBuffer(url: string, context?: BaseContext, signal?: AbortSignal): Promise<AudioBuffer> {\n const ctx = context ?? this.context;\n let cacheForContext = this.impulseResponseCache.get(ctx);\n if (!cacheForContext) {\n cacheForContext = new Map<string, Promise<AudioBuffer>>();\n this.impulseResponseCache.set(ctx, cacheForContext);\n }\n const cached = cacheForContext.get(url);\n if (cached) {\n return this.waitForImpulseResponseLoad(cached, signal);\n }\n if (signal?.aborted) {\n throw this.createAbortError();\n }\n\n const pending = (async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to load impulse response from ${url}: ${response.status} ${response.statusText}`);\n }\n const encoded = await response.arrayBuffer();\n return ctx.decodeAudioData(encoded);\n })();\n cacheForContext.set(url, pending);\n void pending.catch(() => {\n if (cacheForContext.get(url) === pending) {\n cacheForContext.delete(url);\n }\n });\n return this.waitForImpulseResponseLoad(pending, signal);\n }\n\n async createWorkletNode(\n name: string,\n url: string,\n signal?: AbortSignal,\n options?: AudioWorkletNodeOptions,\n context?: BaseContext,\n ): Promise<AudioWorkletNode> {\n // Use the supplied context for cross-context worklet construction\n // (e.g. ReverbEffect built against a context other than this host's\n // own). Default to this host's `context` for the common single-context\n // case so all existing callers keep working without a change.\n const ctx = context ?? this.context;\n // ensure audioWorklet has been loaded\n if (!ctx.audioWorklet) {\n throw new Error(\"AudioWorklet not supported\");\n }\n try {\n const node = this.createAudioWorkletNode(ctx, name, options);\n this.logger.info(`${WORKLET_LOG_PREFIX} construct succeeded`, {\n name,\n loaded: this.isWorkletLoadedOn(ctx, name),\n });\n return node;\n } catch (err) {\n this.logger.warn(`${WORKLET_LOG_PREFIX} construct failed`, {\n name,\n loaded: this.isWorkletLoadedOn(ctx, name),\n error: err,\n });\n try {\n await this.loadAudioWorkletModule(name, url, signal, ctx);\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} load failed`, {\n name,\n error: err,\n });\n throw err; // Preserve original error (including AbortError)\n }\n\n try {\n const node = this.createAudioWorkletNode(ctx, name, options);\n this.logger.info(`${WORKLET_LOG_PREFIX} construct after load succeeded`, { name });\n return node;\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} construct after load failed`, {\n name,\n error: err,\n });\n throw err;\n }\n }\n }\n\n async createStereoToBFormatNode(signal?: AbortSignal): Promise<AudioWorkletNode> {\n return this.createWorkletNode(WORKLETS.stereoToBFormat.name, WORKLETS.stereoToBFormat.url, signal, {\n numberOfInputs: 1,\n numberOfOutputs: 1,\n outputChannelCount: [4],\n channelCount: 2,\n channelCountMode: \"explicit\",\n channelInterpretation: \"speakers\",\n });\n }\n\n /**\n * Lookup helper: has worklet `name` been loaded on `ctx` (per the\n * per-context {@link loadedAudioWorklets} cache).\n */\n private isWorkletLoadedOn(ctx: BaseContext, name: string): boolean {\n return this.loadedAudioWorklets.get(ctx)?.has(name) ?? false;\n }\n\n /**\n * Record `name` as loaded on `ctx`, lazily allocating the per-context Set.\n */\n private markWorkletLoadedOn(ctx: BaseContext, name: string): void {\n let names = this.loadedAudioWorklets.get(ctx);\n if (!names) {\n names = new Set<string>();\n this.loadedAudioWorklets.set(ctx, names);\n }\n names.add(name);\n }\n\n private async loadAudioWorkletModule(\n name: string,\n url: string,\n signal?: AbortSignal,\n context?: BaseContext,\n ): Promise<void> {\n const ctx = context ?? this.context;\n if (!ctx.audioWorklet) {\n throw new Error(\"AudioWorklet not supported\");\n }\n // Per-context membership: a module loaded on context A is NOT loaded on\n // context B. The previous host-scoped check by name only would skip the\n // addModule on B after A had loaded the same name, leaving B without\n // the worklet registered.\n if (this.isWorkletLoadedOn(ctx, name)) {\n this.logger.info(`${WORKLET_LOG_PREFIX} load skipped`, { name });\n return;\n }\n // Host seam: the browser loads the inlined `data:` bundle directly, but the\n // Node backend remaps to the on-disk bundle file (see RuntimeOptions.resolveWorkletUrl).\n const resolvedUrl = this.resolveWorkletUrl ? await this.resolveWorkletUrl(name, url) : url;\n this.logger.info(`${WORKLET_LOG_PREFIX} addModule start`, {\n name,\n url: resolvedUrl,\n aborted: signal?.aborted ?? false,\n });\n try {\n await ctx.audioWorklet.addModule(resolvedUrl, {\n credentials: \"same-origin\",\n ...(signal && { signal }),\n });\n this.markWorkletLoadedOn(ctx, name);\n this.logger.info(`${WORKLET_LOG_PREFIX} addModule resolved`, { name });\n } catch (err) {\n this.logger.error(`${WORKLET_LOG_PREFIX} addModule rejected`, {\n name,\n error: err,\n });\n throw err;\n }\n }\n\n private createAbortError(): DOMException {\n return new DOMException(\"Operation was aborted\", \"AbortError\");\n }\n\n private waitForImpulseResponseLoad(pending: Promise<AudioBuffer>, signal?: AbortSignal): Promise<AudioBuffer> {\n if (!signal) {\n return pending;\n }\n if (signal.aborted) {\n return Promise.reject(this.createAbortError());\n }\n return new Promise((resolve, reject) => {\n const handleAbort = () => {\n cleanup();\n reject(this.createAbortError());\n };\n const cleanup = () => {\n signal.removeEventListener(\"abort\", handleAbort);\n };\n signal.addEventListener(\"abort\", handleAbort, { once: true });\n pending.then(\n (buffer) => {\n cleanup();\n resolve(buffer);\n },\n (err: unknown) => {\n cleanup();\n reject(err);\n },\n );\n });\n }\n\n private createMediaSound(\n url: string,\n soundType: \"html\" | \"streaming\",\n panType: PanType,\n signal?: AbortSignal,\n preparedAudio?: HTMLAudioElement,\n useHlsJs: boolean = false,\n ): Promise<Sound> {\n if (signal?.aborted) {\n return Promise.reject(this.createAbortError());\n }\n\n return new Promise<Sound>((resolve, reject) => {\n const audio = preparedAudio ?? new Audio();\n let hlsAdapter: HlsAdapter | undefined;\n let sound: Sound | undefined;\n let settled = false;\n const pendingHlsErrors: Array<{ error: Error; recoverable: boolean }> = [];\n\n const cleanup = () => {\n audio.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.removeEventListener(\"error\", handleError);\n signal?.removeEventListener(\"abort\", handleAbort);\n };\n\n const teardown = () => {\n hlsAdapter?.destroy();\n audio.pause();\n audio.src = \"\";\n audio.load();\n };\n\n const settle = (callback: () => void) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n callback();\n };\n\n const handleLoadedMetadata = () => {\n settle(() => {\n sound = new Sound(\n url,\n undefined,\n this.context,\n this.globalGainNode,\n soundType,\n panType,\n this,\n audio,\n hlsAdapter ? () => hlsAdapter?.destroy() : undefined,\n );\n if (soundType === \"streaming\") {\n sound.streamCapabilities = {\n duration: Number.isFinite(audio.duration) ? audio.duration : Number.POSITIVE_INFINITY,\n live: audio.duration === Number.POSITIVE_INFINITY,\n seekable: (audio.seekable?.length ?? 0) > 0,\n transport: \"media-element\",\n };\n }\n for (const { error, recoverable } of pendingHlsErrors) {\n sound.reportLoadError(error, recoverable);\n }\n resolve(sound);\n });\n };\n\n const handleHlsError = (error: Error, recoverable: boolean) => {\n if (sound) {\n sound.reportLoadError(error, recoverable);\n } else if (recoverable) {\n pendingHlsErrors.push({ error, recoverable });\n } else {\n settle(() => {\n teardown();\n reject(error);\n });\n }\n };\n\n const handleError = () => {\n const error = new Error(`Failed to load audio from ${url}`);\n settle(() => {\n teardown();\n reject(error);\n });\n };\n\n const handleAbort = () => {\n const error = this.createAbortError();\n settle(() => {\n teardown();\n reject(error);\n });\n };\n\n audio.crossOrigin = \"anonymous\";\n audio.preload = \"auto\";\n audio.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.addEventListener(\"error\", handleError);\n signal?.addEventListener(\"abort\", handleAbort, { once: true });\n if (useHlsJs) {\n void HlsAdapter.create(handleHlsError).then(\n (adapter) => {\n hlsAdapter = adapter;\n if (settled) {\n adapter.destroy();\n return;\n }\n try {\n adapter.attach(audio, url);\n } catch (error) {\n settle(() => {\n teardown();\n reject(error);\n });\n }\n },\n (error: unknown) => {\n settle(() => {\n teardown();\n reject(error);\n });\n },\n );\n } else {\n audio.src = url;\n audio.load();\n }\n });\n }\n\n private createHlsSound(url: string, panType: PanType, signal?: AbortSignal): Promise<Sound> {\n const audio = new Audio();\n const nativeHls = audio.canPlayType(\"application/vnd.apple.mpegurl\") || audio.canPlayType(\"application/x-mpegURL\");\n return this.createMediaSound(url, \"streaming\", panType, signal, audio, !nativeHls);\n }\n\n clearMemoryCache(): void {\n this.cache.clearMemoryCache();\n }\n\n createOscillator(options: OscillatorOptions, panType: PanType = \"HRTF\"): Synth {\n const synth = new Synth(this.context, this.globalGainNode, \"oscillator\", panType, options, this);\n return synth;\n }\n\n /**\n * Creates a Sound instance from an AudioBuffer or URL.\n *\n * @param bufferOrUrl - AudioBuffer instance or URL string to create sound from\n * @param soundType - Type of sound (Buffer, HTML, Streaming)\n * @param panType - Type of panning (HRTF or stereo)\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to a Sound instance\n */\n async createSound(\n buffer: AudioBuffer,\n soundType?: SoundType,\n panType?: PanType,\n signal?: AbortSignal,\n ): Promise<Sound>;\n\n async createSound(url: string, soundType?: SoundType, panType?: PanType, signal?: AbortSignal): Promise<Sound>;\n\n /**\n * Creates a Sound from the first playable URL in a Howler-style fallback\n * array. The browser's `HTMLAudioElement.canPlayType` is queried per\n * extension; the first source it reports as supported (`'probably'` or\n * `'maybe'`) is fetched and decoded. If decoding rejects for a candidate,\n * the next playable candidate is tried — a `'maybe'` can still fail at\n * decode time.\n *\n * MIME types are inferred from file extensions: `.webm` → `audio/webm`,\n * `.mp3` → `audio/mpeg`, `.ogg` → `audio/ogg`, `.wav` → `audio/wav`,\n * `.flac` → `audio/flac`, `.m4a` → `audio/mp4`, `.aac` → `audio/aac`,\n * `.opus` → `audio/ogg; codecs=opus`.\n *\n * Cache and loading events fire only for the URL actually fetched.\n *\n * v1 limitation: only the default `'buffer'` sound type participates in\n * fallback. Passing an array with any non-`'buffer'` soundType (e.g.\n * `'html'`, `'streaming'`, `'oscillator'`) rejects with a clear \"not yet\n * supported\" error.\n *\n * @param urls - Non-empty array of candidate URLs in priority order.\n * @throws If the array is empty, if `soundType` is not `'buffer'`, or if\n * no candidate is playable (codec unsupported or every decode failed).\n * The error message names the URLs that were tried.\n */\n async createSound(urls: string[], soundType?: SoundType, panType?: PanType, signal?: AbortSignal): Promise<Sound>;\n\n async createSound(\n bufferOrUrl: AudioBuffer | string | string[],\n soundType: SoundType = \"buffer\",\n panType: PanType = \"HRTF\",\n signal?: AbortSignal,\n ): Promise<Sound> {\n if (Array.isArray(bufferOrUrl)) {\n return this.createSoundFromUrlArray(bufferOrUrl, soundType, panType, signal);\n }\n if (typeof bufferOrUrl === \"object\") {\n return Promise.resolve(new Sound(\"\", bufferOrUrl, this.context, this.globalGainNode, soundType, panType, this));\n }\n const url = bufferOrUrl;\n if (soundType === \"html\") {\n return this.createMediaSound(url, \"html\", panType, signal);\n }\n if (soundType === \"streaming\") {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n return this.loadBufferSound(url, soundType, panType, signal);\n }\n\n private async loadBufferSound(\n url: string,\n soundType: SoundType,\n panType: PanType,\n signal?: AbortSignal,\n ): Promise<Sound> {\n const buffer = await this.cache.getAudioBuffer(this.context, url, signal, {\n onLoadingStart: (event) => this.emitAsync(\"loadingStart\", event),\n onLoadingProgress: (event) => this.emitAsync(\"loadingProgress\", event),\n onLoadingComplete: (event) => this.emitAsync(\"loadingComplete\", event),\n onLoadingError: (event) => this.emitAsync(\"loadingError\", event),\n onCacheHit: (event) => this.emitAsync(\"cacheHit\", event),\n onCacheMiss: (event) => this.emitAsync(\"cacheMiss\", event),\n onCacheError: (event) => this.emitAsync(\"cacheError\", event),\n });\n return new Sound(url, buffer, this.context, this.globalGainNode, soundType, panType, this);\n }\n\n /**\n * Returns true if the error came from `AudioContext.decodeAudioData`.\n *\n * Per the Web Audio spec, decode failures throw a `DOMException` with name\n * `EncodingError`. We deliberately do NOT treat plain `Error` instances as\n * decode failures: fetch/cache/network errors must propagate so the caller\n * sees the real cause instead of silently falling through to a different\n * format. See `reports/format-fallback-codex-review.md` (Major 1).\n */\n private static isDecodeError(error: unknown): boolean {\n if (typeof DOMException === \"undefined\") {\n return false;\n }\n return error instanceof DOMException && error.name === \"EncodingError\";\n }\n\n private async createSoundFromUrlArray(\n urls: string[],\n soundType: SoundType,\n panType: PanType,\n signal?: AbortSignal,\n ): Promise<Sound> {\n if (urls.length === 0) {\n throw new Error(\"createSound: URL array is empty; provide at least one URL\");\n }\n if (soundType !== \"buffer\") {\n throw new Error(\n `createSound: URL array with soundType='${soundType}' is not yet supported. ` +\n `Format fallback is only available for buffer sounds in this version.`,\n );\n }\n const candidateReasons = new Map<string, string>();\n const playable: string[] = [];\n for (const url of urls) {\n if (this.canPlaySource(url)) {\n playable.push(url);\n } else {\n const ext = url.match(/\\.([a-z0-9]+)(?:\\?|#|$)/i)?.[1]?.toLowerCase();\n candidateReasons.set(url, ext ? `codec unsupported: .${ext}` : \"codec unsupported: unknown extension\");\n }\n }\n if (playable.length === 0) {\n const detail = urls.map((u) => `${u}: ${candidateReasons.get(u) ?? \"codec unsupported\"}`).join(\"; \");\n throw new Error(\n `createSound: no playable source found among [${urls.join(\", \")}] ` +\n `(no candidate's MIME type was reported as supported by HTMLAudioElement.canPlayType). ` +\n `Reasons: ${detail}`,\n );\n }\n for (const url of playable) {\n try {\n // Array path always produces buffer sounds; html/streaming were rejected above.\n return await this.loadBufferSound(url, \"buffer\", panType, signal);\n } catch (error) {\n if ((error as { name?: string } | null)?.name === \"AbortError\") {\n throw error;\n }\n if (!Cacophony.isDecodeError(error)) {\n // Fetch/cache/network failure of the selected source — propagate.\n // Only decode failures fall through to the next candidate.\n throw error;\n }\n candidateReasons.set(url, `decode failed: ${(error as Error)?.message ?? String(error)}`);\n }\n }\n const detail = urls.map((u) => `${u}: ${candidateReasons.get(u) ?? \"unknown\"}`).join(\"; \");\n throw new Error(\n `createSound: every playable candidate failed to decode. Tried [${urls.join(\", \")}]. Reasons: ${detail}`,\n );\n }\n\n private canPlaySource(url: string): boolean {\n if (typeof Audio === \"undefined\") {\n return true;\n }\n const mime = mimeTypeForUrl(url);\n if (!mime) {\n return false;\n }\n try {\n const probe = new Audio();\n const result = probe.canPlayType(mime);\n return result === \"probably\" || result === \"maybe\";\n } catch {\n return false;\n }\n }\n\n async createGroup(sounds: Sound[]): Promise<Group> {\n const group = new Group();\n sounds.forEach((sound) => group.addSound(sound));\n return group;\n }\n\n /**\n * Creates a Group containing Sound instances loaded from multiple URLs.\n *\n * @param urls - Array of URL strings to load as sounds\n * @param soundType - Type of sound (Buffer, HTML, Streaming)\n * @param panType - Type of panning (HRTF or stereo)\n * @param signal - Optional AbortSignal to cancel the operation\n * @returns Promise that resolves to a Group containing all loaded sounds\n */\n async createGroupFromUrls(\n urls: string[],\n soundType: SoundType = \"buffer\",\n panType: PanType = \"HRTF\",\n signal?: AbortSignal,\n ): Promise<Group> {\n const group = new Group();\n const sounds = await Promise.all(urls.map((url) => this.createSound(url, soundType, panType, signal)));\n sounds.forEach((sound) => group.addSound(sound));\n return group;\n }\n\n /**\n * Creates a sample-accurate URL stream when WebCodecs can decode the primary\n * audio track. Encoded media is fetched and demuxed incrementally, then its\n * PCM is fed into the same routable AudioWorklet source returned by\n * {@link createPcmStreamSound}. Browsers without WebCodecs, or without a\n * decoder for the track, fall back to the media-element streaming tier.\n * @param url - Encoded audio stream URL.\n * @param signal - Optional cancellation signal retained as the second\n * parameter for backwards compatibility.\n * @param panType - Type of panning (HRTF or stereo).\n */\n async createStream(\n url: string,\n signal?: AbortSignal,\n panType: PanType = \"HRTF\",\n ): Promise<Sound | WebCodecsStreamSound> {\n if (/\\.m3u8(?:$|[?#])/i.test(url)) {\n return this.createHlsSound(url, panType, signal);\n }\n\n if (typeof globalThis.AudioDecoder !== \"function\") {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n\n const adapter = await WebCodecsPullAdapter.open(url, this.context.sampleRate, signal);\n if (!adapter) {\n return this.createMediaSound(url, \"streaming\", panType, signal);\n }\n try {\n const sound = await this.createPcmStreamSound({\n channelCount: adapter.channelCount,\n panType,\n signal,\n });\n return adapter.attach(sound);\n } catch (error) {\n adapter.cleanup();\n throw error;\n }\n }\n\n createMediaStreamSound(stream: MediaStream, options?: MediaStreamSoundOptions): MediaStreamSound {\n return new MediaStreamSound(stream, this.context, this.globalGainNode, options, this);\n }\n\n /**\n * Creates a push-based PCM source backed by a fixed-size AudioWorklet ring\n * buffer. Each write is an interleaved Float32Array at this context's sample\n * rate.\n */\n async createPcmStreamSound(options: PcmStreamSoundOptions = {}): Promise<PcmStreamSound> {\n options.signal?.throwIfAborted();\n const channelCount = options.channelCount ?? 1;\n const bufferDuration = options.bufferDuration ?? 1;\n const latency = options.latency ?? 0.05;\n if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 32) {\n throw new RangeError(\"PCM channelCount must be an integer between 1 and 32\");\n }\n if (!Number.isFinite(bufferDuration) || bufferDuration <= 0) {\n throw new RangeError(\"PCM bufferDuration must be greater than zero\");\n }\n if (!Number.isFinite(latency) || latency < 0 || latency > bufferDuration) {\n throw new RangeError(\"PCM latency must be between zero and bufferDuration\");\n }\n\n const capacityFrames = Math.ceil(this.context.sampleRate * bufferDuration);\n const latencyFrames = Math.ceil(this.context.sampleRate * latency);\n const node = await this.createWorkletNode(WORKLETS.pcmStream.name, WORKLETS.pcmStream.url, options.signal, {\n numberOfInputs: 0,\n numberOfOutputs: 1,\n outputChannelCount: [channelCount],\n channelCount,\n channelCountMode: \"explicit\",\n processorOptions: {\n capacityFrames,\n channelCount,\n latencyFrames,\n },\n });\n const sound = new PcmStreamSound(\n node,\n this.context,\n this.globalGainNode,\n {\n ...options,\n channelCount,\n bufferDuration,\n latency,\n },\n this,\n );\n if (options.signal?.aborted) {\n sound.cleanup();\n options.signal.throwIfAborted();\n }\n return sound;\n }\n\n createBiquadFilter = ({ type, frequency, gain, Q }: BiquadFilterOptions): BiquadFilterNode => {\n if (frequency === undefined) {\n frequency = 350;\n }\n const filter = this.context.createBiquadFilter();\n filter.type = type ?? \"lowpass\";\n filter.frequency.value = frequency;\n filter.gain.value = gain ?? 0;\n filter.Q.value = Q ?? 1;\n markAsCacophonyBiquad(filter);\n return filter;\n };\n\n /**\n * Wraps a raw AudioNode in a {@link CacophonyEffect} so it can be added\n * to a {@link Bus} filter chain. By default `Bus.addFilter` rejects raw\n * AudioNodes — wrapping via `shareEffect` is the explicit opt-in that\n * signals \"yes, I understand this single node will be shared across every\n * bus I add it to.\"\n */\n shareEffect(node: AudioNode): CacophonyEffect {\n return new ShareEffect(node);\n }\n\n /**\n * Creates a DattorroReverb {@link CacophonyEffect}. The effect's `build`\n * lazily registers the worklet module (no-op if already loaded) and\n * constructs the AudioWorkletNode with the supplied {@link ReverbOptions}\n * as `parameterData`. Add the returned effect to a {@link Bus}'s filter\n * chain via `bus.addFilter(effect)`.\n */\n createReverb(options: ReverbOptions = {}): ReverbEffect {\n return new ReverbEffect(this, options);\n }\n\n /**\n * Creates a native ConvolverNode impulse-response effect. Pass an AudioBuffer\n * for an already-decoded IR or a URL to fetch/decode through the per-context\n * IR cache. The default is wet-only (`dry: 0`, `wet: 1`) and returns a single\n * ConvolverNode when added to a bus; setting `dry` or non-unity `wet` builds\n * an owned dry/wet endpoint graph exposing `dry` and `wet` automation params.\n */\n createImpulseResponse(source: ImpulseResponseSource, options: ImpulseResponseOptions = {}): ImpulseResponseEffect {\n return new ImpulseResponseEffect(this, source, options);\n }\n\n /**\n * Creates a Feedback Delay Network (FDN) {@link CacophonyEffect} — an\n * algorithmic reverb with a lossless degree-0 paraunitary Hadamard feedback\n * matrix (Schlecht & Habets 2019), per-delay-line absorption filters setting\n * the reverberation time T60 (Jot & Chaigne 1991), and multiplication-free\n * velvet-noise input diffusion (Fagerström et al. 2020). The effect's\n * `build` lazily registers the worklet module (no-op if already loaded) and\n * constructs the AudioWorkletNode with the supplied {@link FdnReverbOptions}\n * as `parameterData`. Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createFdnReverb(options: FdnReverbOptions = {}): FdnReverbEffect {\n return new FdnReverbEffect(this, options);\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} configured as a COMPRESSOR\n * (ratio > 1 reduces the level of signals above threshold). Implements the\n * feed-forward design of Giannoulis, Massberg & Reiss 2012. Add the returned\n * effect to a {@link Bus} via `bus.addFilter(effect)`. The same machinery\n * (gain computer + log-domain smooth-branching detector) backs the limiter\n * and gate presets below.\n */\n createCompressor(options: DynamicsOptions = {}): DynamicsEffect {\n return new DynamicsEffect(this, options);\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} preset as a LIMITER — a\n * compressor with an effectively infinite ratio so output is clamped at the\n * threshold (Giannoulis 2012 eqs 18-19). The ratio is fixed to the worklet's\n * limiter sentinel; caller-supplied `ratio` is ignored. Other params\n * (threshold, knee, attack, release, makeup) remain configurable.\n */\n createLimiter(options: Omit<DynamicsOptions, \"ratio\"> = {}): DynamicsEffect {\n return new DynamicsEffect(this, { ...options, ratio: 1000 });\n }\n\n /**\n * Creates a dynamics {@link CacophonyEffect} preset as a downward EXPANDER /\n * GATE — ratio < 1 so signals BELOW the threshold are pushed further down\n * (Giannoulis 2012 p.403). The default `ratio` of 0.1 gives gate-like\n * downward expansion; pass a `ratio` closer to 1 for gentler expansion.\n */\n createGate(options: DynamicsOptions = {}): DynamicsEffect {\n return new DynamicsEffect(this, { ratio: GATE_DEFAULT_RATIO, ...options });\n }\n\n /**\n * Creates a {@link WaveshaperEffect} — an antialiased distortion/waveshaper\n * implementing first-order Antiderivative Antialiasing (Parker, Zavalishin &\n * Le Bivic 2016, DAFx-16): y[n] = (F0(x_n) - F0(x_{n-1}))/(x_n - x_{n-1})\n * (eq.9), with an f(midpoint) fallback at the 0/0 singularity (eq.10). Ships\n * two nonlinearities — `shape: 0` hard clip (polynomial F0, eq.25), `shape: 1`\n * tanh soft clip (F0 = log cosh, eq.20). Carries an inherent 0.5-sample group\n * delay (eq.17). Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createWaveshaper(options: WaveshaperOptions = {}): WaveshaperEffect {\n return new WaveshaperEffect(this, options);\n }\n\n /**\n * Creates a {@link WaveshaperEffect} preset as a DISTORTION — the tanh soft\n * clipper (`shape: 1`, F0 = log cosh, Parker 2016 eq.20) with a default drive\n * of 4 for an audible saturated tone. A convenience wrapper over\n * {@link createWaveshaper}; caller-supplied options override the defaults.\n */\n createDistortion(options: WaveshaperOptions = {}): WaveshaperEffect {\n return new WaveshaperEffect(this, { drive: 4, shape: 1, ...options });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a DELAY / ECHO — Dattorro's\n * unified modulated-delay circuit (JAES 1997, Fig. 36) with the modulation OFF\n * (`depth: 0`, `rate: 0`). Echo knobs (Table 6): full dry `blend: 1` plus a\n * unity wet tap `feedforward: 1`; feedback defaults to 0 (a single tap) but is\n * caller-configurable for a regenerating echo (|feedback| < 1, Table 6). The\n * 250 ms tap sits in the echo range (Table 7). All knobs are exposed.\n */\n createDelay(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: 1,\n feedforward: 1,\n feedback: 0,\n delayTime: 250,\n depth: 0,\n rate: 0,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a (white) CHORUS — Dattorro\n * Table 6 white-chorus knobs `blend: 0.7071`, `feedforward: 1`,\n * `feedback: 0.7071` (blend = feedback, feedforward = 1, the negative-feedback\n * path that minimizes the spectral aberration of a plain dry+wet chorus). A\n * 9 ms tap modulated 4 ms at 0.5 Hz sits in the chorus range (Table 7).\n *\n * NOTE: Dattorro's white chorus (p.776) combines this negative-feedback path\n * with ALLPASS fractional-delay interpolation. This implementation uses the\n * white-chorus knob settings but substitutes cubic-Lagrange interpolation\n * (Laakso 1996) — transient-safe under modulation (Laakso p.52), trading some\n * high-frequency trough depth versus a true allpass-interpolated white chorus.\n */\n createChorus(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: 1,\n feedback: DATTORRO_INV_SQRT2,\n delayTime: 9,\n depth: 4,\n rate: 0.5,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a FLANGER — Dattorro Table 6\n * flanger knobs `blend: 0.7071`, `feedforward: 0.7071` (blend = feedforward for\n * the deepest comb trough), `feedback: -0.7071` (negative feedback deepens the\n * troughs). A short 5 ms tap swept 4 ms at 0.25 Hz gives the classic flange\n * (Table 7).\n */\n createFlanger(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: DATTORRO_INV_SQRT2,\n feedback: -DATTORRO_INV_SQRT2,\n delayTime: 5,\n depth: 4,\n rate: 0.25,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as a VIBRATO — Dattorro Table 6\n * vibrato knobs `blend: 0`, `feedforward: 1`, `feedback: 0` (100% wet, no dry\n * path, no feedback), so only the pitch-modulated delayed signal is heard. A\n * 5 ms tap swept 3 ms at 5 Hz (Table 7).\n */\n createVibrato(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: 0,\n feedforward: 1,\n feedback: 0,\n delayTime: 5,\n depth: 3,\n rate: 5,\n ...options,\n });\n }\n\n /**\n * Creates a {@link ModulatedDelayEffect} preset as DOUBLING (\"double tracking\")\n * — Dattorro Table 6 doubling knobs `blend: 0.7071`, `feedforward: 0.7071`,\n * `feedback: 0` (no feedback). A ~20 ms tap (Table 7 doubling nominal) with a\n * gentle 1 ms / 0.4 Hz wobble fattens a single take into two.\n */\n createDoubling(options: ModulatedDelayOptions = {}): ModulatedDelayEffect {\n return new ModulatedDelayEffect(this, {\n blend: DATTORRO_INV_SQRT2,\n feedforward: DATTORRO_INV_SQRT2,\n feedback: 0,\n delayTime: 20,\n depth: 1,\n rate: 0.4,\n ...options,\n });\n }\n\n /**\n * Creates a {@link PhaserEffect} — a classic MXR/Univibe-style phase shifter: a\n * cascade of first-order allpass sections at a common LFO-swept break frequency\n * summed additively with the dry signal to sweep notches through the spectrum\n * (Smith STAN-M-21; PASP §8.9). Two allpass sections per notch (default 4\n * sections = 2 notches). Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createPhaser(options: PhaserOptions = {}): PhaserEffect {\n return new PhaserEffect(this, options);\n }\n\n /**\n * Creates a {@link TremoloEffect} — LFO-driven amplitude modulation (a VCA\n * swung by a low-frequency oscillator). The gain swings between (1 - depth)\n * and 1, never inverting (a true tremolo, not ring modulation). Anchored to\n * standard AM theory, Dattorro 1997 (p.776) for the quadrature stereo LFO, and\n * Mitcheltree et al. (DAFx23) for the LFO-driven-effect framing. `shape` selects\n * the LFO waveform (0 = sine, 1 = triangle, 2 = square); `stereoPhase` offsets\n * the per-channel LFO. Add the returned effect to a {@link Bus} via\n * `bus.addFilter(effect)`.\n */\n createTremolo(options: TremoloOptions = {}): TremoloEffect {\n return new TremoloEffect(this, options);\n }\n\n /**\n * Creates a {@link TremoloEffect} preset as an AUTO-PAN — a tremolo with\n * `stereoPhase: 180`, so the left and right channel gains modulate in\n * anti-phase (the sound swings hard between the speakers). The 180-deg\n * per-channel LFO offset is Dattorro's stereo-modulation convention (p.776). A\n * convenience wrapper over {@link createTremolo}; caller options override the\n * preset.\n */\n createAutoPan(options: TremoloOptions = {}): TremoloEffect {\n return new TremoloEffect(this, { stereoPhase: 180, ...options });\n }\n\n /**\n * Creates a Hilbert-transform single-sideband frequency shifter (Wardle,\n * DAFx-98). Unlike pitch shifting, every partial moves by the same signed\n * number of hertz, deliberately breaking harmonic ratios.\n */\n createFrequencyShifter(options: FrequencyShifterOptions = {}): FrequencyShifterEffect {\n return new FrequencyShifterEffect(this, options);\n }\n\n /**\n * Creates the spectral-delay SSB barberpole effect from Esqueda, Valimaki &\n * Parker (DAFx-15, Fig. 12): notches travel indefinitely in the direction\n * selected by the sign of `rate`, rather than reversing like a normal phaser.\n */\n createBarberpole(options: BarberpoleOptions = {}): BarberpoleEffect {\n return new BarberpoleEffect(this, options);\n }\n\n /** Adds two identity-phase-locked pitch voices in one Laroche-Dolson STFT pass. */\n createHarmonizer(options: HarmonizerOptions = {}): HarmonizerEffect {\n return new HarmonizerEffect(this, options);\n }\n\n /**\n * Captures and sustains a spectrum while continuing each bin's measured\n * phase advance. Automate the `freeze` AudioParam to capture/release.\n */\n createSpectralFreeze(options: SpectralFreezeOptions = {}): SpectralFreezeEffect {\n return new SpectralFreezeEffect(this, options);\n }\n\n /** Creates an explicit-stereo sparse decorrelator with transient protection. */\n createStereoWidener(options: StereoWidenerOptions = {}): StereoWidenerEffect {\n return new StereoWidenerEffect(this, options);\n }\n\n /** The default audio context for this Cacophony instance (used by\n * {@link FoaDecoder} when no explicit context is supplied). */\n defaultContext(): BaseContext {\n return this.context;\n }\n\n /**\n * Creates a {@link FoaDecoder} — a standalone first-order ambisonic (FOA) ->\n * binaural FORMAT CONVERTER built from native Web Audio nodes (no worklet).\n * It decodes a 4-channel ACN/SN3D `[W, Y, Z, X]` signal to headphone stereo\n * via the per-SH-channel HRIR decode of Ahrens 2022 (eq.31), using\n * Omnitone's WY/ZX 2-stereo-ConvolverNode packing and the bundled order-1\n * SH-HRIR.\n *\n * It is 4-channel-in / 2-channel-out; this method returns the explicit\n * endpoint object for custom graph wiring:\n * feed FOA into `decoder.input` (4-ch) and route `decoder.output` (2-ch\n * stereo) downstream:\n * ```ts\n * const decoder = await cacophony.createFoaDecoder();\n * foaSource.connect(decoder.input);\n * decoder.output.connect(bus.input); // or context.destination\n * ```\n * Build is async (the bundled HRIR is fetched + decoded). Pair it with\n * {@link encodeMonoToFoaSN3D} (clean, physically correct) or with\n * `createStereoToBFormatNode` (the perceptual, approximate stereo-upmix path).\n */\n async createFoaDecoder(options: FoaDecoderOptions = {}, context?: BaseContext): Promise<FoaDecoder> {\n return FoaDecoder.create(this, options, context);\n }\n\n /**\n * Creates a bus-filter wrapper around {@link FoaDecoder}. Use this on a\n * dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only\n * filter that converts that bus to stereo binaural output. For custom manual\n * wiring, use {@link createFoaDecoder} instead.\n */\n createFoaDecoderEffect(options: FoaDecoderOptions = {}): FoaDecoderEffect {\n return new FoaDecoderEffect(this, options);\n }\n\n /**\n * Creates an ITU-R BS.1770-5 {@link LoudnessMeter} that TAPS the output of a\n * target node without altering the audible path. The meter reports momentary\n * (400 ms), short-term (3 s), and gated integrated loudness in LKFS/LUFS, plus\n * true-peak level in dBTP (Annex 2 4× oversampling).\n *\n * The tap is a BRANCH: the target's output is connected to the metering\n * worklet in ADDITION to its existing forward edge, and the worklet's output\n * is routed through an owned zero-gain sink to keep the branch silent and live.\n * By default the target is the\n * master bus output (`master.output`), measuring everything that reaches the\n * destination — the integrated-loudness target. Pass a {@link Bus} to meter\n * one bus's output, or any {@link AudioNode} to meter that node.\n *\n * Lazily registers the `loudness-meter` worklet module (no-op if already\n * loaded) on the target's context, then constructs the node and branch-taps.\n *\n * @param target Node or bus whose output to meter. Defaults to the master bus.\n * @returns A {@link LoudnessMeter} handle exposing the live readings.\n */\n async createLoudnessMeter(target: Bus | AudioNode = this.master): Promise<LoudnessMeter> {\n const sourceNode: AudioNode = target instanceof Bus ? target.output : target;\n // Load AND construct the worklet on the SOURCE node's OWN context, not always\n // `this.context`. A caller may pass an AudioNode (or Bus) living on a\n // different BaseContext; building the meter on this host's context would\n // cross-connect two contexts (illegal in Web Audio). The source node carries\n // its full context at runtime (native + standardized-audio-context both do);\n // the structural `AudioNode.context` type is narrowed, so widen it here.\n const targetContext = (sourceNode.context as BaseContext | undefined) ?? this.context;\n await this.loadAudioWorkletModule(\n WORKLETS.loudnessMeter.name,\n WORKLETS.loudnessMeter.url,\n undefined,\n targetContext,\n );\n const node = await this.createWorkletNode(\n WORKLETS.loudnessMeter.name,\n WORKLETS.loudnessMeter.url,\n undefined,\n { numberOfInputs: 1, numberOfOutputs: 1 },\n targetContext,\n );\n const silentSink = targetContext.createGain();\n return new LoudnessMeter(node, sourceNode, silentSink, targetContext.destination);\n }\n\n /**\n * Creates a new {@link Bus}. If `name` is provided, the bus is registered\n * so {@link getBus} can look it up by name. Anonymous buses (no name) are\n * not registered and cannot be retrieved by name.\n *\n * @throws if a named bus with the same name already exists, or if the\n * reserved name 'master' is used (the master bus is auto-created).\n */\n createBus(name?: string): Bus {\n if (name === \"master\") {\n throw new Error(\"The name 'master' is reserved — use cacophony.master to access the master bus.\");\n }\n if (name !== undefined && this._busRegistry.has(name)) {\n throw new Error(`A bus named '${name}' already exists.`);\n }\n const onDestroy = name !== undefined ? () => this._busRegistry.delete(name) : undefined;\n const bus = new Bus(this.context, name ?? null, undefined, onDestroy);\n if (name !== undefined) {\n this._busRegistry.set(name, bus);\n }\n // New buses default-route to master so audio flows out somewhere unless\n // the user wires it elsewhere. master is intentionally not destroyable.\n bus.connect(this.master);\n return bus;\n }\n\n /**\n * Retrieves a bus by name from the named-bus registry. Returns `undefined`\n * if no bus with that name exists. The special name `'master'` returns\n * the auto-created master bus.\n */\n getBus(name: string): Bus | undefined {\n if (name === \"master\") {\n return this.master;\n }\n return this._busRegistry.get(name);\n }\n\n /**\n * Returns the names of every registered named bus. Anonymous buses are\n * not included. The master bus's name (`'master'`) is included.\n */\n listBuses(): string[] {\n return [\"master\", ...this._busRegistry.keys()];\n }\n\n createSplitter(numChannels: number = 2): ChannelSplitterNode {\n if (!this.context.createChannelSplitter) {\n throw new Error(\"ChannelSplitterNode not supported\");\n }\n return this.context.createChannelSplitter(numChannels);\n }\n\n createMerger(numChannels: number = 2): ChannelMergerNode {\n if (!this.context.createChannelMerger) {\n throw new Error(\"ChannelMergerNode not supported\");\n }\n return this.context.createChannelMerger(numChannels);\n }\n\n /**\n * Creates a PannerNode with the specified options.\n * @param {PannerOptions} options - An object containing the options to use when creating the PannerNode.\n * @returns {PannerNode} A new PannerNode instance with the specified options.\n * @example\n * const panner = audio.createPanner({\n * positionX: 0,\n * positionY: 0,\n * positionZ: 0,\n * orientationX: 0,\n * orientationY: 0,\n * orientationZ: 0,\n * });\n */\n\n createPanner({\n coneInnerAngle,\n coneOuterAngle,\n coneOuterGain,\n distanceModel,\n maxDistance,\n channelCount,\n channelCountMode,\n channelInterpretation,\n panningModel,\n refDistance,\n rolloffFactor,\n positionX,\n positionY,\n positionZ,\n orientationX,\n orientationY,\n orientationZ,\n }: Partial<PannerOptions>): PannerNode {\n const panner = this.context.createPanner();\n panner.coneInnerAngle = coneInnerAngle ?? 360;\n panner.coneOuterAngle = coneOuterAngle ?? 360;\n panner.coneOuterGain = coneOuterGain ?? 0;\n panner.distanceModel = distanceModel ?? \"inverse\";\n panner.maxDistance = maxDistance ?? 10000;\n panner.channelCount = channelCount ?? 2;\n panner.channelCountMode = channelCountMode ?? \"clamped-max\";\n panner.channelInterpretation = channelInterpretation ?? \"speakers\";\n panner.panningModel = panningModel ?? \"HRTF\";\n panner.refDistance = refDistance ?? 1;\n panner.rolloffFactor = rolloffFactor ?? 1;\n panner.positionX.value = positionX ?? 0;\n panner.positionY.value = positionY ?? 0;\n panner.positionZ.value = positionZ ?? 0;\n panner.orientationX.value = orientationX ?? 0;\n panner.orientationY.value = orientationY ?? 0;\n panner.orientationZ.value = orientationZ ?? 0;\n return panner;\n }\n\n /**\n * Suspends the audio context.\n *\n * Resolves after the underlying AudioContext transition completes, then\n * emits the `suspend` event. If the context is already suspended (per this\n * instance's view), resolves immediately as a no-op. If the underlying\n * `suspend()` call rejects, the rejection is propagated and no event fires.\n */\n async pause(): Promise<void> {\n if (!this.context.suspend) {\n return;\n }\n if (this.suspendState === \"suspended\") {\n return;\n }\n await this.context.suspend();\n this.suspendState = \"suspended\";\n this.emit(\"suspend\", undefined);\n }\n\n /**\n * Resumes the audio context.\n * This method is required to resume the audio context on mobile devices.\n * On desktop, the audio context will automatically resume when a sound is played.\n *\n * Resolves after the underlying AudioContext transition completes, then\n * emits the `resume` event. If the underlying `resume()` call rejects, the\n * rejection is propagated and no event fires.\n */\n async resume(): Promise<void> {\n if (!this.context.resume) {\n return;\n }\n await this.context.resume();\n this.suspendState = \"running\";\n this.emit(\"resume\", undefined);\n }\n\n setGlobalVolume(volume: number) {\n if (this.muted) {\n this.prevVolume = volume;\n return;\n }\n if (this.globalGainNode.gain.value === volume) {\n return;\n }\n this.globalGainNode.gain.value = volume;\n this.emit(\"volumeChange\", volume);\n }\n\n get volume(): number {\n return this.globalGainNode.gain.value;\n }\n\n set volume(volume: number) {\n if (this.muted) {\n this.prevVolume = volume;\n return;\n }\n this.setGlobalVolume(volume);\n }\n\n mute() {\n if (!this.muted) {\n this.prevVolume = this.globalGainNode.gain.value;\n this.isMuted = true;\n if (this.globalGainNode.gain.value !== 0) {\n this.globalGainNode.gain.value = 0;\n this.emit(\"volumeChange\", 0);\n }\n this.emit(\"mute\", undefined);\n }\n }\n\n unmute() {\n if (this.muted) {\n this.isMuted = false;\n this.setGlobalVolume(this.prevVolume);\n this.emit(\"unmute\", undefined);\n }\n }\n\n get muted(): boolean {\n return this.isMuted;\n }\n\n set muted(muted: boolean) {\n if (muted !== this.muted) {\n if (muted) {\n this.mute();\n } else {\n this.unmute();\n }\n }\n }\n\n async getMicrophoneStream(options: MicrophoneStreamOptions = {}): Promise<MicrophoneStream> {\n return MicrophoneStream.request(this.context, this.globalGainNode, options, this);\n }\n\n get listenerOrientation(): Orientation {\n return {\n forward: [...this.cachedListenerForward],\n up: [...this.cachedListenerUp],\n };\n }\n\n set listenerOrientation(orientation: Orientation) {\n const forward: Position = [orientation.forward[0], orientation.forward[1], orientation.forward[2]];\n const up: Position = [orientation.up[0], orientation.up[1], orientation.up[2]];\n writeListenerOrientation(this.listener, forward, up);\n this.cachedListenerForward = forward;\n this.cachedListenerUp = up;\n }\n\n get listenerUpOrientation(): Position {\n return [...this.cachedListenerUp];\n }\n\n set listenerUpOrientation(up: Position) {\n const nextUp: Position = [up[0], up[1], up[2]];\n writeListenerOrientation(this.listener, this.cachedListenerForward, nextUp);\n this.cachedListenerUp = nextUp;\n }\n\n get listenerForwardOrientation(): Position {\n return [...this.cachedListenerForward];\n }\n\n set listenerForwardOrientation(forward: Position) {\n const nextForward: Position = [forward[0], forward[1], forward[2]];\n writeListenerOrientation(this.listener, nextForward, this.cachedListenerUp);\n this.cachedListenerForward = nextForward;\n }\n\n get listenerPosition(): Position {\n return [...this.cachedListenerPosition];\n }\n\n set listenerPosition(position: Position) {\n const nextPosition: Position = [position[0], position[1], position[2]];\n writeListenerPosition(this.listener, nextPosition, this.context.currentTime);\n this.cachedListenerPosition = nextPosition;\n }\n}\n"],"mappings":"2hBgB0CA,IAAa,EAAW,CACtB,aAAc,CAAE,KAAM,gBAAiB,IAAK,kkiDAAsB,EAClE,WAAY,CAAE,KAAM,aAAc,IAAK,0r+CAAoB,EAC3D,eAAgB,CAAE,KAAM,kBAAmB,IAAK,0xjCAAwB,EACxE,iBAAkB,CAAE,KAAM,oBAAqB,IAAK,ktNAA0B,EAC9E,WAAY,CAAE,KAAM,aAAc,IAAK,8kQAAoB,EAC3D,cAAe,CAAE,KAAM,iBAAkB,IAAK,0kOAAuB,EACrE,gBAAiB,CAAE,KAAM,oBAAqB,IAAK,snfAAyB,EAC5E,eAAgB,CAAE,KAAM,kBAAmB,IAAK,sncAAwB,EACxE,SAAU,CAAE,KAAM,WAAY,IAAK,8wpBAAkB,EACrD,UAAW,CAAE,KAAM,aAAc,IAAK,8yrCAAmB,EACzD,WAAY,CAAE,KAAM,aAAc,IAAK,8odAAoB,EAC3D,eAAgB,CAAE,KAAM,kBAAmB,IAAK,kq1BAAwB,EACxE,UAAW,CAAE,KAAM,aAAc,IAAK,s8UAAmB,EACzD,OAAQ,CAAE,KAAM,SAAU,IAAK,kjiBAAgB,EAC/C,QAAS,CAAE,KAAM,UAAW,IAAK,8zbAAiB,EAClD,cAAe,CAAE,KAAM,iBAAkB,IAAK,kg5CAAuB,CACvE,EAGa,EAAyC,OAAO,OAAO,CAAQ,ECkCtD,EAAtB,KAAiF,CAE1D,KACF,QACE,QAHrB,YACE,EACA,EACA,EACA,CAHmB,KAAA,KAAA,EACF,KAAA,QAAA,EACE,KAAA,QAAA,CAClB,CAMH,gBAA0B,EAAoC,CAC5D,MAAO,CAAE,GAAG,CAAQ,CACtB,CAEA,MAAM,EAAiD,CACrD,OAAO,KAAK,KAAK,mBAAmB,KAAK,QAAS,KAAK,gBAAgB,KAAK,OAAO,EAAG,CAAO,CAC/F,CACF,EAQM,EAAwB,IAAI,QAMlC,SAAgB,EAAsB,EAA8B,CAClE,EAAsB,IAAI,CAAI,CAChC,CAOA,SAAgB,EAAuB,EAAe,EAAiD,CACrG,OACE,OAAO,GAAS,YAChB,GACA,EAAsB,IAAI,CAAwB,IACjD,IAAY,IAAA,IAAc,EAA0B,UAAY,EAErE,CAQA,IAAa,EAAb,KAAqD,CACtB,KAA7B,YAAY,EAAyC,CAAxB,KAAA,KAAA,CAAyB,CAEtD,MAAM,EAAkC,CACtC,OAAO,KAAK,IACd,CACF,EAOa,EAAb,KAA2D,CAC5B,SAA7B,YAAY,EAA6C,CAA5B,KAAA,SAAA,CAA6B,CAE1D,MAAM,EAAwC,CAC5C,IAAM,EAAO,EAAQ,mBAAmB,EAMxC,MALA,GAAK,KAAO,KAAK,SAAS,KAC1B,EAAK,UAAU,MAAQ,KAAK,SAAS,UAAU,MAC/C,EAAK,OAAO,MAAQ,KAAK,SAAS,OAAO,MACzC,EAAK,EAAE,MAAQ,KAAK,SAAS,EAAE,MAC/B,EAAK,KAAK,MAAQ,KAAK,SAAS,KAAK,MAC9B,CACT,CACF,EAUa,EAAb,KAAoD,CACrB,KAA7B,YAAY,EAAkC,CAAjB,KAAA,KAAA,CAAkB,CAE/C,MAAM,EAAkC,CACtC,OAAO,KAAK,IACd,CACF,EAqBa,EAAb,KAA8D,CAEzC,KACA,OACA,QAHnB,YACE,EACA,EACA,EAAmD,CAAC,EACpD,CAHiB,KAAA,KAAA,EACA,KAAA,OAAA,EACA,KAAA,QAAA,CAChB,CAEH,MAAM,MAAM,EAA4C,CACtD,GAAI,CAAC,EAAQ,gBACX,MAAU,MAAM,gDAAgD,EAGlE,IAAM,EACJ,OAAO,KAAK,QAAW,SACnB,MAAM,KAAK,KAAK,0BAA0B,KAAK,OAAQ,EAAS,KAAK,QAAQ,MAAM,EACnF,KAAK,OACL,EAAY,EAAQ,gBAAgB,EAC1C,EAAU,UAAY,KAAK,QAAQ,WAAa,GAChD,EAAU,OAAS,EAEnB,IAAM,EAAM,KAAK,QAAQ,KAAO,EAC1B,EAAM,KAAK,QAAQ,KAAO,EAChC,GAAI,IAAQ,GAAK,IAAQ,EACvB,OAAO,EAGT,IAAM,EAAQ,EAAQ,WAAW,EAC3B,EAAU,EAAQ,WAAW,EAC7B,EAAU,EAAQ,WAAW,EAC7B,EAAS,EAAQ,WAAW,EAUlC,MATA,GAAQ,KAAK,MAAQ,EACrB,EAAQ,KAAK,MAAQ,EAErB,EAAM,QAAQ,CAAO,EACrB,EAAQ,QAAQ,CAAM,EACtB,EAAM,QAAQ,CAAS,EACvB,EAAU,QAAQ,CAAO,EACzB,EAAQ,QAAQ,CAAM,EAEf,CACL,QACA,SACA,OAAQ,EACR,OAAQ,CAAE,IAAK,EAAQ,KAAM,IAAK,EAAQ,IAAK,EAC/C,YAAe,CACb,GAAI,CACF,EAAM,WAAW,CAAO,CAC1B,MAAQ,CAAC,CACT,GAAI,CACF,EAAQ,WAAW,CAAM,CAC3B,MAAQ,CAAC,CACT,GAAI,CACF,EAAM,WAAW,CAAS,CAC5B,MAAQ,CAAC,CACT,GAAI,CACF,EAAU,WAAW,CAAO,CAC9B,MAAQ,CAAC,CACT,GAAI,CACF,EAAQ,WAAW,CAAM,CAC3B,MAAQ,CAAC,CACX,CACF,CACF,CACF,EAmDa,EAAb,cAAkC,CAA6B,CAC7D,YAAY,EAAyB,EAAyB,CAAC,EAAG,CAChE,MAAM,EAAM,EAAS,eAAgB,CAAO,CAC9C,CACF,EAoCa,EAAb,cAAoC,CAA+B,CACjE,YAAY,EAAyB,EAA2B,CAAC,EAAG,CAClE,MAAM,EAAM,EAAS,SAAU,CAAO,CACxC,CACF,EAmCa,EAAb,cAAqC,CAAgC,CACnE,YAAY,EAAyB,EAA4B,CAAC,EAAG,CACnE,MAAM,EAAM,EAAS,UAAW,CAAO,CACzC,CACF,EAaM,EAA4B,CAAE,SAAU,EAAG,KAAM,CAAE,EAGnD,EAAyB,CAAE,KAAM,EAAG,SAAU,EAAG,OAAQ,CAAE,EAG3D,EAAyC,CAAE,MAAO,EAAG,OAAQ,CAAE,EASrE,SAAS,EAAiB,EAAoC,EAAmD,CAC3G,OAAU,IAAA,GAMd,OAHI,OAAO,GAAU,SACZ,EAEF,EAAM,IAAU,CACzB,CAuCA,IAAa,EAAb,cAAsC,CAAiC,CACrE,YAAY,EAAyB,EAA6B,CAAC,EAAG,CACpE,MAAM,EAAM,EAAS,WAAY,CAAO,CAC1C,CAEA,gBAAmC,EAAoD,CACrF,IAAM,EAAgB,CAAE,GAAG,CAAQ,EAC7B,EAAa,EAAiB,EAAQ,MAAO,CAAyB,EAI5E,OAHI,IAAe,IAAA,KACjB,EAAc,MAAQ,GAEjB,CACT,CACF,EA+Ca,EAAb,cAA0C,CAAqC,CAC7E,YAAY,EAAyB,EAAiC,CAAC,EAAG,CACxE,MAAM,EAAM,EAAS,eAAgB,CAAO,CAC9C,CAEA,gBAAmC,EAAwD,CACzF,IAAM,EAAgB,CAAE,GAAG,CAAQ,EAC7B,EAAqB,EAAiB,EAAQ,cAAe,CAAsC,EAIzG,OAHI,IAAuB,IAAA,KACzB,EAAc,cAAgB,GAEzB,CACT,CACF,EAsCa,EAAb,cAAkC,CAA6B,CAC7D,YAAY,EAAyB,EAAyB,CAAC,EAAG,CAChE,MAAM,EAAM,EAAS,OAAQ,CAAO,CACtC,CACF,EAwCa,EAAb,cAAmC,CAA8B,CAC/D,YAAY,EAAyB,EAA0B,CAAC,EAAG,CACjE,MAAM,EAAM,EAAS,QAAS,CAAO,CACvC,CAEA,gBAAmC,EAAiD,CAClF,IAAM,EAAgB,CAAE,GAAG,CAAQ,EAC7B,EAAa,EAAiB,EAAQ,MAAO,CAAsB,EAIzE,OAHI,IAAe,IAAA,KACjB,EAAc,MAAQ,GAEjB,CACT,CACF,EAWa,EAAb,cAA4C,CAAuC,CACjF,YAAY,EAAyB,EAAmC,CAAC,EAAG,CAC1E,MAAM,EAAM,EAAS,iBAAkB,CAAO,CAChD,CACF,EAea,EAAb,cAAsC,CAAiC,CACrE,YAAY,EAAyB,EAA6B,CAAC,EAAG,CACpE,MAAM,EAAM,EAAS,WAAY,CAAO,CAC1C,CACF,EAYa,EAAb,cAAsC,CAAiC,CACrE,YAAY,EAAyB,EAA6B,CAAC,EAAG,CACpE,MAAM,EAAM,EAAS,WAAY,CAAO,CAC1C,CACF,EAaa,EAAb,cAA0C,CAAqC,CAC7E,YAAY,EAAyB,EAAiC,CAAC,EAAG,CACxE,MAAM,EAAM,EAAS,eAAgB,CAAO,CAC9C,CACF,EAUa,EAAb,KAA4D,CAEvC,KACA,QAFnB,YACE,EACA,EAAiD,CAAC,EAClD,CAFiB,KAAA,KAAA,EACA,KAAA,QAAA,CAChB,CAEH,MAAM,EAAiD,CACrD,OAAO,KAAK,KAAK,mBACf,EAAS,cACT,CAAE,GAAG,KAAK,OAAQ,EAClB,EACA,CACE,eAAgB,EAChB,gBAAiB,EACjB,mBAAoB,CAAC,CAAC,EACtB,aAAc,EACd,iBAAkB,WAClB,sBAAuB,UACzB,CACF,CACF,CACF,EA4Fa,EAAb,MAAa,CAAW,CAEtB,MAEA,OACA,WAEA,YAAoB,EAAkB,EAAmB,EAAkC,CACzF,KAAK,MAAQ,EACb,KAAK,OAAS,EACd,KAAK,WAAa,CACpB,CAGA,SAAgB,CACd,IAAK,IAAM,KAAQ,KAAK,WACtB,GAAI,CACF,EAAK,WAAW,CAClB,MAAQ,CAAC,CAEb,CAOA,aAAa,OACX,EACA,EAA6B,CAAC,EAC9B,EACqB,CACrB,IAAM,EAAM,GAAW,EAAK,eAAe,EAC3C,GAAI,CAAC,EAAI,uBAAyB,CAAC,EAAI,qBAAuB,CAAC,EAAI,iBAAmB,CAAC,EAAI,aACzF,MAAU,MACR,kGACF,EAIF,IAAM,EAAO,EAAQ,MAAS,MAAM,EAAK,YAAY,CAAG,EACxD,GAAI,EAAK,iBAAmB,EAC1B,MAAU,MAAM,kEAAkE,EAAK,kBAAkB,EAI3G,IAAM,EAAQ,EAAI,sBAAsB,CAAC,EAGnC,EAAW,EAAI,oBAAoB,CAAC,EACpC,EAAW,EAAI,oBAAoB,CAAC,EAE1C,EAAM,QAAQ,EAAU,EAAG,CAAC,EAC5B,EAAM,QAAQ,EAAU,EAAG,CAAC,EAE5B,EAAM,QAAQ,EAAU,EAAG,CAAC,EAC5B,EAAM,QAAQ,EAAU,EAAG,CAAC,EAE5B,IAAM,EAAc,EAAI,gBAAgB,EAClC,EAAc,EAAI,gBAAgB,EAGxC,EAAY,UAAY,GACxB,EAAY,UAAY,GACxB,EAAY,OAAS,EAAW,cAAc,EAAK,EAAM,EAAG,CAAC,EAC7D,EAAY,OAAS,EAAW,cAAc,EAAK,EAAM,EAAG,CAAC,EAC7D,EAAS,QAAQ,CAAW,EAC5B,EAAS,QAAQ,CAAW,EAM5B,IAAM,EAAa,EAAI,sBAAsB,CAAC,EACxC,EAAa,EAAI,sBAAsB,CAAC,EAC9C,EAAY,QAAQ,CAAU,EAC9B,EAAY,QAAQ,CAAU,EAE9B,IAAM,EAAiB,EAAI,oBAAoB,CAAC,EAC1C,EAAiB,EAAI,WAAW,EACtC,EAAe,KAAK,MAAQ,GAG5B,EAAW,QAAQ,EAAgB,EAAG,CAAC,EACvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EAEvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EACvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EACvC,EAAe,QAAQ,EAAgB,EAAG,CAAC,EAE3C,EAAW,QAAQ,EAAgB,EAAG,CAAC,EACvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EAEvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EACvC,EAAW,QAAQ,EAAgB,EAAG,CAAC,EAEvC,IAAM,EAAS,EAAI,WAAW,EAG9B,OAFA,EAAe,QAAQ,CAAM,EAEtB,IAAI,EAAW,EAAO,EAAQ,CACnC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACF,CAAC,CACH,CAMA,OAAe,cAAc,EAAsB,EAAmB,EAAc,EAA2B,CAC7G,IAAM,EAAS,EAAQ,aAAc,EAAG,EAAK,OAAQ,EAAK,UAAU,EAGpE,OAFA,EAAO,cAAc,EAAK,eAAe,CAAI,EAAG,CAAC,EACjD,EAAO,cAAc,EAAK,eAAe,CAAI,EAAG,CAAC,EAC1C,CACT,CACF,EAOa,GAAb,KAAyD,CAEpC,KACA,QAFnB,YACE,EACA,EAA8C,CAAC,EAC/C,CAFiB,KAAA,KAAA,EACA,KAAA,QAAA,CAChB,CAEH,MAAM,MAAM,EAAiD,CAC3D,IAAM,EAAU,MAAM,EAAW,OAAO,KAAK,KAAM,KAAK,QAAS,CAAO,EACxE,MAAO,CACL,MAAO,EAAQ,MACf,OAAQ,EAAQ,OAChB,OAAQ,EAAQ,MAChB,YAAe,EAAQ,QAAQ,CACjC,CACF,CACF,EAEA,SAAS,EAAgB,EAAoC,CAC3D,OACE,OAAO,GAAU,YACjB,GACA,OAAQ,EAAoB,SAAY,YACxC,OAAQ,EAAoB,YAAe,UAE/C,CAKA,SAAgB,GAAmB,EAA2C,CAC5E,OACE,OAAO,GAAU,YACjB,GACA,UAAW,GACX,WAAY,GACZ,EAAiB,EAA6B,KAAK,GACnD,EAAiB,EAA8B,MAAM,CAEzD,CAKA,SAAgB,GAAc,EAAsC,CAClE,OAAO,EAAgB,CAAK,GAAK,GAAmB,CAAK,CAC3D,CAMA,SAAgB,GAAkB,EAA0C,CAC1E,OACE,OAAO,GAAU,YACjB,GACA,UAAW,GACX,OAAQ,EAA6B,OAAU,UAEnD,CCj/BA,IAAa,GAAb,KAAyB,CAOb,MACA,OACS,gBARnB,QAA8C,CAAC,EAC/C,SAA4B,IAAI,IAChC,MAAiE,CAAC,EAClE,UAAoB,GAEpB,YACE,EACA,EACA,EAAmC,cACnC,CAHQ,KAAA,MAAA,EACA,KAAA,OAAA,EACS,KAAA,gBAAA,EAEjB,KAAK,YAAY,EAAO,CAAM,CAChC,CAEA,IAAI,OAA8B,CAChC,OAAO,KAAK,QAAQ,OAAO,KAAK,OAAO,EAAE,IAAK,GAAU,EAAM,MAAM,CACtE,CAEA,IAAI,EAA0B,CAC5B,OAAO,KAAK,QAAQ,KAAM,GAAU,KAAK,QAAQ,CAAK,GAAK,EAAM,SAAW,CAAI,CAClF,CAEA,aAAa,EAAkB,EAAyB,CAClD,IAAU,KAAK,OAAS,IAAW,KAAK,SAG5C,KAAK,gBAAgB,EACrB,KAAK,MAAQ,EACb,KAAK,OAAS,EACd,KAAK,QAAQ,EACf,CAEA,IAAI,EAAoB,EAAQ,KAAK,QAAQ,OAAmB,CAC9D,GAAI,KAAK,UACP,MAAM,KAAK,WAAW,2CAA2C,EAEnE,IAAM,EAAQ,KAAK,UAAU,CAAK,EAClC,GAAI,KAAK,IAAI,EAAM,MAAM,EAEvB,MADA,EAAM,UAAU,EACV,KAAK,WAAW,uCAAuC,EAI/D,OAFA,KAAK,QAAQ,OAAO,EAAO,EAAG,CAAK,EACnC,KAAK,QAAQ,EACN,EAAM,MACf,CAGA,SAAkB,CAChB,GAAI,KAAK,UACP,MAAM,KAAK,WAAW,+CAA+C,EAEvE,IAAM,EAAc,OAAO,0BAA0B,EAErD,OADA,KAAK,QAAQ,KAAK,CAAE,aAAY,CAAC,EAC1B,CACT,CAGA,QAAQ,EAAqB,EAA+B,CAC1D,IAAM,EAAQ,KAAK,UAAU,CAAK,EAClC,GAAI,KAAK,UAEP,OADA,EAAM,UAAU,EACT,EAAM,OAEf,IAAM,EAAQ,KAAK,QAAQ,UACxB,GAAc,CAAC,KAAK,QAAQ,CAAS,GAAK,EAAU,cAAgB,CACvE,EACA,GAAI,IAAU,GAEZ,MADA,EAAM,UAAU,EACV,KAAK,WAAW,oDAAoD,EAE5E,GAAI,KAAK,IAAI,EAAM,MAAM,EAGvB,MAFA,KAAK,QAAQ,OAAO,EAAO,CAAC,EAC5B,EAAM,UAAU,EACV,KAAK,WAAW,uCAAuC,EAI/D,MAFA,MAAK,QAAQ,GAAS,EACtB,KAAK,QAAQ,EACN,EAAM,MACf,CAEA,OAAO,EAA2B,CAChC,IAAM,EAAQ,KAAK,QAAQ,UACxB,GAAc,CAAC,KAAK,QAAQ,CAAS,GAAK,EAAU,cAAgB,CACvE,EACI,IAAU,IACZ,KAAK,QAAQ,OAAO,EAAO,CAAC,CAEhC,CAEA,OAAO,EAAuB,CAC5B,IAAM,EAAQ,KAAK,QAAQ,UAAW,GAAU,KAAK,QAAQ,CAAK,GAAK,EAAM,SAAW,CAAI,EAC5F,GAAI,IAAU,GACZ,MAAM,KAAK,WAAW,8CAA8C,EAEtE,GAAM,CAAC,GAAS,KAAK,QAAQ,OAAO,EAAO,CAAC,EAC5C,KAAK,SAAS,OAAO,CAAI,EACzB,KAAK,QAAQ,EACT,GAAS,KAAK,QAAQ,CAAK,GAC7B,EAAM,UAAU,CAEpB,CAEA,SAAS,EAAmC,CAK1C,GAAI,EAHF,EAAM,SAAW,KAAK,MAAM,QAC5B,IAAI,IAAI,CAAK,EAAE,OAAS,EAAM,QAC9B,EAAM,MAAO,GAAS,KAAK,IAAI,CAAI,CAAC,GAEpC,MAAM,KAAK,WAAW,yDAAyD,EAEjF,IAAM,EAAU,EAAM,IAAK,GAAS,KAAK,QAAQ,KAAM,GAAU,KAAK,QAAQ,CAAK,GAAK,EAAM,SAAW,CAAI,CAAE,EAC3G,EAAe,EACnB,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,QAAQ,OAAQ,GAAS,EACpD,KAAK,QAAQ,KAAK,QAAQ,EAAO,IACnC,KAAK,QAAQ,GAAS,EAAQ,MAGlC,KAAK,QAAQ,CACf,CAEA,YAAY,EAAiB,EAAyB,CACpD,GAAI,CAAC,KAAK,IAAI,CAAI,EAChB,MAAM,KAAK,WAAW,8CAA8C,EAGlE,IADoB,KAAK,SAAS,IAAI,CACzB,IAGb,EACF,KAAK,SAAS,IAAI,CAAI,EAEtB,KAAK,SAAS,OAAO,CAAI,EAE3B,KAAK,QAAQ,EACf,CAEA,WAAW,EAA0B,CACnC,OAAO,KAAK,SAAS,IAAI,CAAI,CAC/B,CAEA,UAAU,EAAiB,EAAmB,EAAe,EAAwD,CACnH,IAAM,EAAQ,KAAK,QAAQ,OAAO,KAAK,OAAO,EAAE,KAAM,GAAc,EAAU,SAAW,CAAI,EAC7F,GAAI,CAAC,EAAO,CACV,QAAQ,KACN,GAAG,KAAK,gBAAgB,iDAAiD,KAAK,gBAAgB,YAAY,EAAE,4BAA4B,EAAU,GACpJ,EACA,MACF,CAEA,IAAM,EAAQ,EAAM,SAAS,IAAc,KAAK,kBAAkB,EAAM,CAAS,EACjF,GAAI,CAAC,EAAO,CACV,QAAQ,KACN,GAAG,KAAK,gBAAgB,kDAAkD,EAAU,+BACtF,EACA,MACF,CAEA,IAAM,EAAM,EAAK,QAAQ,YACnB,EAAW,GAAS,SAC1B,GAAI,IAAa,IAAA,IAAa,GAAY,EAAG,CAC3C,EAAM,eAAe,EAAO,CAAG,EAC/B,MACF,CAEA,IAAM,EAAU,EAAM,EAAW,IACjC,EAAM,eAAe,EAAM,MAAO,CAAG,EACjC,GAAS,OAAS,cACpB,EAAM,6BAA6B,IAAU,EAAI,KAAS,EAAO,CAAO,EAExE,EAAM,wBAAwB,EAAO,CAAO,CAEhD,CAEA,SAAgB,CACd,KAAK,UAAY,GACjB,KAAK,gBAAgB,EACrB,IAAK,IAAM,KAAS,KAAK,QACvB,GAAI,KAAK,QAAQ,CAAK,EACpB,GAAI,CACF,EAAM,UAAU,CAClB,MAAQ,CAAC,CAGb,KAAK,QAAQ,OAAS,EACtB,KAAK,SAAS,MAAM,CACtB,CAEA,SAAwB,CACtB,IAAM,EAAU,KAAK,aAAa,EAClC,IAAK,GAAM,CAAC,EAAQ,KAAgB,KAAK,MAIvC,GAAI,CAHiB,EAAQ,MAAM,CAAC,EAAe,KAC1C,IAAkB,GAAU,IAAuB,CAEvD,EACH,GAAI,CACF,EAAO,WAAW,CAAW,CAC/B,MAAQ,CAAC,CAGb,IAAK,GAAM,CAAC,EAAQ,KAAgB,EACT,KAAK,MAAM,MAAM,CAAC,EAAe,KACjD,IAAkB,GAAU,IAAuB,CAEvD,GACH,EAAO,QAAQ,CAAW,EAG9B,KAAK,MAAM,OAAS,EACpB,KAAK,MAAM,KAAK,GAAG,CAAO,CAC5B,CAEA,cAA+D,CAC7D,IAAM,EAAS,KAAK,QAAQ,OAAO,KAAK,OAAO,EAAE,OAAQ,GAAU,CAAC,KAAK,SAAS,IAAI,EAAM,MAAM,CAAC,EACnG,GAAI,EAAO,SAAW,EACpB,MAAO,CAAC,CAAC,KAAK,MAAO,KAAK,MAAM,CAAC,EAEnC,IAAM,EAAkD,CAAC,EACrD,EAAW,KAAK,MACpB,IAAK,IAAM,KAAS,EAClB,EAAQ,KAAK,CAAC,EAAU,EAAM,KAAK,CAAC,EACpC,EAAW,EAAM,OAGnB,OADA,EAAQ,KAAK,CAAC,EAAU,KAAK,MAAM,CAAC,EAC7B,CACT,CAEA,UAAkB,EAAsC,CACtD,GAAI,CAAC,GAAc,CAAK,EACtB,MAAM,KAAK,WAAW,2DAA2D,EAWnF,OATI,GAAmB,CAAK,EACnB,CACL,OAAQ,EAAM,QAAU,EAAM,MAC9B,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,QAAS,EAAM,OACjB,EAEK,CACL,OAAQ,EACR,MAAO,EACP,OAAQ,CACV,CACF,CAEA,QAAgB,EAAiD,CAC/D,MAAO,WAAY,CACrB,CAEA,WAAmB,EAAwB,CACzC,OAAW,MAAM,GAAG,KAAK,gBAAgB,IAAI,GAAS,CACxD,CAEA,kBAA0B,EAAiB,EAA2C,CACpF,IAAM,EAAc,EAA0B,WAC9C,GAAI,GAAc,OAAO,EAAW,KAAQ,WAAY,CACtD,IAAM,EAAe,EAAW,IAAI,CAAS,EAC7C,GAAI,KAAK,aAAa,CAAY,EAChC,OAAO,CAEX,CAEA,IAAM,EAAe,EAA4C,GACjE,GAAI,KAAK,aAAa,CAAW,EAC/B,OAAO,CAIX,CAEA,aAAqB,EAAqC,CACxD,OACE,OAAO,GAAU,YACjB,GACA,OAAQ,EAAqB,gBAAmB,YAChD,OAAQ,EAAqB,yBAA4B,YACzD,OAAQ,EAAqB,8BAAiC,UAElE,CAEA,YAAoB,EAAmB,EAA8B,CACnE,EAAO,QAAQ,CAAW,EAC1B,KAAK,MAAM,KAAK,CAAC,EAAQ,CAAW,CAAC,CACvC,CAEA,iBAAgC,CAC9B,IAAK,GAAM,CAAC,EAAQ,KAAgB,KAAK,MACvC,GAAI,CACF,EAAO,WAAW,CAAW,CAC/B,MAAQ,CAAC,CAEX,KAAK,MAAM,OAAS,CACtB,CACF,ECxQa,EAAb,MAAa,CAAI,CAEf,KAMA,MAMA,OAEA,SACA,aACA,WAAkE,IAAI,IACtE,mBAAgE,IAAI,IAQpE,eAAkC,IAAI,IAKtC,WACA,aACA,WAAqB,GAWrB,YACE,EACA,EAAsB,KACtB,EACA,EACA,EAAc,GACd,CACA,KAAK,SAAW,EAChB,KAAK,KAAO,EACZ,KAAK,MAAQ,GAAS,EAAQ,WAAW,EACzC,KAAK,OAAS,EAAQ,WAAW,EACjC,KAAK,WAAa,EAClB,KAAK,aAAe,EACpB,KAAK,aAAe,IAAI,GAAY,KAAK,MAAO,KAAK,OAAQ,KAAK,CACpE,CAGA,IAAI,WAAqB,CACvB,OAAO,KAAK,UACd,CAGA,IAAI,MAAe,CACjB,OAAO,KAAK,OAAO,KAAK,KAC1B,CAEA,IAAI,KAAK,EAAW,CAClB,KAAK,OAAO,KAAK,MAAQ,CAC3B,CAGA,IAAI,SAAgC,CAClC,OAAO,KAAK,aAAa,KAC3B,CAqBA,UAAU,EAAyE,CACjF,GAAI,CAAC,EAAuB,EAAK,KAAK,QAAQ,GAAK,CAAC,GAAkB,CAAG,EACvE,MAAU,MACR,4IACF,EAGF,IAAI,EACJ,GAAI,CACF,KAAK,kBAAkB,EACvB,EAAc,KAAK,aAAa,QAAQ,CAC1C,OAAS,EAAO,CACd,OAAO,QAAQ,OAAO,CAAK,CAC7B,CAEA,IAAI,EACJ,GAAI,CACF,EAAQ,EAAuB,EAAK,KAAK,QAAQ,EAAI,EAAM,EAAI,MAAM,KAAK,QAAQ,CACpF,OAAS,EAAO,CAEd,OADA,KAAK,aAAa,OAAO,CAAW,EAC7B,QAAQ,OAAO,CAAK,CAC7B,CAEA,GAAI,aAAiB,SAAY,OAAO,GAAU,UAAY,GAAkB,SAAU,EACxF,OAAO,QAAQ,QAAQ,CAAK,EAAE,KAC3B,GAAa,KAAK,aAAa,QAAQ,EAAa,CAAQ,EAC5D,GAAU,CAET,MADA,KAAK,aAAa,OAAO,CAAW,EAC9B,CACR,CACF,EAGF,GAAI,CACF,OAAO,QAAQ,QAAQ,KAAK,aAAa,QAAQ,EAAa,CAAK,CAAC,CACtE,OAAS,EAAO,CAEd,OADA,KAAK,aAAa,OAAO,CAAW,EAC7B,QAAQ,OAAO,CAAK,CAC7B,CACF,CAQA,aAAa,EAAuB,CAClC,KAAK,kBAAkB,EACvB,KAAK,aAAa,OAAO,CAAI,CAC/B,CAYA,eAAe,EAAmC,CAChD,KAAK,kBAAkB,EACvB,KAAK,aAAa,SAAS,CAAK,CAClC,CAmBA,kBAAkB,EAAiB,EAAyB,CAC1D,KAAK,kBAAkB,EACvB,KAAK,aAAa,YAAY,EAAM,CAAQ,CAC9C,CAMA,iBAAiB,EAA0B,CACzC,OAAO,KAAK,aAAa,WAAW,CAAI,CAC1C,CAoCA,gBACE,EACA,EACA,EACA,EACM,CACN,KAAK,kBAAkB,EACvB,KAAK,aAAa,UAAU,EAAM,EAAW,EAAO,CAAO,CAC7D,CAgBA,QAAQ,EAA6B,EAAqB,CACxD,KAAK,kBAAkB,EACvB,IAAM,EAAa,aAAkB,EAAM,EAAO,MAAQ,EAC1D,GAAI,IAAS,IAAA,IAAa,IAAS,EAAG,CACpC,GAAI,KAAK,mBAAmB,IAAI,CAAM,EACpC,OAIF,IAAM,EAAe,KAAK,WAAW,IAAI,CAAM,EAC/C,GAAI,EAAc,CAChB,GAAI,CACF,KAAK,OAAO,WAAW,CAAY,CACrC,MAAQ,CAAC,CACT,GAAI,CACF,EAAa,WAAW,CAC1B,MAAQ,CAAC,CACT,KAAK,WAAW,OAAO,CAAM,CAC/B,CACA,KAAK,OAAO,QAAQ,CAAU,EAC9B,KAAK,mBAAmB,IAAI,CAAM,EAClC,MACF,CACA,IAAM,EAAe,KAAK,WAAW,IAAI,CAAM,EAC/C,GAAI,EAAc,CAChB,EAAa,KAAK,MAAQ,EAC1B,MACF,CAGA,GAAI,KAAK,mBAAmB,IAAI,CAAM,EAAG,CACvC,GAAI,CACF,KAAK,OAAO,WAAW,CAAU,CACnC,MAAQ,CAAC,CACT,KAAK,mBAAmB,OAAO,CAAM,CACvC,CACA,IAAM,EAAW,KAAK,SAAS,WAAW,EAC1C,EAAS,KAAK,MAAQ,EACtB,KAAK,OAAO,QAAQ,CAAQ,EAC5B,EAAS,QAAQ,CAAU,EAC3B,KAAK,WAAW,IAAI,EAAQ,CAAQ,CACtC,CASA,WAAW,EAAmC,CAC5C,KAAK,kBAAkB,EACvB,IAAM,EAAa,aAAkB,EAAM,EAAO,MAAQ,EACpD,EAAW,KAAK,WAAW,IAAI,CAAM,EAC3C,GAAI,EAAU,CACZ,GAAI,CACF,KAAK,OAAO,WAAW,CAAQ,CACjC,MAAQ,CAAC,CACT,GAAI,CACF,EAAS,WAAW,CACtB,MAAQ,CAAC,CACT,KAAK,WAAW,OAAO,CAAM,CAC/B,CACA,GAAI,KAAK,mBAAmB,IAAI,CAAM,EAAG,CACvC,GAAI,CACF,KAAK,OAAO,WAAW,CAAU,CACnC,MAAQ,CAAC,CACT,KAAK,mBAAmB,OAAO,CAAM,CACvC,CACF,CAWA,sBAAsB,EAA+B,CAC/C,KAAK,YAGT,KAAK,eAAe,IAAI,CAAM,CAChC,CAQA,wBAAwB,EAA+B,CACrD,KAAK,eAAe,OAAO,CAAM,CACnC,CAUA,QAAQ,EAAmB,CAEzB,GADA,KAAK,kBAAkB,EACnB,IAAW,KACb,MAAU,MAAM,8BAA8B,EAEhD,IAAK,IAAM,IAAU,CAAC,GAAG,KAAK,cAAc,EAC1C,EAAO,cAAc,KAAM,CAAM,EAEnC,KAAK,eAAe,MAAM,CAC5B,CAaA,QAAQ,EAAmC,CACzC,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,oCAAoC,EAElD,SAAK,WAMT,CAHI,GAAS,SACX,KAAK,QAAQ,EAAQ,OAAO,EAE9B,KAAK,WAAa,GAElB,IAAK,GAAM,EAAG,KAAa,KAAK,WAC9B,GAAI,CACF,EAAS,WAAW,CACtB,MAAQ,CAAC,CAEX,KAAK,WAAW,MAAM,EACtB,KAAK,mBAAmB,MAAM,EAI9B,KAAK,aAAa,QAAQ,EAC1B,GAAI,CACF,KAAK,MAAM,WAAW,CACxB,MAAQ,CAAC,CACT,GAAI,CACF,KAAK,OAAO,WAAW,CACzB,MAAQ,CAAC,CACT,KAAK,aAAa,CAnBA,CAoBpB,CAEA,mBAAkC,CAChC,GAAI,KAAK,WACP,MAAU,MAAM,QAAQ,KAAK,MAAQ,cAAc,qBAAqB,CAE5E,CACF,EC/bA,SAAS,EAAQ,EAAuB,CACtC,OAAO,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CACjE,CAMA,SAAS,EAAQ,EAAqB,CACpC,MAAO,GAAG,EAAI,MAChB,CAEA,IAAM,GAAN,KAAgC,CAC9B,SACA,YAAsB,EACtB,MACA,cAEA,YAAY,EAAkB,EAAqC,CACjE,KAAK,SAAW,EAChB,KAAK,cAAgB,EACrB,KAAK,MAAQ,IAAI,GACnB,CAEA,IAAI,EAAuB,CACzB,IAAM,EAAQ,KAAK,MAAM,IAAI,CAAG,EAC5B,OAAU,IAAA,GAGd,OAFA,KAAK,MAAM,OAAO,CAAG,EACrB,KAAK,MAAM,IAAI,EAAK,CAAK,EAClB,EAAM,KACf,CAEA,IAAI,EAAQ,EAAgB,CAC1B,IAAM,EAAQ,KAAK,cAAc,CAAK,EAEtC,GAAI,KAAK,MAAM,IAAI,CAAG,IACpB,KAAK,aAAe,KAAK,MAAM,IAAI,CAAG,GAAG,OAAS,EAClD,KAAK,MAAM,OAAO,CAAG,GAGnB,IAAQ,KAAK,UAIjB,MAAO,KAAK,YAAc,EAAQ,KAAK,UAAU,CAC/C,IAAM,EAAQ,KAAK,MAAM,KAAK,EAAE,KAAK,EACrC,GAAI,EAAM,KACR,MAEF,IAAM,EAAW,EAAM,MACjB,EAAS,KAAK,MAAM,IAAI,CAAQ,EACtC,KAAK,aAAe,GAAQ,OAAS,EACrC,KAAK,MAAM,OAAO,CAAQ,CAC5B,CAEA,KAAK,MAAM,IAAI,EAAK,CAAE,QAAO,OAAM,CAAC,EACpC,KAAK,aAAe,CAHpB,CAIF,CACF,EAgBM,GAAqC,GAAK,KAAO,KACjD,GAA2B,EAEjC,SAAS,GAAyB,EAAkC,CAClE,OAAO,EAAY,OAAS,EAAY,iBAAmB,EAC7D,CAWA,SAAS,GAAY,EAAuD,CAC1E,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAQ,EAAmB,MAAM,kCAAkC,EACzE,GAAI,CAAC,EAAO,OAAO,KACnB,IAAM,EAAW,EAAM,IAAM,EAAM,GACnC,GAAI,IAAa,IAAA,GAAW,OAAO,KACnC,IAAM,EAAS,OAAO,SAAS,EAAU,EAAE,EAC3C,OAAO,OAAO,MAAM,CAAM,EAAI,KAAO,CACvC,CAOA,SAAS,GAAqB,EAAiD,CAI7E,OAHK,EAGE,2DAA2D,KAAK,CAAkB,EAFhF,EAGX,CAEA,SAAS,GAAoB,EAAiD,CAO5E,OANI,aAAiB,cAAgB,EAAM,OAAS,aAC3C,QAEL,aAAiB,MACZ,UAEF,SACT,CAOA,SAAS,GAAa,EAAyD,CAC7E,IAAM,EAAa,EAAI,QAAQ,GAAG,EAClC,GAAI,EAAa,EAAG,OAAO,KAC3B,IAAM,EAAS,EAAI,MAAM,EAAG,CAAU,EAChC,EAAU,EAAI,MAAM,EAAa,CAAC,EAClC,EAAW,YAAY,KAAK,CAAM,EAClC,GAAQ,EAAW,EAAO,QAAQ,YAAa,EAAE,EAAI,IAAW,aACtE,GAAI,CACF,GAAI,EAAU,CACZ,IAAM,EAAS,KAAK,CAAO,EACrB,EAAQ,IAAI,WAAW,EAAO,MAAM,EAC1C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IACjC,EAAM,GAAK,EAAO,WAAW,CAAC,EAEhC,MAAO,CAAE,OAAM,OAAM,CACvB,CACA,IAAM,EAAU,mBAAmB,CAAO,EACpC,EAAQ,IAAI,WAAW,EAAQ,MAAM,EAC3C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAClC,EAAM,GAAK,EAAQ,WAAW,CAAC,EAEjC,MAAO,CAAE,OAAM,OAAM,CACvB,MAAQ,CACN,OAAO,IACT,CACF,CA0CA,IAAa,GAAb,MAAa,CAA6B,CACxC,gBAA0B,IAAI,QAC9B,iBAA2B,IAAI,QAC/B,eAAyB,IAAI,QAC7B,OAAe,oBAA8B,KAAU,GAAK,IAE5D,OAAc,uBAAuB,EAAoB,CACvD,EAAW,oBAAsB,CACnC,CASA,OAAe,qBAA+B,CAC5C,OAAO,OAAO,OAAW,KAAe,QAAU,IACpD,CAEA,aAAqB,WAA4B,CAC/C,GAAI,CACF,OAAO,MAAM,OAAO,KAAK,aAAa,CACxC,OAAS,EAAO,CAEd,MADA,QAAQ,MAAM,wBAAyB,CAAK,EACtC,CACR,CACF,CAOA,OAAe,iBACb,EACA,EACA,EACA,EACM,CACN,IAAM,EAAY,GAAkB,IAAI,CAAG,EACvC,GACF,EAAU,QAAS,GAAgB,CACjC,IAAM,EAAW,EAAY,GAC7B,GAAI,EACF,GAAI,CAGF,EAA4C,CAAS,CACvD,OAAS,EAAO,CACd,QAAQ,MAAM,YAAY,OAAO,CAAY,EAAE,YAAa,CAAK,CACnE,CAEJ,CAAC,CAEL,CAEA,MAAc,0BACZ,EACA,EACA,EACA,EACA,EACsB,CACtB,GAAI,GAAQ,QACV,MAAM,IAAI,aAAa,wBAAyB,YAAY,EAG9D,IAAI,EAAkB,KAAK,gBAAgB,IAAI,CAAO,EAClD,IAAoB,IAAA,KACtB,EAAkB,IAAI,IACtB,KAAK,gBAAgB,IAAI,EAAS,CAAe,GAEnD,IAAI,EAAmB,KAAK,iBAAiB,IAAI,CAAO,EAOxD,GANI,IAAqB,IAAA,KACvB,EAAmB,IAAI,IACvB,KAAK,iBAAiB,IAAI,EAAS,CAAgB,GAIjD,EAAW,CACb,IAAM,EAAoB,EAAiB,IAAI,CAAG,GAAK,CAAC,EACxD,EAAkB,KAAK,CAAS,EAChC,EAAiB,IAAI,EAAK,CAAiB,CAC7C,CAEA,IAAI,EAAiB,EAAgB,IAAI,CAAG,EAC5C,GAAI,CAAC,EAAgB,CACnB,IAAM,EAAa,IAAI,gBAgBvB,EAAiB,CAAE,SAfK,SAAY,CAClC,GAAI,CACF,IAAM,EAAS,MAAM,EAAc,EAAW,MAAM,EACpD,GAAI,IAAW,IAAA,GACb,MAAU,MAAM,gCAAgC,EAElD,OAAO,CACT,QAAU,CACJ,EAAgB,IAAI,CAAG,IAAM,IAC/B,EAAgB,OAAO,CAAG,EAC1B,EAAiB,OAAO,CAAG,EAE/B,CACF,GAE4B,EAAgB,aAAY,QAAS,CAAE,EACnE,EAAgB,IAAI,EAAK,CAAc,CACzC,CAGA,MADA,GAAe,UACR,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAU,GACR,EAAW,GAAqB,CAChC,MAKJ,IAJA,EAAU,GACV,GAAQ,oBAAoB,QAAS,CAAW,EAChD,EAAe,UAEX,GAAW,EAAW,CACxB,IAAM,EAAsB,EAAiB,IAAI,CAAG,EAC9C,EAAgB,GAAqB,QAAQ,CAAS,GAAK,GAC7D,GAAiB,GACnB,GAAqB,OAAO,EAAe,CAAC,CAEhD,CAEI,GAAW,EAAe,UAAY,GAAK,EAAgB,IAAI,CAAG,IAAM,IAC1E,EAAgB,OAAO,CAAG,EAC1B,EAAiB,OAAO,CAAG,EAC3B,EAAe,WAAW,MAAM,EALlC,CAOF,EACM,MAAoB,CACxB,EAAQ,EAAI,EACZ,EAAO,IAAI,aAAa,wBAAyB,YAAY,CAAC,CAChE,EAEA,GAAQ,iBAAiB,QAAS,EAAa,CAAE,KAAM,EAAK,CAAC,EAC7D,EAAe,QAAQ,KACpB,GAAW,CACN,IACJ,EAAQ,EAAK,EACb,EAAQ,CAAM,EAChB,EACC,GAAmB,CACd,IACJ,EAAQ,EAAK,EACb,EAAO,CAAK,EACd,CACF,CACF,CAAC,CACH,CAEA,aAAqB,eAAe,EAAc,EAAa,EAA6C,CAC1G,IAAM,EAA0B,CAC9B,MACA,UAAW,KAAK,IAAI,EACpB,GAAG,CACL,EAEA,MAAM,EAAM,IACV,EAAQ,CAAG,EACX,IAAI,SAAS,KAAK,UAAU,CAAQ,EAAG,CACrC,QAAS,CAAE,eAAgB,kBAAmB,CAChD,CAAC,CACH,CACF,CAEA,aAAqB,mBAAmB,EAAa,EAA2C,CAC9F,GAAI,CACF,IAAM,EAAW,MAAM,EAAM,MAAM,CAAG,EAItC,OAHI,GAAU,GACL,MAAM,EAAS,YAAY,EAE7B,IACT,OAAS,EAAO,CAEd,OADA,QAAQ,MAAM,iCAAkC,CAAK,EAC9C,IACT,CACF,CAEA,MAAc,oBACZ,EACA,EACA,EACA,EACA,EACA,EACsB,CACtB,IAAM,EAAO,GAAY,KACnB,EAAe,GAAY,aAC3B,EAAU,IAAI,QAChB,GAAM,EAAQ,OAAO,gBAAiB,CAAI,EAC1C,GAAc,EAAQ,OAAO,oBAAqB,CAAY,EAElE,QAAQ,MAAM,yBAAyB,IAAO,CAC5C,QAAS,OAAO,YAAY,EAAQ,QAAQ,CAAC,EAC7C,QAAS,CAAC,CAAC,EACX,gBAAiB,CAAC,CAAC,CACrB,CAAC,EAED,IAAM,EAAgB,MAAM,MAAM,EAAK,CAAE,UAAS,QAAO,CAAC,EAU1D,GARA,QAAQ,MAAM,yBAAyB,IAAO,CAC5C,OAAQ,EAAc,OACtB,WAAY,EAAc,WAC1B,KAAM,EAAc,SAAS,IAAI,MAAM,EACvC,aAAc,EAAc,SAAS,IAAI,eAAe,EACxD,aAAc,EAAc,SAAS,IAAI,eAAe,CAC1D,CAAC,EAEG,EAAc,SAAW,IAAK,CAChC,IAAM,EAAiB,MAAM,EAAM,MAAM,CAAG,EAC5C,GAAI,EAAgB,CAElB,GAAI,GAAW,WAAY,CACzB,IAAM,EAAuB,CAC3B,MACA,UAAW,cACX,UAAW,KAAK,IAAI,CACtB,EACA,EAAU,WAAW,CAAK,CAC5B,CAGA,IAAM,EAAY,KAAK,IAAI,EACrB,EAAkB,EAAc,SAAS,IAAI,eAAe,EAQlE,OAPA,MAAM,EAAW,eAAe,EAAO,EAAK,CAC1C,YACA,OACA,eAEA,GAAI,EAAkB,CAAE,aAAc,CAAgB,EAAI,CAAC,CAC7D,CAAC,EACM,MAAM,EAAe,YAAY,CAC1C,KAAO,CAIL,QAAQ,KAAK,oCAAoC,EAAI,gDAAgD,EAGrG,IAAM,EAAgB,MAAM,MAAM,EAAK,CAAE,QAAO,CAAC,EACjD,GAAI,EAAc,SAAW,IAAK,CAChC,IAAM,EAAgB,EAAc,MAAM,EACpC,EAAU,EAAc,QAAQ,IAAI,MAAM,EAC1C,EAAkB,EAAc,QAAQ,IAAI,eAAe,EAC3D,EAAkB,EAAc,QAAQ,IAAI,eAAe,EAUjE,GARA,MAAM,EAAW,uBAAuB,EAAO,EAAK,EAAe,CACjE,UAAW,KAAK,IAAI,EACpB,KAAM,GAAW,IAAA,GACjB,aAAc,GAAmB,IAAA,GACjC,aAAc,GAAmB,IAAA,EACnC,CAAC,EAGG,EAAc,KAAM,CACtB,GAAM,CAAE,SAAQ,SAAU,KAAK,6BAA6B,EAAS,EAAe,EAAK,CAAM,EAC/F,OAAO,MAAM,EAAW,2BAA2B,EAAQ,GAAS,IAAA,EAAS,CAC/E,MAEE,OAAO,MAAM,EAAc,YAAY,CAE3C,MACE,MAAU,MACR,uDAAuD,EAAc,OAAO,GAAG,EAAc,YAC/F,CAEJ,CACF,CAEA,GAAI,EAAc,SAAW,IAAK,CAChC,IAAM,EAAgB,EAAc,MAAM,EACpC,EAAU,EAAc,QAAQ,IAAI,MAAM,EAC1C,EAAkB,EAAc,QAAQ,IAAI,eAAe,EAC3D,EAAkB,EAAc,QAAQ,IAAI,eAAe,EAEjE,MAAM,EAAW,uBAAuB,EAAO,EAAK,EAAe,CACjE,UAAW,KAAK,IAAI,EACpB,KAAM,GAAW,IAAA,GACjB,aAAc,GAAmB,IAAA,GACjC,aAAc,GAAmB,IAAA,EACnC,CAAC,CACH,CAEA,GAAI,GAAQ,QACV,MAAM,IAAI,aAAa,wBAAyB,YAAY,EAI9D,GAAI,EAAc,KAAM,CACtB,GAAM,CAAE,SAAQ,SAAU,KAAK,6BAA6B,EAAS,EAAe,EAAK,CAAM,EAC/F,OAAO,MAAM,EAAW,2BAA2B,EAAQ,GAAS,IAAA,EAAS,CAC/E,MAEE,OAAO,MAAM,EAAc,YAAY,CAE3C,CAOA,aAAqB,uBACnB,EACA,EACA,EACA,EACe,CACf,GAAI,CACF,MAAM,QAAQ,IAAI,CAAC,EAAM,IAAI,EAAK,CAAQ,EAAG,EAAW,eAAe,EAAO,EAAK,CAAQ,CAAC,CAAC,CAC/F,OAAS,EAAO,CAGd,MADA,MAAM,QAAQ,WAAW,CAAC,EAAM,OAAO,CAAG,EAAG,EAAM,OAAO,EAAQ,CAAG,CAAC,CAAC,CAAC,EAClE,CACR,CACF,CAYA,6BACE,EACA,EACA,EACA,EAC8D,CAE9D,IAAM,EAAsB,EAAS,QAAQ,IAAI,gBAAgB,EAC3D,EAAQ,EAAsB,OAAO,SAAS,EAAqB,EAAE,EAAI,KAE3E,EAAS,EAEb,GAAI,CAAC,EAAS,KAEZ,MAAU,MAAM,uBAAuB,EAGzC,IAAM,EAAS,EAAS,KAAK,UAAU,EACjC,EAAmB,KAAK,iBAAiB,IAAI,CAAO,EAoD1D,MAAO,CAAE,OAAA,IAlDU,eAA2B,CAC5C,MAAM,MAAM,EAAY,CACtB,GAAI,CACF,OAAa,CACX,GAAI,GAAQ,QACV,MAAM,IAAI,aAAa,wBAAyB,YAAY,EAE9D,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,CAER,EAAW,iBAAiB,EAAkB,EAAK,oBAAqB,CACtE,MACA,SACA,QACA,SAAU,EAAQ,EAAI,GACtB,UAAW,KAAK,IAAI,CACtB,CAAgC,EAChC,EAAW,MAAM,EACjB,MACF,CAEA,GAAI,EAAO,CACT,GAAU,EAAM,WAGhB,IAAM,EAAW,EAAQ,EAAS,EAAQ,GAC1C,EAAW,iBAAiB,EAAkB,EAAK,oBAAqB,CACtE,MACA,SACA,QACA,WACA,UAAW,KAAK,IAAI,CACtB,CAAgC,EAEhC,EAAW,QAAQ,CAAK,CAC1B,CACF,CACF,OAAS,EAAO,CACd,EAAW,MAAM,CAAK,CACxB,QAAU,CACR,EAAO,YAAY,CACrB,CACF,EAEA,OAAO,EAAQ,CAEb,OAAO,EAAO,OAAO,CAAM,CAC7B,CACF,CAES,EAAQ,OAAM,CACzB,CAOA,aAAqB,2BACnB,EACA,EACsB,CACtB,IAAM,EAAS,EAAO,UAAU,EAEhC,GAAI,CACF,GAAI,IAAgB,IAAA,IAAa,EAAc,EAAG,CAEhD,IAAM,EAAS,IAAI,YAAY,CAAW,EACpC,EAAY,IAAI,WAAW,CAAM,EACnC,EAAS,EAEb,OAAa,CACX,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEN,IACF,EAAU,IAAI,EAAO,CAAM,EAC3B,GAAU,EAAM,WAEpB,CAEA,OAAO,CACT,KAAO,CAEL,IAAI,EAAS,IAAI,WAAW,IAAI,EAC5B,EAAc,EAElB,OAAa,CACX,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEV,GAAI,EAAO,CAET,GAAI,EAAc,EAAM,WAAa,EAAO,OAAQ,CAClD,IAAM,EAAU,KAAK,IAAI,EAAO,OAAS,EAAG,EAAc,EAAM,UAAU,EACpE,EAAY,IAAI,WAAW,CAAO,EACxC,EAAU,IAAI,EAAO,SAAS,EAAG,CAAW,CAAC,EAC7C,EAAS,CACX,CAEA,EAAO,IAAI,EAAO,CAAW,EAC7B,GAAe,EAAM,UACvB,CACF,CAGA,OAAO,EAAO,MAAM,EAAG,CAAW,EAAE,MACtC,CACF,QAAU,CACR,EAAO,YAAY,CACrB,CACF,CAYA,MAAc,2BACZ,EACA,EACA,EACsB,CACtB,IAAM,EAAgB,MAAM,MAAM,EAAK,CAAE,QAAO,CAAC,EAEjD,GAAI,EAAc,SAAW,KAAO,CAAC,EAAc,GACjD,MAAU,MAAM,6BAA6B,EAAc,OAAO,GAAG,EAAc,YAAY,EAGjG,GAAI,GAAQ,QACV,MAAM,IAAI,aAAa,wBAAyB,YAAY,EAI9D,GAAI,EAAc,KAAM,CACtB,GAAM,CAAE,SAAQ,SAAU,KAAK,6BAA6B,EAAS,EAAe,EAAK,CAAM,EAC/F,OAAO,MAAM,EAAW,2BAA2B,EAAQ,GAAS,IAAA,EAAS,CAC/E,CAEA,OAAO,MAAM,EAAc,YAAY,CACzC,CAQA,MAAc,oBACZ,EACA,EACA,EACA,EACA,EACA,EAAmB,GACG,CACtB,GAAI,CACF,IAAM,EAAc,MAAM,EAAY,EAClC,EACJ,GAAI,CACF,EAAc,MAAM,EAAW,gBAAgB,EAAS,CAAW,CACrE,OAAS,EAAO,CAOd,MANA,GAAW,iBAAiB,CAC1B,MACA,MAAO,EAAQ,CAAK,EACpB,UAAW,SACX,UAAW,KAAK,IAAI,CACtB,CAAC,EACK,CACR,CASA,OAPA,GAAW,oBAAoB,CAC7B,MACA,SAAU,EAAY,SACtB,KAAM,EAAY,WAClB,UAAW,KAAK,IAAI,CACtB,CAAC,EACD,EAAe,IAAI,EAAK,CAAW,EAC5B,CACT,OAAS,EAAO,CAed,MAdA,GAAW,iBAAiB,CAC1B,MACA,MAAO,EAAQ,CAAK,EACpB,UAAW,GAAoB,CAAK,EACpC,UAAW,KAAK,IAAI,CACtB,CAAC,EACG,GACF,GAAW,eAAe,CACxB,MACA,MAAO,EAAQ,CAAK,EACpB,UAAW,MACX,UAAW,KAAK,IAAI,CACtB,CAAC,EAEG,CACR,CACF,CAEA,aAAqB,gBAAgB,EAAsB,EAAgD,CACzG,GAAI,CACF,OAAO,MAAM,EAAQ,gBAAgB,CAAW,CAClD,OAAS,EAAO,CAEd,MADA,QAAQ,MAAM,+BAAgC,CAAK,EAC7C,CACR,CACF,CAEA,aAAqB,qBAAqB,EAAa,EAA6C,CAClG,GAAI,CACF,IAAM,EAAe,MAAM,EAAM,MAAM,EAAQ,CAAG,CAAC,EAInD,OAHI,GAAc,GACT,MAAM,EAAa,KAAK,EAE1B,IACT,OAAS,EAAO,CAEd,OADA,QAAQ,MAAM,qCAAsC,CAAK,EAClD,IACT,CACF,CAoBA,MAAa,eACX,EACA,EACA,EACA,EACsB,CACtB,IAAI,EAAiB,KAAK,eAAe,IAAI,CAAO,EAChD,IAAmB,IAAA,KACrB,EAAiB,IAAI,GACnB,GACA,EACF,EACA,KAAK,eAAe,IAAI,EAAS,CAAc,GAI7C,GAAW,gBACb,EAAU,eAAe,CACvB,MACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAKH,IAAM,EAAY,EAAe,IAAI,CAAG,EACxC,GAAI,IAAc,IAAA,GAQhB,OAPI,GAAW,YACb,EAAU,WAAW,CACnB,MACA,UAAW,SACX,UAAW,KAAK,IAAI,CACtB,CAAC,EAEI,EAIT,GAAI,EAAI,WAAW,OAAO,EAAG,CAC3B,IAAM,EAAS,GAAa,CAAG,EAC/B,GAAI,IAAW,KAAM,CACnB,IAAM,EAAY,MAAM,wBAAwB,EAAI,MAAM,EAAG,EAAE,EAAE,EAAE,EASnE,MARI,GAAW,gBACb,EAAU,eAAe,CACvB,MACA,QACA,UAAW,SACX,UAAW,KAAK,IAAI,CACtB,CAAC,EAEG,CACR,CACA,IAAM,EAAc,MAAM,EAAW,gBAAgB,EAAS,EAAO,MAAM,MAAqB,EAEhG,OADA,EAAe,IAAI,EAAK,CAAW,EAC5B,CACT,CAMA,GAAI,CAAC,EAAW,oBAAoB,EAClC,OAAO,KAAK,0BACV,EACA,EACA,KAAO,KAED,GAAW,aACb,EAAU,YAAY,CACpB,MACA,OAAQ,YACR,UAAW,KAAK,IAAI,CACtB,CAAC,EAGI,MAAM,KAAK,oBAChB,EACA,EACA,MACM,KAAK,2BAA2B,EAAS,EAAK,CAAa,EACjE,CACF,GAEF,EACA,CAAE,kBAAmB,GAAW,iBAAkB,CACpD,EAGF,IAAM,EAAQ,MAAM,EAAW,UAAU,EAEnC,EAAW,MAAM,EAAW,qBAAqB,EAAK,CAAK,EAO3D,OAAqB,CAMzB,GALI,CAAC,GAKD,GAAqB,EAAS,YAAY,EAC5C,MAAO,GAIT,IAAM,EAAS,GAAY,EAAS,YAAY,EAChD,GAAI,IAAW,KAAM,CACnB,IAAM,GAAO,KAAK,IAAI,EAAI,EAAS,WAAa,IAChD,GAAI,EAAS,GAAK,EAAM,EACtB,MAAO,EAGX,CAQA,OALI,EAAS,MAAQ,EAAS,aACrB,GAIF,KAAK,IAAI,EAAI,EAAS,UAAY,EAAW,mBACtD,GAAG,EAEH,OAAO,KAAK,0BACV,EACA,EACA,KAAO,IAAkB,CACvB,GAAI,EAUF,OARI,GAAW,aACb,EAAU,YAAY,CACpB,MACA,OAAQ,EAAW,UAAY,YAC/B,UAAW,KAAK,IAAI,CACtB,CAAC,EAGI,MAAM,KAAK,oBAChB,EACA,EACA,MAEE,KAAK,oBACH,EACA,EACA,EACA,CAAE,KAAM,GAAU,KAAM,aAAc,GAAU,YAAa,EAC7D,EACA,CAAE,WAAY,GAAW,UAAW,CACtC,EACF,EACA,EACF,EACK,CAEL,IAAM,EAAe,MAAM,EAAW,mBAAmB,EAAK,CAAK,EACnE,GAAI,EAAc,CAEZ,GAAW,YACb,EAAU,WAAW,CACnB,MACA,UAAW,UACX,UAAW,KAAK,IAAI,CACtB,CAAC,EAGH,IAAM,EAAc,MAAM,EAAW,gBAAgB,EAAS,CAAY,EAE1E,OADA,EAAe,IAAI,EAAK,CAAW,EAC5B,CACT,MAYE,OAVI,GAAW,cACb,EAAU,aAAa,CACrB,MACA,MAAW,MAAM,0DAA0D,EAC3E,UAAW,MACX,UAAW,KAAK,IAAI,CACtB,CAAC,EAII,MAAM,KAAK,oBAChB,EACA,EACA,MAEE,KAAK,oBACH,EACA,EACA,EACA,CAAE,KAAM,GAAU,KAAM,aAAc,GAAU,YAAa,EAC7D,EACA,CAAE,WAAY,GAAW,UAAW,CACtC,EACF,CACF,CAEJ,CACF,EACA,EACA,CAAE,kBAAmB,GAAW,iBAAkB,CACpD,CACF,CAEA,kBAAgC,CAC9B,KAAK,eAAiB,IAAI,QAC1B,KAAK,gBAAkB,IAAI,QAC3B,KAAK,iBAAmB,IAAI,OAC9B,CACF,EClgCA,GAAe,iwFCwBT,GAAsE,CAAC,WAAY,QAAS,SAAS,EACrG,GAA2B,CAAE,QAAS,GAAM,QAAS,EAAK,EA6ChE,SAAgB,GAAsB,EAAyC,CAC7E,GAAM,CAAE,UAAS,YAAa,EAG9B,GAAI,OAAO,SAAa,IACtB,UAAa,CAAC,EAShB,IAAM,EAAU,EAAwD,OACxE,GAAI,OAAO,GAAW,WACpB,UAAa,CAAC,EAGhB,IAAM,EAAM,SACN,EAA2B,EAAI,MAA+C,EAC9E,EAAoB,EAMtB,EAAQ,GACR,EAAiB,GACjB,EAAW,GAET,MAAkB,CACjB,KACL,KAAK,IAAM,KAAQ,GACjB,EAAO,oBAAoB,EAAM,EAAS,EAAwB,EAEpE,EAAQ,EAF4D,CAGtE,EAEM,MAAgB,CAChB,QAAY,GAKhB,CAJA,EAAiB,GAIjB,EAAU,EAOV,GAAI,CASF,IAAM,EAAS,EAAoB,aAAa,EAAG,EAAG,EAAQ,UAAU,EAClE,EAAS,EAAQ,mBAAmB,EAC1C,EAAO,OAAS,EAChB,EAAO,QAAQ,EAAQ,WAAW,EAIlC,EAAO,MAAM,CAAC,EACd,EAAO,KAAK,CAAC,CACf,OAAS,EAAK,CAIZ,QAAQ,KAAK,4CAA6C,CAAG,CAC/D,CAOA,EAAO,KAAK,CAAO,EAAE,SACb,CACJ,KAAiB,GACb,GACJ,GAAI,CACF,EAAS,CACX,OAAS,EAAK,CACZ,QAAQ,MAAM,sDAAuD,CAAG,CAC1E,CACF,EACC,GAAiB,CAChB,EAAiB,GACb,KACJ,QAAQ,KAAK,4CAA6C,CAAG,EAC7D,EAAqB,EACvB,CACF,CArDU,CAsDZ,EAEM,MAAe,CACf,QAAY,GAAS,GACzB,KAAK,IAAM,KAAQ,GACjB,EAAO,iBAAiB,EAAM,EAAS,EAAwB,EAEjE,EAAQ,EAFyD,CAGnE,EAEA,SAAS,GAA6B,CAChC,EAAkB,QAAU,YAC9B,EAAO,EAEP,EAAU,CAEd,CAEA,IAAM,MAA0B,CACzB,GAAU,EAAqB,CACtC,EAKA,OAHA,EAAkB,mBAAmB,cAAe,CAAiB,EACrE,EAAqB,MAER,CACP,IACJ,EAAW,GACX,EAAU,EACV,EAAkB,sBAAsB,cAAe,CAAiB,EAC1E,CACF,CCxLA,IAAa,EAAb,KAAmD,CACjD,UAAsC,CAAC,EASvC,GAA0B,EAAc,EAAqC,CAG3E,OAFc,KAAK,UAAU,KAAe,CAAC,GACxC,KAAK,CAAE,KAAI,KAAM,EAAM,CAAC,MAChB,KAAK,IAAI,EAAW,CAAE,CACrC,CAQA,KAA4B,EAAc,EAAqC,CAG7E,OAFc,KAAK,UAAU,KAAe,CAAC,GACxC,KAAK,CAAE,KAAI,KAAM,EAAK,CAAC,MACf,KAAK,IAAI,EAAW,CAAE,CACrC,CAKA,IAA2B,EAAc,EAA+B,CACtE,IAAM,EAAY,KAAK,UAAU,GAC7B,IACF,KAAK,UAAU,GAAa,EAAU,OAAQ,GAAa,EAAS,KAAO,CAAE,EAEjF,CAeA,KAA4B,EAAc,EAAoB,CAC5D,IAAM,EAAS,KAAK,UAAU,GAC9B,GAAI,CAAC,GAAU,EAAO,SAAW,EAAG,OAKpC,IAAM,EAAO,EAAO,MAAM,EACpB,EAAO,EAAO,OAAQ,GAAa,CAAC,EAAS,IAAI,EACvD,KAAK,UAAU,GAAa,EAC5B,IAAK,IAAM,KAAY,EACrB,EAAS,GAAG,CAAM,CAEtB,CAUA,UAAiC,EAAc,EAA6B,CAC1E,IAAM,EAAS,KAAK,UAAU,GAC9B,GAAI,CAAC,GAAU,EAAO,SAAW,EAAG,OAAO,QAAQ,QAAQ,EAC3D,IAAM,EAAO,EAAO,MAAM,EACpB,EAAO,EAAO,OAAQ,GAAa,CAAC,EAAS,IAAI,EACvD,KAAK,UAAU,GAAa,EAC5B,IAAM,EAAW,EAAK,IAAK,GACzB,QAAQ,QAAQ,EACb,SAAW,EAAS,GAAG,CAAM,CAAC,EAC9B,MAAO,GAAU,QAAQ,MAAM,+BAA+B,EAAU,GAAI,CAAK,CAAC,CACvF,EACA,OAAO,QAAQ,IAAI,CAAQ,EAAE,SAAW,CAAC,CAAC,CAC5C,CAKA,oBAA2B,CACzB,KAAK,UAAY,CAAC,CACpB,CACF,ECxGa,EAAb,KAAwC,CAKnB,OAJnB,UAA8B,CAAC,EAAG,EAAG,CAAC,EACtC,UAAuB,EACvB,UAA4B,EAE5B,YAAY,EAAyB,CAAC,EAAG,CAAtB,KAAA,OAAA,CAAuB,CAM1C,eAAsC,CACpC,GAAI,KAAK,OAAO,SAAW,EACzB,OAGF,IAAM,EADc,KAAK,YACP,EAAY,QAAQ,EACtC,OAAO,EAAU,OAAS,EAAI,EAAU,GAAK,IAAA,EAC/C,CAOA,WAAW,EAA6C,CAClD,QAAK,OAAO,SAAW,EAG3B,OAAO,KAAK,YAAY,EAAE,KAAK,CAAO,EAAE,EAC1C,CAQA,eAAe,EAAsB,GAA4B,CAI/D,GAHI,KAAK,OAAO,SAAW,GAGvB,KAAK,WAAa,KAAK,OAAO,OAChC,OAGF,IAAM,EADQ,KAAK,OAAO,KAAK,WACP,QAAQ,EAC5B,KAAU,SAAW,EAOzB,MAJA,MAAK,WAAa,KAAK,UAAY,GAAK,KAAK,OAAO,OAChD,CAAC,GAAc,KAAK,YAAc,IACpC,KAAK,UAAY,KAAK,OAAO,QAExB,EAAU,EACnB,CASA,YAAY,EAAsB,GAAM,EAA6C,CACnF,GAAI,KAAK,OAAO,SAAW,GAAK,KAAK,WAAa,KAAK,OAAO,OAC5D,OAEF,IAAM,EAAY,KAAK,OAAO,KAAK,WAAW,KAAK,CAAO,EACtD,KAAU,SAAW,EAOzB,MAJA,MAAK,WAAa,KAAK,UAAY,GAAK,KAAK,OAAO,OAChD,CAAC,GAAc,KAAK,YAAc,IACpC,KAAK,UAAY,KAAK,OAAO,QAExB,EAAU,EACnB,CAKA,YAAmB,CACjB,KAAK,UAAY,CACnB,CAEA,IAAI,UAAW,CACb,OAAO,KAAK,OAAO,IAAK,GAAU,EAAM,QAAQ,EAAE,QAAQ,EAAG,IAAM,KAAK,IAAI,EAAG,CAAC,EAAG,CAAC,CACtF,CAEA,KAAK,EAAoB,CACvB,KAAK,OAAO,QAAS,GAAU,EAAM,OAAO,CAAI,CAAC,CACnD,CAEA,SAAS,EAAoB,CAC3B,KAAK,OAAO,KAAK,CAAK,CACxB,CAMA,aAAqB,CACnB,GAAI,KAAK,OAAO,SAAW,EACzB,MAAU,MAAM,+CAA+C,EAEjE,IAAM,EAAc,KAAK,MAAM,KAAK,OAAO,EAAI,KAAK,OAAO,MAAM,EACjE,OAAO,KAAK,OAAO,EACrB,CAEA,SAAsB,CAEpB,OADkB,KAAK,OAAO,IAAK,GAAU,EAAM,QAAQ,CACpD,EAAU,KAAK,CACxB,CAOA,KAAK,EAAmC,CACtC,OAAO,KAAK,OAAO,QAAS,GAAU,EAAM,KAAK,CAAO,CAAC,CAC3D,CAOA,IAAI,WAAqB,CACvB,OAAO,KAAK,OAAO,KAAM,GAAU,EAAM,SAAS,CACpD,CAMA,MAAa,CACX,KAAK,OAAO,QAAS,GAAU,EAAM,KAAK,CAAC,CAC7C,CAEA,OAAc,CACZ,KAAK,OAAO,QAAS,GAAU,EAAM,MAAM,CAAC,CAC9C,CAEA,OAAO,EAAe,EAAkB,EAAgC,CACtE,OAAO,QAAQ,IAAI,KAAK,OAAO,IAAK,GAAU,EAAM,OAAO,EAAO,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CACnG,CAEA,OAAO,EAAkB,EAAgC,CACvD,OAAO,QAAQ,IAAI,KAAK,OAAO,IAAK,GAAU,EAAM,OAAO,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CAC5F,CAEA,QAAQ,EAAkB,EAAgC,CACxD,OAAO,QAAQ,IAAI,KAAK,OAAO,IAAK,GAAU,EAAM,QAAQ,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CAC7F,CAEA,aAAa,EAAkB,EAAgC,CAC7D,OAAO,QAAQ,IAAI,KAAK,OAAO,IAAK,GAAU,EAAM,aAAa,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CAClG,CAEA,KAAK,EAAkC,CAMrC,OALI,IAAc,IAAA,GACT,KAAK,WAEd,KAAK,UAAY,EACjB,KAAK,OAAO,QAAS,GAAU,EAAM,KAAK,CAAS,CAAC,EAC7C,KAAK,UACd,CAMA,UAAU,EAAgC,CACxC,KAAK,OAAO,QAAS,GAAU,EAAM,UAAU,CAAM,CAAC,CACxD,CAKA,aAAa,EAAgC,CAC3C,KAAK,OAAO,QAAS,GAAU,EAAM,aAAa,CAAM,CAAC,CAC3D,CAQA,QAAQ,EAAsB,EAAyB,CACjD,IAAa,IAAA,GAGf,KAAK,OAAO,QAAS,GAAU,EAAM,QAAQ,CAAM,CAAC,EAFpD,KAAK,OAAO,QAAS,GAAU,EAAM,QAAQ,EAAQ,CAAQ,CAAC,CAIlE,CAEA,IAAI,SAAS,EAAoC,CAC/C,KAAK,UAAY,EACjB,KAAK,OAAO,QAAS,GAAW,EAAM,SAAW,KAAK,SAAU,CAClE,CAEA,IAAI,UAAqC,CACvC,OAAO,KAAK,SACd,CAEA,IAAI,QAAiB,CAInB,OAHI,KAAK,OAAO,SAAW,EAClB,EAEF,KAAK,OAAO,IAAK,GAAU,EAAM,MAAM,EAAE,QAAQ,EAAG,IAAM,EAAI,EAAG,CAAC,EAAI,KAAK,OAAO,MAC3F,CAEA,IAAI,OAAO,EAAgB,CACzB,KAAK,OAAO,QAAS,GAAW,EAAM,OAAS,CAAO,CACxD,CAEA,IAAI,cAAuB,CAIzB,OAHI,KAAK,OAAO,SAAW,EAClB,EAEF,KAAK,OAAO,GAAG,YACxB,CAEA,IAAI,aAAa,EAAc,CAC7B,KAAK,OAAO,QAAS,GAAW,EAAM,aAAe,CAAK,CAC5D,CACF,ECxOM,GAA4B,sEAC5B,GACJ,0HAIF,SAAS,GAAmB,EAAyB,CACnD,IAAI,EAAU,EACR,EAAU,IAAI,IACpB,KAAO,aAAmB,OAAS,CAAC,EAAQ,IAAI,CAAO,GAAG,CACxD,EAAQ,IAAI,CAAO,EACnB,IAAM,EAAQ,EAAuC,KACrD,GAAI,EAAQ,QAAQ,SAAS,QAAQ,IAAM,IAAS,wBAA0B,IAAS,oBACrF,MAAO,GAET,EAAW,EAAwC,KACrD,CACA,MAAO,EACT,CAQA,IAAa,GAAb,MAAa,CAAW,CAWH,IACA,WACA,QAZnB,UAAoB,GACpB,wBAAkC,GAElC,aAAgC,EAAgB,IAA0B,CACxE,IAAM,EAAU,UAAU,EAAK,KAAK,IAAI,EAAK,UACvC,EAAY,MAAM,EAAS,EAAK,MAAQ,CAAE,MAAO,EAAK,KAAM,EAAI,IAAA,EAAS,EAC/E,KAAK,QAAQ,EAAO,EAAK,CAC3B,EAEA,YACE,EACA,EACA,EACA,CAHiB,KAAA,IAAA,EACA,KAAA,WAAA,EACA,KAAA,QAAA,CAChB,CAEH,aAAa,OAAO,EAAgD,CAClE,IAAI,EACJ,GAAI,EACD,CAAE,QAAS,GAAmB,MAAM,OAAO,UAC9C,OAAS,EAAO,CAId,MAHI,GAAmB,CAAK,EAChB,MAAM,GAA2B,CAAE,OAAM,CAAC,EAEhD,CACR,CAEA,GAAI,CAAC,EAAe,YAAY,EAC9B,MAAU,MAAM,EAAuB,EAGzC,OAAO,IAAI,EAAW,IAAI,EAAkB,EAAe,OAAO,MAAO,CAAO,CAClF,CAEA,OAAO,EAAyB,EAAmB,CACjD,GAAI,KAAK,UACP,MAAU,MAAM,+BAA+B,EAEjD,AAEE,KAAK,2BADL,KAAK,IAAI,GAAG,KAAK,WAAY,KAAK,WAAW,EACd,IAEjC,KAAK,IAAI,WAAW,CAAG,EACvB,KAAK,IAAI,YAAY,CAAK,CAC5B,CAEA,SAAgB,CACV,KAAK,YAGT,KAAK,UAAY,GACjB,AAEE,KAAK,2BADL,KAAK,IAAI,IAAI,KAAK,WAAY,KAAK,WAAW,EACf,IAEjC,KAAK,IAAI,YAAY,EACrB,KAAK,IAAI,QAAQ,EACnB,CACF,ECnEa,GAAiC,CAC5C,MAAO,GAAG,IAAS,QAAQ,KAAK,GAAG,CAAI,EACvC,MAAO,GAAG,IAAS,QAAQ,KAAK,GAAG,CAAI,EACvC,OAAQ,GAAG,IAAS,QAAQ,MAAM,GAAG,CAAI,CAC3C,EAGa,GAA8B,CACzC,SAAY,CAAC,EACb,SAAY,CAAC,EACb,UAAa,CAAC,CAChB,ECzBsB,GAAtB,KAAoC,CAClC,SAA+B,CAAC,EAMhC,UAAU,EAA0B,CAElC,GAAI,KAAK,SAAS,SAAS,CAAM,EAC/B,MAAU,MAAM,2CAA2C,EAE7D,KAAK,SAAS,KAAK,CAAM,CAC3B,CAMA,aAAa,EAA0B,CAGrC,IAAM,EAAgB,KAAK,SAAS,OAIpC,GAHA,KAAK,SAAW,KAAK,SAAS,OAAQ,GAAM,IAAM,CAAM,EAGpD,KAAK,SAAS,SAAW,EAC3B,MAAU,MAAM,6DAA6D,CAEjF,CAEA,IAAI,SAAU,CACZ,OAAO,KAAK,QACd,CAEA,WAAW,EAA6B,CACtC,EAAQ,QAAS,GAAW,KAAK,UAAU,CAAM,CAAC,CACpD,CAEA,SAAgB,CAEd,KAAK,SAAS,QAAS,GAAW,EAAO,WAAW,CAAC,EACrD,KAAK,SAAW,CAAC,CACnB,CACF,ECOA,SAAgB,GAAuC,EAAa,CAClE,MAAe,UAAoB,CAAK,CACtC,OACA,SAAoB,SAEpB,IAAI,SAAmB,CACrB,OAAO,KAAK,QACd,CAEA,WAAW,EAAkB,EAA2B,CAClD,KAAK,WAAa,GAAW,KAAK,SAMlC,KAAK,QACP,KAAK,OAAO,WAAW,EAGzB,KAAK,SAAW,EACZ,IAAY,SACd,KAAK,OAAS,EAAa,mBAAmB,EAE9C,KAAK,OAAS,EAAa,aAAa,EAE5C,CAEA,cAAc,EAAwB,CACpC,KAAK,OAAS,CAChB,CAOA,IAAI,WAA2B,CAI7B,OAHI,KAAK,UAAY,SACX,KAAK,OAA4B,IAAI,MAExC,IACT,CAQA,IAAI,UAAU,EAAe,CAC3B,GAAI,KAAK,UAAY,SACnB,MAAU,MAAM,kDAAkD,EAEpE,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,2DAA2D,EAE7E,GAAI,EAAQ,IAAM,EAAQ,EACxB,MAAU,WAAW,sCAAsC,EAE7D,KAAM,OAA4B,IAAI,eAAe,EAAO,KAAK,OAAO,QAAQ,WAAW,CAC7F,CAQA,IAAI,eAA+B,CACjC,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,2DAA2D,EAE7E,GAAI,KAAK,UAAY,OACnB,MAAU,MAAM,yDAAyD,EAE3E,IAAM,EAAS,KAAK,OACpB,MAAO,CACL,QAAS,OACT,eAAgB,EAAO,eACvB,eAAgB,EAAO,eACvB,cAAe,EAAO,cACtB,cAAe,EAAO,cACtB,YAAa,EAAO,YACpB,aAAc,KAAK,OAAO,aAC1B,iBAAkB,EAAO,iBACzB,sBAAuB,EAAO,sBAC9B,aAAc,EAAO,aACrB,YAAa,EAAO,YACpB,cAAe,EAAO,cACtB,UAAW,EAAO,UAAU,MAC5B,UAAW,EAAO,UAAU,MAC5B,UAAW,EAAO,UAAU,MAC5B,aAAc,EAAO,aAAa,MAClC,aAAc,EAAO,aAAa,MAClC,aAAc,EAAO,aAAa,KACpC,CACF,CAQA,IAAI,cAAc,EAAqD,CACrE,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,2DAA2D,EAE7E,GAAI,KAAK,UAAY,OACnB,MAAU,MAAM,yDAAyD,EAG3E,GAAI,YAAa,GAAW,EAAQ,UAAY,SAC9C,MAAU,MAAM,qDAAqD,EAEvE,IAAM,EAAS,KAAK,OACpB,EAAO,eAAiB,EAAQ,iBAAmB,IAAA,GAAqC,EAAO,eAAhC,EAAQ,eACvE,EAAO,eAAiB,EAAQ,iBAAmB,IAAA,GAAqC,EAAO,eAAhC,EAAQ,eACvE,EAAO,cAAgB,EAAQ,gBAAkB,IAAA,GAAoC,EAAO,cAA/B,EAAQ,cACrE,EAAO,cAAgB,EAAQ,eAAiB,EAAO,cACvD,EAAO,YAAc,EAAQ,cAAgB,IAAA,GAAkC,EAAO,YAA7B,EAAQ,YACjE,EAAO,aAAe,EAAQ,eAAiB,IAAA,GAAmC,EAAO,aAA9B,EAAQ,aACnE,EAAO,iBAAmB,EAAQ,kBAAoB,EAAO,iBAC7D,EAAO,sBAAwB,EAAQ,uBAAyB,EAAO,sBACvE,EAAO,aAAe,EAAQ,cAAgB,EAAO,aACrD,EAAO,YAAc,EAAQ,cAAgB,IAAA,GAAkC,EAAO,YAA7B,EAAQ,YACjE,EAAO,cAAgB,EAAQ,gBAAkB,IAAA,GAAoC,EAAO,cAA/B,EAAQ,cACrE,EAAO,UAAU,MAAQ,EAAQ,YAAc,IAAA,GAAgC,EAAO,UAAU,MAArC,EAAQ,UACnE,EAAO,UAAU,MAAQ,EAAQ,YAAc,IAAA,GAAgC,EAAO,UAAU,MAArC,EAAQ,UACnE,EAAO,UAAU,MAAQ,EAAQ,YAAc,IAAA,GAAgC,EAAO,UAAU,MAArC,EAAQ,UACnE,EAAO,aAAa,MAAQ,EAAQ,eAAiB,IAAA,GAAmC,EAAO,aAAa,MAA3C,EAAQ,aACzE,EAAO,aAAa,MAAQ,EAAQ,eAAiB,IAAA,GAAmC,EAAO,aAAa,MAA3C,EAAQ,aACzE,EAAO,aAAa,MAAQ,EAAQ,eAAiB,IAAA,GAAmC,EAAO,aAAa,MAA3C,EAAQ,YAC3E,CAQA,IAAI,SAAS,EAAoB,CAC/B,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,8CAA8C,EAEhE,GAAI,KAAK,UAAY,OACnB,MAAU,MAAM,4CAA4C,EAE9D,GAAM,CAAC,EAAG,EAAG,GAAK,EACZ,EAAS,KAAK,OACpB,EAAO,UAAU,MAAQ,EACzB,EAAO,UAAU,MAAQ,EACzB,EAAO,UAAU,MAAQ,CAC3B,CAQA,IAAI,UAAqB,CACvB,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,yDAAyD,EAE3E,GAAI,KAAK,UAAY,OACnB,MAAU,MAAM,uDAAuD,EAEzE,IAAM,EAAS,KAAK,OACpB,MAAO,CAAC,EAAO,UAAU,MAAO,EAAO,UAAU,MAAO,EAAO,UAAU,KAAK,CAChF,CAEA,SAAgB,CACd,AAEE,KAAK,UADL,KAAK,OAAO,WAAW,EACT,IAAA,IAEhB,MAAM,QAAQ,CAChB,CACF,CAEA,OAAO,CACT,CC7MA,SAAgB,GAAuC,EAAa,CAClE,MAAe,UAAoB,CAAK,CACtC,SACA,aACA,UAAqB,GACrB,YAEA,YAAY,EAAoB,CAC9B,KAAK,SAAW,CAClB,CAEA,SAAgB,CAGd,KAAK,WAAW,EAChB,AAEE,KAAK,YADL,KAAK,SAAS,WAAW,EACT,IAAA,IAElB,MAAM,QAAQ,CAChB,CAOA,IAAI,QAAiB,CACnB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,uDAAuD,EAEzE,OAAO,KAAK,SAAS,KAAK,KAC5B,CAOA,IAAI,OAAO,EAAW,CACpB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,uDAAuD,EAEzE,KAAK,WAAW,EAChB,KAAK,SAAS,KAAK,MAAQ,CAC7B,CAKA,IAAI,UAAoB,CACtB,OAAO,KAAK,SACd,CAUA,OAAO,EAAe,EAAkB,EAAiB,SAAyB,CAChF,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,8CAA8C,EAEhE,KAAK,WAAW,EAKhB,IAAM,EAAO,KAAK,SACZ,EAAM,EAAK,QAAQ,YACnB,EAAU,EAAM,EAAW,IAoBjC,OAlBA,EAAK,KAAK,eAAe,EAAK,KAAK,MAAO,CAAG,EAEzC,IAAS,cACX,EAAK,KAAK,6BAA6B,IAAU,EAAI,KAAS,EAAO,CAAO,EAE5E,EAAK,KAAK,wBAAwB,EAAO,CAAO,EAG9C,mBAAoB,EAAK,SACvB,IAAU,GAAK,IAAS,eAC1B,EAAK,KAAK,eAAe,EAAG,CAAO,EAErC,KAAK,UAAY,GACV,QAAQ,QAAQ,IAGzB,KAAK,UAAY,GAEV,IAAI,QAAe,GAAY,CACpC,IAAM,EAAU,eAAiB,CAI3B,KAAK,aAAa,UAAY,IAGlC,KAAK,YAAc,IAAA,GACnB,KAAK,aAAe,IAAA,GACpB,KAAK,UAAY,GAGb,IAAU,GAAK,IAAS,gBAC1B,EAAK,KAAK,MAAQ,GAEpB,EAAQ,EACV,EAAG,CAAQ,EACX,KAAK,YAAc,CAAE,UAAS,UAAS,OAAM,OAAQ,EAAO,MAAK,EACjE,KAAK,aAAe,CACtB,CAAC,EACH,CAOA,YAAmB,CACjB,IAAM,EAAS,KAAK,YAMpB,GALA,KAAK,YAAc,IAAA,GACf,KAAK,eAAiB,IAAA,KACxB,aAAa,KAAK,YAAY,EAC9B,KAAK,aAAe,IAAA,IAElB,GAAU,KAAK,SAAU,CAC3B,IAAM,EAAO,KAAK,SACZ,EAAM,EAAK,QAAQ,YACzB,EAAK,KAAK,sBAAsB,CAAG,EAI/B,EAAO,SAAW,GAAK,EAAO,OAAS,cACzC,EAAK,KAAK,MAAQ,EAIlB,EAAK,KAAK,eAAe,EAAK,KAAK,MAAO,CAAG,CAEjD,CACA,KAAK,UAAY,GAGb,GACF,EAAO,QAAQ,CAEnB,CAQA,OAAO,EAAkB,EAAgC,CACvD,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,8CAA8C,EAEhE,IAAM,EAAS,KAAK,SAAS,KAAK,MAElC,OADA,KAAK,SAAS,KAAK,eAAe,KAAQ,KAAK,SAAS,QAAQ,WAAW,EACpE,KAAK,OAAO,EAAQ,EAAU,CAAI,CAC3C,CAQA,QAAQ,EAAkB,EAAgC,CACxD,OAAO,KAAK,OAAO,EAAG,EAAU,CAAI,CACtC,CACF,CAEA,OAAO,CACT,CCvMA,IAAsB,EAAtB,cAA2C,GAAY,GAAY,EAAa,CAAC,CAAE,CACjF,OACA,aACA,eAKA,WAAiC,IAAI,IACrC,OAAkC,WAClC,OACA,aAAyD,IAAI,EAE7D,YAAY,EAA2B,CACrC,MAAM,EACN,KAAK,OAAS,CAChB,CAEA,wBAAkC,EAAkB,EAAyB,CAI3E,GAHA,KAAK,eACF,EAAM,SACN,KAAK,OAAyD,QAC7D,KAAK,aAAc,CACrB,KAAK,aAAa,aAAa,EAAO,CAAM,EAC5C,MACF,CACA,KAAK,aAAe,IAAI,GAAY,EAAO,EAAQ,KAAK,YAAY,IAAI,CAC1E,CAGA,UAAU,EAA6C,CACrD,GAAI,CACF,OAAO,KAAK,kBAAkB,CAAM,CACtC,OAAS,EAAO,CACd,OAAO,QAAQ,OAAO,CAAK,CAC7B,CACF,CAGA,iBAAiB,EAA6C,CAC5D,OAAO,KAAK,kBAAkB,CAAM,CACtC,CAGA,iBAAiB,EAA6C,CAC5D,OAAO,KAAK,kBAAkB,EAAS,GAAW,CAChD,KAAK,SAAS,KAAK,CAA0B,CAC/C,CAAC,CACH,CAEA,kBAA0B,EAAyB,EAA2D,CAC5G,GAAI,CAAC,KAAK,cAAgB,CAAC,KAAK,eAC9B,MAAU,MAAM,sEAAsE,EAExF,IAAM,EAAQ,KAAK,aACb,EAAc,EAAM,QAAQ,EAC9B,EACJ,GAAI,CACF,EAAQ,EAAO,MAAM,KAAK,cAAc,CAC1C,OAAS,EAAO,CAEd,MADA,EAAM,OAAO,CAAW,EAClB,CACR,CACA,GAAI,aAAiB,SAAY,OAAO,GAAU,UAAY,GAAkB,SAAU,EACxF,OAAO,QAAQ,QAAQ,CAAK,EAAE,KAC3B,GAAa,CACZ,IAAM,EAAS,EAAM,QAAQ,EAAa,CAAQ,EAElD,OADA,IAAU,CAAM,EACT,CACT,EACC,GAAU,CAET,MADA,EAAM,OAAO,CAAW,EAClB,CACR,CACF,EAEF,GAAI,CACF,IAAM,EAAS,EAAM,QAAQ,EAAa,CAAK,EAE/C,OADA,IAAU,CAAM,EACT,QAAQ,QAAQ,CAAM,CAC/B,OAAS,EAAO,CAEd,MADA,EAAM,OAAO,CAAW,EAClB,CACR,CACF,CAEA,gBACE,EACA,EACA,EACA,EACM,CACN,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,2EAA2E,EAE7F,KAAK,aAAa,UAAU,EAAQ,EAAW,EAAO,CAAO,CAC/D,CAEA,UAAU,EAAgC,CACxC,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,qEAAqE,EAEvF,KAAK,aAAa,IAAI,EAAQ,KAAK,SAAS,MAAM,EAClD,KAAK,SAAS,KAAK,CAAM,CAC3B,CAEA,aAAa,EAAgC,CAC3C,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,wEAAwE,EAE1F,KAAK,aAAa,OAAO,CAAM,EAC/B,KAAK,SAAW,KAAK,SAAS,OAAQ,GAAc,IAAc,CAAM,CAC1E,CAEA,eAAe,EAA4C,CACzD,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,wEAAwE,EAE1F,IAAM,EAAa,KAAK,aAAa,MAAM,OAAQ,GAAS,CAAC,KAAK,SAAS,SAAS,CAAwB,CAAC,EAC7G,KAAK,aAAa,SAAS,CAAC,GAAG,EAAS,GAAG,CAAU,CAAC,EACtD,KAAK,SAAW,CAAC,GAAG,CAAO,CAC7B,CAEA,kBAAkB,EAA0B,EAAyB,CACnE,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,wEAAwE,EAE1F,KAAK,aAAa,YAAY,EAAQ,CAAQ,CAChD,CAEA,iBAAiB,EAAmC,CAClD,OAAO,KAAK,cAAc,WAAW,CAAM,GAAK,EAClD,CAEA,gBACE,EACA,EACA,EACA,EACM,CACN,GAAI,CAAC,KAAK,aACR,MAAU,MAAM,0EAA0E,EAE5F,KAAK,aAAa,UAAU,EAAQ,EAAW,EAAO,CAAO,CAC/D,CAUA,IAAI,WAAqB,CACvB,OAAO,KAAK,SAAW,SACzB,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,SAAW,QACzB,CAEA,aAA8B,CAC5B,KAAK,OAAS,SAChB,CAEA,YAA6B,CAC3B,KAAK,OAAS,QAChB,CAEA,aAA8B,CAC5B,KAAK,OAAS,SAChB,CAEA,gBAA0B,EAAyB,CACjD,KAAK,YAAY,EACjB,KAAK,KAAK,OAAQ,IAAI,EAClB,GACF,KAAK,KAAK,SAAU,IAAA,EAAS,EAE/B,KAAK,OAAO,WAAW,KAAK,aAAc,CACxC,OAAQ,KAAK,OACb,UAAW,KAAK,IAAI,CACtB,CAAC,CACH,CAEA,YAA6B,CAC3B,KAAK,WAAW,EAChB,KAAK,KAAK,QAAS,IAAA,EAAS,EAC5B,KAAK,OAAO,WAAW,KAAK,cAAe,CACzC,OAAQ,KAAK,OACb,UAAW,KAAK,IAAI,CACtB,CAAC,CACH,CAEA,aAA8B,CAC5B,KAAK,YAAY,EACjB,KAAK,KAAK,OAAQ,IAAA,EAAS,EAC3B,KAAK,OAAO,WAAW,KAAK,aAAc,CACxC,OAAQ,KAAK,OACb,UAAW,KAAK,IAAI,CACtB,CAAC,CACH,CAMA,GAAmC,EAAU,EAAyD,CACpG,OAAO,KAAK,aAAa,GAAG,EAAO,CAAQ,CAC7C,CAKA,IAAoC,EAAU,EAAmD,CAC/F,KAAK,aAAa,IAAI,EAAO,CAAQ,CACvC,CAEA,KAA4C,EAAU,EAA+B,CACnF,KAAK,aAAa,KAAK,EAAO,CAAI,CACpC,CAEA,MAAa,UAA0C,EAAU,EAAwC,CACvG,OAAO,KAAK,aAAa,UAAU,EAAO,CAAI,CAChD,CAKA,OAAO,EAAe,EAAkB,EAAiB,SAAyB,CAEhF,OADA,KAAK,KAAK,YAAa,CAAE,OAAQ,EAAO,WAAU,MAAK,CAAC,EACjD,MAAM,OAAO,EAAO,EAAU,CAAI,EAAE,SAAW,CACpD,KAAK,KAAK,UAAW,IAAA,EAAS,CAChC,CAAC,CACH,CAKA,YAAmB,CACjB,IAAM,EAAY,KAAK,UACvB,MAAM,WAAW,EACb,GACF,KAAK,KAAK,aAAc,IAAA,EAAS,CAErC,CAEA,SAAgB,CACd,IAAK,IAAM,KAAY,KAAK,WAAW,OAAO,EAC5C,GAAI,CACF,EAAS,WAAW,CACtB,MAAQ,CAER,CAEF,KAAK,WAAW,MAAM,EACtB,KAAK,cAAc,QAAQ,EAC3B,KAAK,aAAe,IAAA,GACpB,KAAK,eAAiB,IAAA,GACtB,KAAK,aAAa,mBAAmB,EACrC,MAAM,QAAQ,CAChB,CACF,ECxQM,GAA0C,CAC9C,QAAS,OACT,eAAgB,IAChB,eAAgB,IAChB,cAAe,EACf,cAAe,UACf,YAAa,IACb,aAAc,EACd,iBAAkB,cAClB,sBAAuB,WACvB,aAAc,OACd,YAAa,EACb,cAAe,EACf,UAAW,EACX,UAAW,EACX,UAAW,EACX,aAAc,EACd,aAAc,EACd,aAAc,CAChB,EA4CA,SAAgB,GAA6C,EAAa,CACxE,MAAe,UAA0B,CAAK,CAC5C,UAA4B,CAAC,EAC7B,UAAsB,CAAC,EAAG,EAAG,CAAC,EAC9B,WAAqB,EAOrB,eAAgC,CAAE,GAAG,EAAyB,EAC9D,QAAkB,EAUlB,MAAuB,CACrB,IAAM,EAAW,KAAK,QAAQ,EAE9B,OADA,EAAS,QAAS,GAAM,EAAE,KAAK,CAAC,EACzB,CACT,CAMA,MAAO,CACL,KAAK,UAAU,QAAS,GAAM,CAC5B,EAAE,KAAK,EACP,EAAE,QAAQ,CACZ,CAAC,EACD,KAAK,UAAY,CAAC,CACpB,CAMA,OAAc,CACZ,KAAK,UAAU,QAAS,GAAa,EAAS,MAAM,CAAC,CACvD,CAQA,UAAU,EAAgC,CACxC,MAAM,UAAU,CAAM,CAGxB,CAQA,aAAa,EAAgC,CAC3C,MAAM,aAAa,CAAM,CAE3B,CAOA,IAAI,WAAqB,CACvB,OAAO,KAAK,UAAU,KAAM,GAAM,EAAE,SAAS,CAC/C,CAOA,IAAI,UAAqB,CAIvB,OAHI,KAAK,eAAe,UAAY,SAC3B,CAAC,EAAG,EAAG,CAAC,EAEV,CAAC,KAAK,eAAe,UAAW,KAAK,eAAe,UAAW,KAAK,eAAe,SAAS,CACrG,CAQA,IAAI,SAAS,EAAoB,CAC3B,KAAK,eAAe,UAAY,SAClC,KAAK,eAAe,UAAY,EAAS,GACzC,KAAK,eAAe,UAAY,EAAS,GACzC,KAAK,eAAe,UAAY,EAAS,IAE3C,KAAK,UAAU,QAAS,GAAO,EAAE,SAAW,CAAS,CACvD,CAOA,IAAI,eAA+B,CACjC,MAAO,CAAE,GAAG,KAAK,cAAe,CAClC,CAWA,IAAI,cAAc,EAAqD,CACjE,YAAa,EAEf,KAAK,eAAiB,CAAE,GAAG,CAAQ,EAC1B,KAAK,eAAe,UAAY,OACzC,KAAK,eAAiB,CAAE,GAAG,KAAK,eAAgB,GAAG,CAAQ,EAG3D,KAAK,eAAiB,CAAE,GAAG,GAA0B,GAAG,CAAQ,EAElE,KAAK,UAAU,QAAS,GAAO,EAAE,cAAgB,KAAK,cAAe,CACvE,CAEA,IAAI,WAAoB,CACtB,OAAO,KAAK,UACd,CAEA,IAAI,UAAU,EAAe,CAC3B,KAAK,WAAa,EAClB,KAAK,UAAU,QAAS,GAAO,EAAE,UAAY,CAAM,CACrD,CAOA,IAAI,QAAiB,CACnB,OAAO,KAAK,OACd,CAOA,IAAI,OAAO,EAAgB,CACzB,KAAK,QAAU,EACf,KAAK,UAAU,QAAS,GAAO,EAAE,OAAS,CAAO,CACnD,CASA,OAAO,EAAe,EAAkB,EAAgC,CAEtE,MADA,MAAK,QAAU,EACR,QAAQ,IAAI,KAAK,UAAU,IAAK,GAAM,EAAE,OAAO,EAAO,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CAC9F,CAQA,OAAO,EAAkB,EAAgC,CACvD,OAAO,QAAQ,IAAI,KAAK,UAAU,IAAK,GAAM,EAAE,OAAO,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CACvF,CAQA,QAAQ,EAAkB,EAAgC,CACxD,OAAO,QAAQ,IAAI,KAAK,UAAU,IAAK,GAAM,EAAE,QAAQ,EAAU,CAAI,CAAC,CAAC,EAAE,SAAW,CAAC,CAAC,CACxF,CAQA,aAAa,EAAkB,EAAgC,CAC7D,OAAO,KAAK,QAAQ,EAAU,CAAI,EAAE,SAAW,KAAK,KAAK,CAAC,CAC5D,CACF,CACA,OAAO,CACT,CC5QA,IAAsB,EAAtB,cAA6C,GAAkB,EAAa,CAA6B,CAMvG,aAAqC,KAErC,OAAqC,IAAI,IAEzC,SAA+C,CAAC,EAChD,eAAkC,IAAI,IACtC,qBAAwC,IAAI,IAE5C,UAAU,EAA+B,CACvC,GAAI,KAAK,SAAS,SAAS,CAAM,EAC/B,MAAU,MAAM,yCAAyC,EAE3D,KAAK,SAAS,KAAK,CAAM,CAC3B,CAEA,aAAa,EAA+B,CAC1C,IAAM,EAAQ,KAAK,SAAS,QAAQ,CAAM,EAC1C,GAAI,IAAU,GACZ,MAAU,MAAM,qDAAqD,EAEvE,KAAK,SAAS,OAAO,EAAO,CAAC,CAC/B,CAEA,UAAmB,EAAgC,CACjD,MAAM,UAAU,CAAM,EACtB,IAAM,EAAS,IAAI,EAAmB,CAAM,EAC5C,KAAK,eAAe,IAAI,EAAQ,CAAM,EACtC,KAAK,qBAAqB,IAAI,CAAM,EACpC,KAAK,SAAS,KAAK,CAAM,CAC3B,CAEA,aAAsB,EAAgC,CACpD,MAAM,aAAa,CAAM,EACzB,IAAM,EAAS,KAAK,eAAe,IAAI,CAAM,EACzC,IACF,KAAK,SAAS,OAAO,KAAK,SAAS,QAAQ,CAAM,EAAG,CAAC,EACrD,KAAK,eAAe,OAAO,CAAM,EACjC,KAAK,qBAAqB,OAAO,CAAM,EAE3C,CAMA,QAAQ,EAAsB,EAAyB,CACrD,IAAM,EAAM,KAAK,eAAe,CAAM,EACtC,GAAI,IAAa,IAAA,GAAW,CAC1B,KAAK,SAAS,EAAK,CAAQ,EAC3B,MACF,CACA,KAAK,YAAY,CAAG,CACtB,CAEA,eAAuB,EAA2B,CAChD,GAAI,OAAO,GAAW,SACpB,OAAO,EAET,IAAM,EAAM,KAAK,WAAW,OAAO,CAAM,EACzC,GAAI,CAAC,EACH,MAAU,MAAM,gCAAgC,EAAO,EAAE,EAE3D,OAAO,CACT,CAMA,yBAAoC,CAUlC,OATK,KAAK,aAGN,KAAK,aAAa,WACpB,QAAQ,KACN,GAAG,KAAK,YAAY,KAAK,4BAA4B,KAAK,aAAa,MAAQ,cAAc,0BAC/F,EACO,KAAK,gBAEP,KAAK,aAAa,MARhB,KAAK,cAShB,CAKA,gBAA0B,EAA8B,CACtD,IAAM,EAAc,EAA8D,WAClF,IAAK,GAAM,CAAC,EAAK,KAAc,KAAK,OAAQ,CAC1C,GAAI,EAAI,UAAW,CACjB,QAAQ,KAAK,GAAG,KAAK,YAAY,KAAK,gCAAgC,EAAI,MAAQ,cAAc,YAAY,EAC5G,QACF,CACA,IAAM,EAAW,KAAK,QAAQ,WAAW,EACzC,EAAS,KAAK,MAAQ,EACtB,EAAW,QAAQ,CAAQ,EAC3B,EAAS,QAAQ,EAAI,KAAK,EAC1B,EAAS,WAAW,IAAI,EAAK,CAAQ,CACvC,CACF,CAGA,iBAA2B,EAA8B,CACvD,IAAK,IAAM,KAAU,KAAK,UACV,KAAK,qBAAqB,IAAI,CAAM,EAC9C,EAAS,iBAAiB,CAAM,EAChC,EAAS,iBAAiB,CAAM,GACzB,MAAO,GAAU,CAC1B,QAAQ,KAAK,GAAG,KAAK,YAAY,KAAK,gEAAiE,CAAK,CAC9G,CAAC,EAEH,KAAK,gBAAgB,CAAQ,CAC/B,CAEA,YAAoB,EAAgB,CAClC,GAAI,EAAI,UACN,MAAU,MAAM,kCAAkC,EAAI,MAAQ,cAAc,EAAE,EAEhF,IAAM,EAAmB,EAAI,QAAU,KAAK,eACtC,EAAY,KAAK,aACjB,EAAgB,KAAK,wBAAwB,EACnD,KAAK,aAAe,EAAmB,KAAO,EAC9C,IAAM,EAAY,KAAK,aACjB,EAAgB,KAAK,wBAAwB,EAKnD,GAAI,IAAc,IACZ,GAAa,CAAC,KAAK,OAAO,IAAI,CAAS,GACzC,EAAU,wBAAwB,IAAI,EAEpC,GACF,EAAU,sBAAsB,IAAI,GAGpC,IAAkB,EAGtB,IAAK,IAAM,KAAY,KAAK,UAAsE,CAChG,GAAI,CACF,EAAS,WAAW,WAAW,CAAa,CAC9C,MAAQ,CAER,CACA,GAAI,CACF,EAAS,WAAW,QAAQ,CAAa,CAC3C,MAAQ,CAER,CACF,CACF,CAEA,SAAiB,EAAU,EAAyB,CAClD,GAAI,EAAI,UACN,MAAU,MAAM,uCAAuC,EAAI,MAAQ,cAAc,EAAE,EAErF,KAAK,OAAO,IAAI,EAAK,CAAS,EAC9B,EAAI,sBAAsB,IAAI,EAC9B,IAAK,IAAM,KAAY,KAAK,UAAsE,CAChG,IAAM,EAAW,EAAS,WAAW,IAAI,CAAG,EAC5C,GAAI,EAAU,CACZ,EAAS,KAAK,MAAQ,EACtB,QACF,CACA,IAAM,EAAW,KAAK,QAAQ,WAAW,EACzC,EAAS,KAAK,MAAQ,EACtB,GAAI,CACF,EAAS,WAAW,QAAQ,CAAQ,CACtC,MAAQ,CACN,GAAI,CACF,EAAS,WAAW,CACtB,MAAQ,CAER,CACA,QACF,CACA,EAAS,QAAQ,EAAI,KAAK,EAC1B,EAAS,WAAW,IAAI,EAAK,CAAQ,CACvC,CACF,CAEA,cAAc,EAAU,EAAmB,CAIzC,GAHI,KAAK,eAAiB,GACxB,KAAK,YAAY,CAAM,EAErB,CAAC,KAAK,OAAO,IAAI,CAAG,EACtB,OAEF,IAAM,EAAY,KAAK,OAAO,IAAI,CAAG,EACrC,KAAK,OAAO,OAAO,CAAG,EACtB,IAAK,IAAM,KAAY,KAAK,UAAsE,CAChG,IAAM,EAAW,EAAS,WAAW,IAAI,CAAG,EACvC,KAGL,IAAI,CACF,EAAS,WAAW,CACtB,MAAQ,CAER,CACA,EAAS,WAAW,OAAO,CAAG,CAD9B,CAEF,CACA,KAAK,SAAS,EAAQ,CAAS,CACjC,CAEA,SAAgB,CACV,KAAK,cACP,KAAK,aAAa,wBAAwB,IAAI,EAEhD,IAAK,IAAM,KAAO,KAAK,OAAO,KAAK,EACjC,EAAI,wBAAwB,IAAI,EAElC,KAAK,SAAS,OAAS,EACvB,KAAK,eAAe,MAAM,EAC1B,KAAK,qBAAqB,MAAM,EAChC,MAAM,QAAQ,CAChB,CACF,EChNa,GAAb,cAAyC,CAAa,CAGpD,iBACA,cAAwB,GACxB,kBAMA,aAEA,YACE,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAAiC,GACjC,CACA,MAAM,CAAM,EACZ,KAAK,iBAAmB,EACxB,KAAK,OAAS,EACd,KAAK,WAAW,EAAS,CAAO,EAChC,KAAK,YAAY,CAAQ,EACzB,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAO,EACtD,KAAK,OAAQ,QAAQ,KAAK,QAAS,EACnC,KAAK,SAAU,QAAQ,CAAU,EAC7B,GACF,KAAK,mBAAmB,CAE5B,CAEA,oBAAmC,CAC7B,YAAO,MAAU,KAAe,CAAC,KAAK,QAG1C,GAAI,CACF,IAAM,EAAU,IAAI,MACpB,EAAQ,MAAQ,GAChB,EAAQ,UAAY,KAAK,OAAO,YAChC,KAAK,aAAe,CACtB,MAAQ,CAEN,KAAK,aAAe,IAAA,EACtB,CACF,CAEA,sBAAqC,CACnC,IAAM,EAAU,KAAK,aAChB,KAGL,MAAK,aAAe,IAAA,GACpB,GAAI,CACF,EAAQ,MAAM,EACd,EAAQ,UAAY,IACtB,MAAQ,CAER,CANoB,CAOtB,CAEA,IAAI,UAAmB,CACrB,MAAO,EACT,CAEA,MAAe,CACb,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,qDAAqD,EAEvE,GAAI,KAAK,UACP,MAAO,CAAC,IAAI,EAEd,IAAM,EAAS,KAAK,OAAO,YAAY,UAAU,EACjD,GAAI,EAAO,OAAS,GAAK,EAAO,MAAO,GAAU,EAAM,aAAe,OAAO,EAC3E,MAAU,MAAM,oDAAoD,EAEtE,IAAM,EAAW,KAAK,SActB,OAbI,GAAY,KAAK,mBACnB,EAAO,QAAS,GAAU,CACxB,IAAM,EAAU,KAAK,mBAAmB,IAAI,CAAK,EAC7C,IAAY,IAAA,KACd,EAAM,QAAU,EAEpB,CAAC,EAEH,KAAK,kBAAoB,IAAA,GAGzB,KAAK,cAAc,KAAK,EAAE,UAAY,CAAC,CAAC,EACxC,KAAK,gBAAgB,CAAQ,EACtB,CAAC,IAAI,CACd,CAEA,OAAc,CACZ,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,UACxB,OAEF,IAAM,EAAS,KAAK,OAAO,YAAY,UAAU,EACjD,KAAK,kBAAoB,IAAI,IAAI,EAAO,IAAK,GAAU,CAAC,EAAO,EAAM,OAAO,CAAC,CAAC,EAC9E,EAAO,QAAS,GAAW,EAAM,QAAU,EAAM,EACjD,KAAK,cAAc,MAAM,EACzB,KAAK,WAAW,CAClB,CAEA,QAAe,CACb,KAAK,KAAK,CACZ,CAEA,MAAa,CACX,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,qDAAqD,EAEvE,IAAM,EAAiB,KAAK,SAAW,WAAa,KAAK,SAAW,SACpE,KAAK,gBAAgB,EACrB,KAAK,kBAAoB,IAAA,GACzB,KAAK,qBAAqB,EACtB,EACF,KAAK,YAAY,EAEjB,KAAK,YAAY,CAErB,CAEA,IAAI,cAAuB,CACzB,MAAO,EACT,CAEA,IAAI,aAAa,EAAe,CAAC,CAEjC,UAAU,EAAgC,CACxC,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,gEAAgE,EAElF,MAAM,UAAU,CAAM,CACxB,CAEA,aAAa,EAAgC,CAC3C,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,qEAAqE,EAEvF,MAAM,aAAa,CAAM,CAC3B,CAEA,IAAI,YAAuB,CACzB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,sEAAsE,EAExF,OAAO,KAAK,QACd,CAEA,QAAQ,EAAgD,CACtD,OAAO,KAAK,WAAW,QAAQ,CAAkB,CACnD,CAEA,WAAW,EAA4C,CACjD,EACF,KAAK,WAAW,WAAW,CAAkB,EAE7C,KAAK,WAAW,WAAW,CAE/B,CAEA,iBAAgC,CAC1B,CAAC,KAAK,kBAAoB,KAAK,eAAiB,CAAC,KAAK,SAG1D,KAAK,OAAO,YAAY,UAAU,EAAE,QAAS,GAAU,EAAM,KAAK,CAAC,EACnE,KAAK,cAAgB,GACvB,CAEA,SAAgB,CACT,KAAK,SAGV,KAAK,gBAAgB,EACrB,KAAK,kBAAoB,IAAA,GACzB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,WAAW,EACvB,KAAK,OAAS,IAAA,GACd,MAAM,QAAQ,EAChB,CACF,EAEa,EAAb,cAAsC,CAAoC,CAc9D,WAZV,QACA,eACA,OACA,iBACA,QACA,sBAEA,YACE,EACA,EACA,EACA,EAAmC,CAAC,EACpC,EACA,CACA,MAAM,EAFE,KAAA,WAAA,EAGR,KAAK,OAAS,EACd,KAAK,QAAU,EACf,KAAK,eAAiB,EACtB,KAAK,QAAU,EAAQ,SAAW,OAClC,KAAK,iBAAmB,EAAQ,kBAAoB,GACpD,KAAK,sBAAwB,EAAQ,uBAAyB,EAChE,CAEA,IAAI,WAAmC,CACrC,OAAO,KAAK,UACd,CAEA,mBAA2B,EAAiD,CAC1E,GAAI,CAAC,KAAK,QAAQ,wBAChB,MAAU,MAAM,0FAA0F,EAE5G,OAAO,KAAK,QAAQ,wBAAwB,CAAM,CACpD,CAEA,SAAiC,CAC/B,GAAI,KAAK,UAAU,GACjB,MAAO,CAAC,KAAK,UAAU,EAAE,EAG3B,IAAM,EAAS,KAAK,mBAAmB,KAAK,MAAM,EAC5C,EAAW,KAAK,QAAQ,WAAW,EACnC,EAAW,IAAI,GACnB,KACA,EACA,EACA,KAAK,QACL,KAAK,wBAAwB,EAC7B,KAAK,QACL,KAAK,iBACL,KAAK,qBACP,EAUA,OATA,KAAK,iBAAiB,CAAQ,EAC9B,EAAS,OAAS,KAAK,OACnB,KAAK,UAAY,QACnB,EAAS,cAAgB,KAAK,cAC9B,EAAS,SAAW,KAAK,UAEzB,EAAS,UAAY,KAAK,WAAa,EAEzC,KAAK,UAAU,KAAK,CAAQ,EACrB,CAAC,CAAQ,CAClB,CAEA,KAAK,EAAqB,CAAC,CAE3B,QAAe,CACb,KAAK,UAAU,QAAS,GAAa,EAAS,OAAO,CAAC,CACxD,CAEA,KAAK,EAAmC,CACtC,MAAO,EACT,CAEA,IAAI,cAAuB,CACzB,MAAO,EACT,CAEA,IAAI,aAAa,EAAe,CAAC,CAEjC,SAAgB,CACd,KAAK,KAAK,EACV,MAAM,QAAQ,CAChB,CACF,EC9Qa,GAAb,KAA2B,CACzB,KACA,OACA,WACA,gBACA,OAAkC,CAChC,UAAW,KACX,UAAW,KACX,WAAY,KACZ,SAAU,IACZ,EAOA,SAYA,YAAY,EAAwB,EAAmB,EAAuB,EAA6B,CACzG,KAAK,KAAO,EACZ,KAAK,OAAS,EACd,KAAK,WAAa,EAClB,KAAK,gBAAkB,EACvB,KAAK,KAAK,KAAK,iBAAiB,UAAW,KAAK,aAAa,EAG7D,KAAK,KAAK,KAAK,QAAQ,EAEvB,KAAK,OAAO,QAAQ,KAAK,IAAI,EACzB,KAAK,YAAc,KAAK,kBAC1B,KAAK,WAAW,KAAK,MAAQ,EAC7B,KAAK,KAAK,QAAQ,KAAK,UAAU,EACjC,KAAK,WAAW,QAAQ,KAAK,eAAe,EAEhD,CAEA,cAAkC,GAA8B,CAC9D,IAAM,EAAO,EAAM,KACf,CAAC,GAAQ,EAAK,OAAS,aAG3B,KAAK,OAAS,CACZ,UAAW,EAAK,UAChB,UAAW,EAAK,UAChB,WAAY,EAAK,WACjB,SAAU,EAAK,QACjB,EACA,KAAK,WAAW,KAAK,MAAM,EAC7B,EAGA,IAAI,WAAoB,CACtB,OAAO,KAAK,OAAO,SACrB,CAGA,IAAI,WAAoB,CACtB,OAAO,KAAK,OAAO,SACrB,CAGA,IAAI,YAAqB,CACvB,OAAO,KAAK,OAAO,UACrB,CAGA,IAAI,UAAmB,CACrB,OAAO,KAAK,OAAO,QACrB,CAGA,IAAI,SAA2B,CAC7B,MAAO,CAAE,GAAG,KAAK,MAAO,CAC1B,CAGA,IAAI,aAAgC,CAClC,OAAO,KAAK,IACd,CAOA,OAAc,CACZ,KAAK,KAAK,KAAK,YAAY,CAAE,QAAS,OAAQ,CAAC,CACjD,CAMA,YAAmB,CACjB,KAAK,KAAK,KAAK,sBAAsB,UAAW,KAAK,aAAa,EAClE,GAAI,CACF,KAAK,OAAO,WAAW,KAAK,IAAI,CAClC,MAAQ,CAER,CACA,GAAI,KAAK,WACP,GAAI,CACF,KAAK,KAAK,WAAW,KAAK,UAAU,CACtC,MAAQ,CAER,CAEF,GAAI,KAAK,YAAc,KAAK,gBAC1B,GAAI,CACF,KAAK,WAAW,WAAW,KAAK,eAAe,CACjD,MAAQ,CAER,CAEJ,CACF,EClIa,GAAb,MAAa,UAAyB,CAAiB,CACrD,aAAa,QACX,EACA,EACA,EAAmC,CAAC,EACpC,EAC2B,CAE3B,OAAO,IAAI,EAAiB,EAAS,MADhB,UAAU,aAAa,aAAa,EAAQ,aAAe,CAAE,MAAO,EAAK,CAAC,EAClD,EAAY,EAAS,CAAS,CAC7E,CAEA,YACE,EACA,EACA,EACA,EAAmC,CAAC,EACpC,EACA,CACA,GAAM,CAAE,YAAa,EAAc,YAAW,gBAAe,GAAG,GAAuB,EACjF,EAAmB,EAAmB,SAAW,OACvD,MACE,EACA,EACC,GAAc,EAAQ,YACvB,CACE,GAAG,EACH,UACA,sBAAuB,EAAmB,uBAAyB,GACnE,iBAAkB,EAAmB,kBAAoB,EAC3D,EACA,CACF,EAEI,IAAY,SACd,KAAK,UAAY,GAAa,EACrB,IACT,KAAK,cAAgB,EAEzB,CACF,EC1Ba,GAAb,cAAuC,CAAa,CAIlD,YACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,MAAM,CAAM,EACZ,KAAK,OAAS,EACd,KAAK,WAAW,EAAS,CAAO,EAChC,KAAK,YAAY,CAAQ,EACzB,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAO,EACtD,KAAK,OAAQ,QAAQ,KAAK,QAAS,EACnC,KAAK,SAAU,QAAQ,CAAU,CACnC,CAEA,IAAI,UAAmB,CACrB,MAAO,IACT,CAEA,MAAe,CACb,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,mDAAmD,EAErE,GAAI,KAAK,UACP,MAAO,CAAC,IAAI,EAEd,IAAM,EAAW,KAAK,SAGtB,OAFA,KAAK,OAAO,KAAK,YAAY,CAAE,KAAM,MAAO,CAAC,EAC7C,KAAK,gBAAgB,CAAQ,EACtB,CAAC,IAAI,CACd,CAEA,OAAc,CACR,CAAC,KAAK,QAAU,CAAC,KAAK,YAG1B,KAAK,OAAO,KAAK,YAAY,CAAE,KAAM,OAAQ,CAAC,EAC9C,KAAK,WAAW,EAClB,CAEA,MAAa,CACX,GAAI,CAAC,KAAK,OACR,OAEF,IAAM,EAAiB,KAAK,WAAa,KAAK,SAC9C,KAAK,OAAO,KAAK,YAAY,CAAE,KAAM,MAAO,CAAC,EACzC,EACF,KAAK,YAAY,EAEjB,KAAK,YAAY,CAErB,CAEA,cAAqB,CACd,KAAK,SAGV,KAAK,YAAY,EACjB,KAAK,KAAK,QAAS,IAAA,EAAS,EAC9B,CAEA,IAAI,cAAuB,CACzB,MAAO,EACT,CAEA,IAAI,aAAa,EAAe,CAAC,CAEjC,UAAU,EAAgC,CACxC,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,8DAA8D,EAEhF,MAAM,UAAU,CAAM,CACxB,CAEA,aAAa,EAAgC,CAC3C,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,mEAAmE,EAErF,MAAM,aAAa,CAAM,CAC3B,CAEA,IAAI,YAAuB,CACzB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,oEAAoE,EAEtF,OAAO,KAAK,QACd,CAEA,QAAQ,EAAgD,CACtD,OAAO,KAAK,WAAW,QAAQ,CAAwB,CACzD,CAEA,WAAW,EAA4C,CACjD,EACF,KAAK,WAAW,WAAW,CAAwB,EAEnD,KAAK,WAAW,WAAW,CAE/B,CAEA,SAAgB,CACT,KAAK,SAGV,KAAK,YAAY,EACjB,KAAK,OAAO,WAAW,EACvB,KAAK,OAAS,IAAA,GACd,MAAM,QAAQ,EAChB,CACF,EAEa,GAAb,cAAoC,CAAoC,CAwDnD,WAtDnB,UACA,mBACA,QACA,eACA,YACA,aACA,eACA,QACA,aAAgC,IAAI,EACpC,OACA,eAAyB,EACzB,cAAwB,GACxB,WAAqB,GACrB,SAAmB,GACnB,MAAgC,OAChC,WAEA,qBAAyC,GAAiD,CACxF,OAAQ,EAAM,KAAK,KAAnB,CACE,IAAK,WACH,KAAK,eAAiB,KAAK,IAAI,EAAG,KAAK,eAAiB,EAAM,KAAK,MAAM,EACrE,KAAK,eAAiB,KAAK,eAAiB,KAAK,iBACnD,KAAK,cAAgB,GACrB,KAAK,KAAK,QAAS,CAAE,iBAAkB,KAAK,gBAAiB,CAAC,GAEhE,MACF,IAAK,WACH,KAAK,KAAK,WAAY,CAAE,iBAAkB,KAAK,gBAAiB,CAAC,EACjE,MACF,IAAK,QACH,KAAK,eAAiB,EACtB,KAAK,UAAU,QAAS,GAAa,EAAS,aAAa,CAAC,EAC5D,KAAK,SAAS,OAAO,EACrB,KAAK,KAAK,QAAS,IAAA,EAAS,EAC5B,MACF,IAAK,WACH,KAAK,cAAgB,GACrB,KACJ,CACF,EAEA,gBAA2C,CACzC,KAAK,QAAQ,CACf,EAEA,YACE,EACA,EACA,EACA,EAKA,EACA,CACA,MAAM,EAFW,KAAA,WAAA,EAGjB,KAAK,YAAc,EACnB,KAAK,QAAU,EACf,KAAK,eAAiB,EACtB,KAAK,aAAe,EAAQ,aAC5B,KAAK,eAAiB,KAAK,KAAK,EAAQ,WAAa,EAAQ,cAAc,EAC3E,KAAK,QAAU,EAAQ,SAAW,OAClC,KAAK,OAAS,EAAQ,OACtB,KAAK,YAAY,KAAK,iBAAiB,UAAW,KAAK,oBAAqC,EAC5F,KAAK,YAAY,KAAK,QAAQ,EAC9B,KAAK,QAAQ,iBAAiB,QAAS,KAAK,YAAa,CAAE,KAAM,EAAK,CAAC,CACzE,CAEA,IAAI,WAAmC,CACrC,OAAO,KAAK,UACd,CAEA,IAAI,kBAA2B,CAC7B,OAAO,KAAK,eAAiB,KAAK,QAAQ,UAC5C,CAEA,IAAI,mBAA4B,CAC9B,OAAO,KAAK,YACd,CAEA,GAAoC,EAAU,EAA0D,CACtG,OAAO,KAAK,aAAa,GAAG,EAAO,CAAQ,CAC7C,CAEA,IAAqC,EAAU,EAAoD,CACjG,KAAK,aAAa,IAAI,EAAO,CAAQ,CACvC,CAEA,KAA8C,EAAU,EAAgC,CACtF,KAAK,aAAa,KAAK,EAAO,CAAI,CACpC,CAEA,SAAiB,EAA6B,CACxC,KAAK,QAAU,IAGnB,KAAK,MAAQ,EACb,KAAK,KAAK,cAAe,CAAK,EAChC,CAEA,iBAAiB,EAA6B,EAAwC,CACpF,GAAI,KAAK,UAAY,KAAK,WACxB,MAAU,MAAM,gDAAgD,EAElE,KAAK,WAAa,EAClB,KAAK,UAAY,YACjB,KAAK,mBAAqB,CAC5B,CAEA,gBAAgB,EAAsB,CAC/B,KAAK,UACR,KAAK,KAAK,QAAS,CAAK,CAE5B,CASA,MAAM,EAAgC,CACpC,GAAI,KAAK,SACP,MAAU,MAAM,uDAAuD,EAEzE,GAAI,KAAK,WACP,MAAU,MAAM,8BAA8B,EAEhD,GAAI,EAAE,aAAmB,cACvB,MAAU,UAAU,gDAAgD,EAEtE,GAAI,EAAQ,OAAS,KAAK,eAAiB,EACzC,MAAU,WAAW,6DAA6D,KAAK,aAAa,EAAE,EAExG,IAAM,EAAa,EAAQ,OAAS,KAAK,aACzC,GAAI,EAAa,KAAK,eACpB,MAAU,WAAW,qDAAqD,EAgB5E,OAdI,KAAK,eAAiB,EAAa,KAAK,iBAK5C,KAAK,YAAY,KAAK,YAAY,CAChC,KAAM,QACN,QAAS,EAAQ,MAAM,CACzB,CAAC,EACD,KAAK,gBAAkB,EACnB,KAAK,iBAAmB,KAAK,iBAC/B,KAAK,cAAgB,GACd,IAEF,EACT,CAEA,KAAY,CACN,KAAK,UAAY,KAAK,aAG1B,KAAK,WAAa,GAClB,KAAK,YAAY,KAAK,YAAY,CAAE,KAAM,KAAM,CAAC,EACnD,CAEA,SAA+B,CAC7B,GAAI,KAAK,SACP,MAAU,MAAM,mDAAmD,EAErE,GAAI,KAAK,UAAU,GACjB,MAAO,CAAC,KAAK,UAAU,EAAE,EAE3B,IAAM,EAAW,IAAI,GACnB,KACA,KAAK,YACL,KAAK,QAAQ,WAAW,EACxB,KAAK,QACL,KAAK,wBAAwB,EAC7B,KAAK,OACP,EAUA,OATA,KAAK,iBAAiB,CAAQ,EAC9B,EAAS,OAAS,KAAK,OACnB,KAAK,UAAY,QACnB,EAAS,cAAgB,KAAK,cAC9B,EAAS,SAAW,KAAK,UAEzB,EAAS,UAAY,KAAK,UAE5B,KAAK,UAAU,KAAK,CAAQ,EACrB,CAAC,CAAQ,CAClB,CAEA,MAA4B,CAC1B,GAAI,KAAK,QAAU,QACjB,MAAU,MAAM,6CAA6C,EAE/D,IAAM,EAAY,KAAK,QAAQ,EAG/B,OAFA,EAAU,QAAS,GAAa,EAAS,KAAK,CAAC,EAC/C,KAAK,SAAS,SAAS,EAChB,CACT,CAEA,OAAc,CACR,KAAK,QAAU,YAGnB,KAAK,UAAU,QAAS,GAAa,EAAS,MAAM,CAAC,EACrD,KAAK,SAAS,QAAQ,EACxB,CAEA,QAAe,CACT,KAAK,QAAU,WAGnB,KAAK,UAAU,QAAS,GAAa,EAAS,KAAK,CAAC,EACpD,KAAK,SAAS,SAAS,EACzB,CAEA,MAAa,CACX,GAAI,KAAK,SACP,OAEF,IAAM,EAAc,KAAK,UAAU,OAAS,EAC5C,MAAM,KAAK,EACN,GACH,KAAK,YAAY,KAAK,YAAY,CAAE,KAAM,MAAO,CAAC,EAEpD,KAAK,eAAiB,EACtB,KAAK,cAAgB,GACrB,KAAK,WAAa,GAClB,KAAK,SAAS,SAAS,CACzB,CAEA,KAAK,EAAoB,CACvB,GAAI,CAAC,KAAK,WACR,MAAU,MAAM,oCAAoC,EAEtD,KAAK,WAAW,KAAK,CAAI,CAC3B,CAEA,KAAK,EAAmC,CACtC,MAAU,MAAM,oCAAoC,CACtD,CAEA,IAAI,cAAuB,CACzB,MAAO,EACT,CAEA,IAAI,aAAa,EAAe,CAAC,CAEjC,SAAgB,CACV,KAAK,WAGT,KAAK,KAAK,EACV,KAAK,SAAW,GAChB,KAAK,YAAY,QAAQ,EACzB,KAAK,WAAa,IAAA,GAClB,KAAK,QAAQ,oBAAoB,QAAS,KAAK,WAAW,EAC1D,KAAK,YAAY,KAAK,oBAAoB,UAAW,KAAK,oBAAqC,EAC/F,KAAK,YAAY,WAAW,EAC5B,KAAK,aAAa,mBAAmB,EACrC,MAAM,QAAQ,EAChB,CACF,ECpWa,GAAqB,GCkBrB,EAAqB,MC7B5B,EAAS,EAAI,KAAK,GAGxB,SAAS,GAAW,EAA8B,CAChD,IAAM,EAAM,IAAI,aAAa,CAAM,EACnC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,IAC1B,EAAI,GAAK,IAAO,EAAI,KAAK,IAAK,EAAS,EAAK,CAAM,GAEpD,OAAO,CACT,CAOA,SAAS,EAAS,EAAmB,CACnC,OAAO,EAAI,EAAS,KAAK,MAAM,EAAI,CAAM,CAC3C,CAGA,SAAS,GAAW,EAA4B,CAC9C,IAAI,EAAI,IAAS,EACjB,UAAa,CACX,GAAK,EACL,EAAK,EAAI,WAAc,EACvB,IAAI,EAAI,KAAK,KAAK,EAAK,IAAM,GAAK,EAAI,CAAC,EAEvC,MADA,GAAK,EAAI,KAAK,KAAK,EAAK,IAAM,EAAI,GAAK,CAAC,EAAK,IACpC,EAAK,IAAM,MAAS,GAAK,UACpC,CACF,CASA,IAAM,GAAN,KAAc,CACZ,KACA,KACA,KAAe,EAEf,YAAY,EAAkB,CAC5B,KAAK,KAAO,IAAI,aAAa,CAAQ,EACrC,KAAK,KAAO,IAAI,WAAW,CAAQ,CACrC,CAEA,OAAc,CACZ,KAAK,KAAO,CACd,CAEA,IAAI,QAAiB,CACnB,OAAO,KAAK,IACd,CAEA,KAAK,EAAa,EAAmB,CACnC,IAAI,EAAI,KAAK,OAIb,IAHA,KAAK,KAAK,GAAK,EACf,KAAK,KAAK,GAAK,EAER,EAAI,GAAG,CACZ,IAAM,EAAU,EAAI,GAAM,EAC1B,GAAI,KAAK,KAAK,IAAW,KAAK,KAAK,GAAI,MACvC,KAAK,KAAK,EAAG,CAAM,EACnB,EAAI,CACN,CACF,CAGA,KAAc,CACZ,IAAM,EAAS,KAAK,KAAK,GACnB,EAAO,EAAE,KAAK,KACpB,KAAK,KAAK,GAAK,KAAK,KAAK,GACzB,KAAK,KAAK,GAAK,KAAK,KAAK,GAEzB,IAAI,EAAI,EACR,OAAS,CACP,IAAM,EAAI,EAAI,EAAI,EACZ,EAAI,EAAI,EACV,EAAU,EAGd,GAFI,EAAI,KAAK,MAAQ,KAAK,KAAK,GAAK,KAAK,KAAK,KAAU,EAAU,GAC9D,EAAI,KAAK,MAAQ,KAAK,KAAK,GAAK,KAAK,KAAK,KAAU,EAAU,GAC9D,IAAY,EAAG,MACnB,KAAK,KAAK,EAAG,CAAO,EACpB,EAAI,CACN,CACA,OAAO,CACT,CAEA,KAAa,EAAW,EAAiB,CACvC,IAAM,EAAI,KAAK,KAAK,GACpB,KAAK,KAAK,GAAK,KAAK,KAAK,GACzB,KAAK,KAAK,GAAK,EACf,IAAM,EAAI,KAAK,KAAK,GACpB,KAAK,KAAK,GAAK,KAAK,KAAK,GACzB,KAAK,KAAK,GAAK,CACjB,CACF,EAuBA,SAAgB,EAAY,EAAqB,EAAgB,EAA2B,CAAC,EAAiB,CAC5G,GAAI,EAAE,EAAS,GACb,MAAU,MAAM,wCAAwC,GAAQ,EAQlE,IAAM,EAAI,EAAK,SAAW,KAC1B,GAAI,CAAC,OAAO,UAAU,CAAC,GAAK,EAAI,IAAO,EAAK,EAAI,EAC9C,MAAU,MAAM,yDAAyD,GAAG,EAE9E,IAAM,EAAK,EAAK,aAAe,EAAI,EAKnC,GAAI,CAAC,OAAO,UAAU,CAAE,GAAK,EAAK,GAAK,EAAK,EAC1C,MAAU,MAAM,sEAAsE,EAAE,SAAS,GAAI,EAEvG,IAAM,EAAK,KAAK,IAAI,EAAG,KAAK,MAAM,EAAK,CAAM,CAAC,EACxC,EAAM,EAAK,KAAO,KAClB,EAAO,GAAW,EAAK,YAAc,CAAC,EAGtC,EADM,EAAI,EACI,EAQd,EAAM,GAAW,CAAC,EAMlB,EAAM,EACN,EAAY,EAAM,OAAS,EAAI,EAC/B,EAAS,IAAI,aAAa,CAAS,EACzC,EAAO,IAAI,EAAO,CAAG,EAErB,IAAM,EAAU,KAAK,IAAI,EAAG,KAAK,OAAO,EAAY,GAAK,CAAE,EAAI,CAAC,EAG1D,EAAM,IAAI,EAAA,QAAI,CAAC,EAGf,EAAY,IAAI,aAAa,CAAC,EAC9B,EAAO,EAAI,mBAAmB,EAG9B,EAA0B,MAAM,CAAO,EACvC,EAA4B,MAAM,CAAO,EAE/C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAAK,CAChC,IAAM,EAAQ,EAAI,EAClB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IACrB,EAAU,GAAK,EAAO,EAAQ,GAAK,EAAI,GAEzC,EAAI,cAAc,EAAM,CAAS,EACjC,IAAM,EAAI,IAAI,aAAa,CAAK,EAC1B,EAAK,IAAI,aAAa,CAAK,EACjC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC9B,IAAM,EAAK,EAAK,EAAI,GACd,EAAK,EAAK,EAAI,EAAI,GACxB,EAAE,GAAK,KAAK,MAAM,EAAI,CAAE,EACxB,EAAG,GAAK,KAAK,MAAM,EAAI,CAAE,CAC3B,CACA,EAAI,GAAK,EACT,EAAM,GAAK,CACb,CAwCA,IAAM,EAA4B,MAAM,CAAO,EACzC,EAA4B,MAAM,CAAO,EAE/C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAC3B,EAAM,GAAK,IAAI,aAAa,CAAK,EACjC,EAAM,GAAK,IAAI,aAAa,CAAK,EAGnC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAAK,CAChC,IAAM,EAAK,EAAM,GACX,EAAS,EAAI,EAAI,EAAM,EAAI,GAAK,KAChC,EAAS,EAAI,EAAU,EAAI,EAAM,EAAI,GAAK,KAEhD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAE9B,IAAM,EAAY,EAAS,EAAI,EAAM,EAKjC,EACJ,AASE,EATE,GAAU,EAGP,IAFQ,EAAS,EAAG,GAAK,EAAO,GAAK,CAAQ,EAAI,GAC1C,EAAS,EAAO,GAAK,EAAG,GAAK,CAAQ,EAAI,IAE5C,EACJ,EAAS,EAAG,GAAK,EAAO,GAAK,CAAQ,EAAI,EACrC,EACJ,EAAS,EAAO,GAAK,EAAG,GAAK,CAAQ,EAAI,EAEzC,EAEP,EAAM,GAAG,GAAK,EAKd,EAAM,GAAG,GAAK,EAAI,EAAQ,EAAI,EAAS,EAAG,EAAI,GAAK,EAAG,EAAE,EAAI,CAC9D,CACF,CAQA,IAAM,EAAQ,EAAK,EACb,EAAiC,MAAM,CAAO,EACpD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAAK,EAAW,GAAK,IAAI,aAAa,CAAK,EAIxE,IAAM,EAAO,IAAI,GAAQ,EAAI,CAAK,EAC5B,EAAW,IAAI,WAAW,CAAK,EAC/B,EAAQ,IAAI,WAAW,CAAK,EAElC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAAK,CAChC,IAAM,EAAI,EAAI,GACR,EAAQ,EAAI,EAAI,EAAI,EAAI,GAAK,KAC7B,EAAO,EAAW,GAClB,EAAW,EAAI,EAAI,EAAW,EAAI,GAAK,KAGzC,EAAO,EACX,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACrB,EAAE,GAAK,IAAM,EAAO,EAAE,IACtB,GAAS,EAAM,GAAK,IAAM,EAAO,EAAM,IAE7C,IAAM,EAAS,EAAM,EAIrB,EAAK,MAAM,EACX,IAAI,EAAY,EAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACzB,EAAS,GAAK,EACV,EAAE,GAAK,GACT,EAAM,GAAK,EACX,MAEA,EAAM,GAAK,EACX,EAAK,GAAK,EAAS,EAAK,EACxB,EAAS,GAAK,GAIlB,GAAI,IAAM,GAAK,CAAC,GAAY,CAAC,EAAO,CAYlC,KAAO,EAAY,GAAG,CACpB,IAAI,EAAO,GACP,EAAU,GACd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACrB,EAAM,IAAM,CAAC,EAAS,IAAM,EAAE,GAAK,IACrC,EAAU,EAAE,GACZ,EAAO,GAGX,GAAI,EAAO,EAAG,MAWd,IAVA,EAAK,GAAQ,EAAM,GAAG,GACtB,EAAS,GAAQ,EACjB,IACA,EAAK,KAAK,EAAE,GAAQ,GAAQ,EAAK,CAAC,EAO3B,EAAK,OAAS,GAAG,CAEtB,IAAM,EADM,EAAK,IACN,GAAO,EACd,EAAK,EAAI,GAAS,EAAM,EAAK,IAAM,CAAC,EAAS,EAAK,KACpD,EAAK,EAAK,GAAK,EAAK,GAAM,EAAQ,EAAM,GAAG,GAC3C,EAAS,EAAK,GAAK,EACnB,IACA,EAAK,KAAK,EAAE,EAAK,GAAM,EAAK,GAAM,EAAK,CAAC,GAEtC,EAAK,GAAK,GAAK,EAAM,EAAK,IAAM,CAAC,EAAS,EAAK,KACjD,EAAK,EAAK,GAAK,EAAK,GAAM,EAAQ,EAAM,GAAG,EAAK,GAChD,EAAS,EAAK,GAAK,EACnB,IACA,EAAK,KAAK,EAAE,EAAK,GAAM,EAAK,GAAM,EAAK,CAAC,EAE5C,CACF,CACA,QACF,CAIA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACrB,EAAM,IAER,EAAK,KAAK,EAAM,GAAK,GAAK,EAAK,CAAC,EAKpC,KAAO,EAAK,OAAS,GAAK,EAAY,GAAG,CACvC,IAAM,EAAM,EAAK,IAAI,EACf,EAAK,GAAO,EACN,EAAM,GAsBZ,EAAK,EAAI,GAAS,EAAM,EAAK,IAAM,CAAC,EAAS,EAAK,KACpD,EAAK,EAAK,GAAK,EAAK,GAAM,EAAQ,EAAM,GAAG,GAC3C,EAAS,EAAK,GAAK,EACnB,IACA,EAAK,KAAK,EAAE,EAAK,GAAM,EAAK,GAAM,EAAK,CAAC,GAEtC,EAAK,GAAK,GAAK,EAAM,EAAK,IAAM,CAAC,EAAS,EAAK,KACjD,EAAK,EAAK,GAAK,EAAK,GAAM,EAAQ,EAAM,GAAG,EAAK,GAChD,EAAS,EAAK,GAAK,EACnB,IACA,EAAK,KAAK,EAAE,EAAK,GAAM,EAAK,GAAM,EAAK,CAAC,IAvBtC,EAAM,IAAO,CAAC,EAAS,KACzB,EAAK,GAAM,EAAS,GAAM,GAAM,GAAS,EAAM,EAAI,GAAG,GAAM,EAAM,GAAG,IACrE,EAAS,GAAM,EACf,IACA,EAAK,KAAK,EAAE,GAAM,GAAM,EAAK,CAAC,EAsBpC,CAKA,GAAI,EAAY,MACT,IAAI,EAAI,EAAG,EAAI,EAAO,IACrB,EAAM,IAAM,CAAC,EAAS,KACxB,EAAK,GAAK,EAAS,GAAK,GAAM,GAAS,EAAM,EAAI,GAAG,GAAK,EAAM,GAAG,IAClE,EAAS,GAAK,EACd,IAIR,CAOA,IAAM,EAAS,KAAK,IAAI,EAAG,KAAK,MAAM,EAAM,OAAS,CAAM,CAAC,EACtD,EAAW,EACX,GAAe,EAAU,GAAK,EAAK,EAAI,EACvC,EAAM,IAAI,aAAa,CAAW,EAClC,EAAY,IAAI,aAAa,CAAW,EAExC,EAAU,EAAI,mBAAmB,EACjC,EAAU,EAAI,mBAAmB,EAEvC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,IAAK,CAChC,IAAM,EAAI,EAAI,GACR,EAAO,EAAW,GAExB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC9B,IAAM,EAAK,EAAE,GAAK,KAAK,IAAI,EAAK,EAAE,EAC5B,EAAK,EAAE,GAAK,KAAK,IAAI,EAAK,EAAE,EAClC,EAAQ,EAAI,GAAK,EACjB,EAAQ,EAAI,EAAI,GAAK,CACvB,CAGA,EAAI,iBAAiB,CAAO,EAC5B,EAAI,iBAAiB,EAAS,CAAO,EAErC,IAAM,EAAQ,EAAI,EAClB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,IAAM,EAAI,EAAI,GAEd,EAAI,EAAQ,IAAM,EAAQ,EAAI,GAAK,EACnC,EAAU,EAAQ,IAAM,EAAI,CAC9B,CACF,CAKA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAC3B,EAAU,GAAK,OAAM,EAAI,IAAM,EAAU,IAG/C,IAAM,EAAS,KAAK,MAAM,EAAM,CAAM,EAChC,EAAM,IAAI,aAAa,CAAM,EACnC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,IAAK,CAC/B,IAAM,EAAM,EAAS,EACrB,EAAI,GAAK,EAAM,EAAc,EAAI,GAAO,CAC1C,CACA,OAAO,CACT,CAOA,SAAgB,GACd,EACA,EACA,EAA2B,CAAC,EACZ,CAChB,OAAO,EAAS,IAAK,GAAO,EAAY,EAAI,EAAQ,CAAI,CAAC,CAC3D,CC7gBA,IAAa,GAAb,MAAa,UAAiB,CAAkC,CAgCrD,OA/BT,QAEA,UAAuB,EACvB,YAAsB,EACtB,OACA,QAA0B,EAC1B,WAA6B,EAC7B,cAAgC,EAQhC,kBACA,cACA,eACA,iBAMA,aAA+B,EAM/B,YACE,EACA,EACA,EACA,CACA,MAAM,CAAM,EAJL,KAAA,OAAA,EAKP,KAAK,QAAU,EAAO,QACtB,KAAK,UAAY,EAAO,UACxB,KAAK,WAAW,EAAO,QAAS,EAAO,OAAO,EAC9C,KAAK,OAAS,EACV,WAAY,GAAU,EAAO,SAC/B,KAAK,OAAS,EAAO,QAEvB,KAAK,gBAAgB,CAAM,EAC3B,KAAK,YAAY,CAAQ,EACzB,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAO,EACtD,KAAK,OAAQ,QAAQ,KAAK,QAAS,CACrC,CAEA,WAAoB,EAAkB,EAAiC,CACrE,IAAM,EAAiB,KAAK,OAC5B,MAAM,WAAW,EAAS,CAAY,EAElC,OAAK,SAAW,GAAkB,CAAC,KAAK,QAAU,CAAC,KAAK,QAAU,CAAC,KAAK,YAI5E,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAM,EACrD,KAAK,OAAO,QAAQ,KAAK,QAAQ,EACnC,CAEA,gBAAwB,EAAoB,CAC1C,GAAI,iBAAkB,GAAU,EAAO,aACrC,EAAO,aAAa,QAAU,KAAK,eAC9B,GAAI,YAAa,EACtB,EAAO,QAAU,KAAK,eAEtB,MAAU,MAAM,yBAAyB,CAE7C,CAQA,IAAI,UAAW,CACb,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,yDAAyD,EAQ3E,MANI,iBAAkB,KAAK,QAAU,KAAK,OAAO,aACxC,KAAK,OAAO,aAAa,SAE7B,KAAK,QAGH,KAAK,OAAO,UAFV,GAGX,CAMA,IAAI,cAAe,CACjB,OAAO,KAAK,aACd,CAEA,IAAI,QAAiB,CACnB,OAAO,MAAM,MACf,CAEA,IAAI,OAAO,EAAgB,CACzB,MAAM,OAAS,EACf,KAAK,KAAK,eAAgB,CAAM,CAClC,CAOA,IAAI,aAAa,EAAc,CAC7B,GAAI,GAAQ,EACV,MAAU,MAAM,sCAAsC,EAExD,GAAI,KAAK,SAAW,UAAW,CAC7B,IAAM,GAAW,KAAK,QAAQ,YAAc,KAAK,YAAc,KAAK,cACpE,KAAK,SAAW,EAChB,KAAK,WAAa,KAAK,QAAQ,WACjC,CACA,KAAK,cAAgB,EAChB,KAAK,SAGN,iBAAkB,KAAK,SACzB,KAAK,OAAO,aAAa,MAAQ,GAE/B,iBAAkB,KAAK,QAAU,KAAK,OAAO,eAC/C,KAAK,OAAO,aAAa,aAAe,GAE5C,CAOA,cAAwB,CAClB,MAAC,KAAK,QAAU,KAAK,SAAW,WAQpC,IAAI,iBAAkB,KAAK,QAAU,KAAK,kBAAmB,CAC3D,KAAU,kBAAkB,SAAW,KAAK,cAAc,CAAC,EAC3D,MACF,CAEA,KAAK,cAAc,CAFnB,CAGF,EAEA,eAA8B,CACxB,CAAC,KAAK,QAAU,KAAK,SAAW,YAIpC,KAAK,cAED,KAAK,YAAc,YAAc,KAAK,YAAc,KAAK,WAE3D,KAAK,KAAK,QAAS,IAAA,EAAS,EACxB,KAAK,eAMP,KAAU,QAAQ,KAAK,eAAe,SAAU,KAAK,eAAe,IAAI,EAAE,SAAW,CAC/E,KAAK,SAAW,WAAa,KAAK,SACpC,KAAK,KAAK,EACV,KAAK,iBAAiB,EAE1B,CAAC,GAED,KAAK,KAAK,EACV,KAAK,iBAAiB,KAGxB,KAAK,mBAAmB,EACxB,KAAK,KAAK,CAAC,EAGP,KAAK,SAAW,WAAa,KAAK,QAAU,EAAE,iBAAkB,KAAK,SAGvE,KAAK,KAAK,EAIR,KAAK,eAAe,SAAW,KAAK,WACtC,KAAK,SAAS,KAAK,eAAe,KAAQ,KAAK,SAAS,QAAQ,WAAW,EAC3E,KAAK,OAAO,KAAK,cAAc,aAAc,KAAK,cAAc,SAAU,KAAK,cAAc,IAAI,IAGvG,CAQA,MAAe,CACb,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,8CAA8C,EAGhE,GAAI,KAAK,SAAW,UAClB,MAAO,CAAC,IAAI,EAGd,IAAM,EAAW,KAAK,SAAW,SAEjC,GAAI,CACF,IAAI,EAyBJ,GAvBI,KAAK,SAAW,SAEd,iBAAkB,KAAK,QAAU,KAAK,OAAO,aAC/C,EAAmB,KAAK,OAAO,aAAa,KAAK,GAGjD,KAAK,eAAe,EAChB,UAAW,KAAK,QAAU,KAAK,OAAO,OACxC,KAAK,OAAO,MAAM,EAAG,KAAK,OAAO,GAMjC,iBAAkB,KAAK,QAAU,KAAK,OAAO,cAC/C,KAAK,OAAO,aAAa,YAAc,KAAK,QAC5C,EAAmB,KAAK,OAAO,aAAa,KAAK,GACxC,UAAW,KAAK,QAAU,KAAK,OAAO,QAC/C,KAAK,eAAe,EACpB,KAAK,OAAO,MAAM,EAAG,KAAK,OAAO,GAIjC,EAAkB,CAKpB,IAAM,EAAU,EACb,SACO,CACJ,KAAK,WAAa,KAAK,QAAQ,YAC/B,KAAK,gBAAgB,CAAQ,CAC/B,EACC,GAAiB,CAChB,KAAU,UAAU,QAAS,CAC3B,QACA,UAAW,SACX,UAAW,KAAK,IAAI,EACpB,YAAa,EACf,CAAC,CACH,CACF,EACC,YAAc,CACT,KAAK,oBAAsB,IAC7B,KAAK,kBAAoB,IAAA,GAE7B,CAAC,EACH,KAAK,kBAAoB,CAC3B,MAEE,KAAK,WAAa,KAAK,QAAQ,YAC/B,KAAK,gBAAgB,CAAQ,EAG/B,MAAO,CAAC,IAAI,CACd,OAAS,EAAO,CAOd,MANA,KAAK,UAAU,QAAS,CACf,QACP,UAAW,SACX,UAAW,KAAK,IAAI,EACpB,YAAa,EACf,CAAC,EACK,CACR,CACF,CAEA,OAAc,CACZ,GAAI,CAAC,KAAK,QAAU,KAAK,SAAW,UAClC,OAGF,IAAM,GAAW,KAAK,QAAQ,YAAc,KAAK,YAAc,KAAK,cACpE,KAAK,SAAW,EAEZ,iBAAkB,KAAK,QAAU,KAAK,OAAO,aAC/C,KAAK,OAAO,aAAa,MAAM,EACtB,SAAU,KAAK,QAGxB,KAAK,OAAO,KAAK,EAGnB,KAAK,WAAW,CAClB,CAEA,MAAa,CACX,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,8CAA8C,EAE5D,KAAK,SAAW,WAAa,KAAK,SAAW,aAIjD,KAAK,WAAW,EAEZ,SAAU,KAAK,QAAU,KAAK,SAAW,WAC3C,KAAK,OAAO,KAAK,EAEf,iBAAkB,KAAK,QAAU,KAAK,OAAO,eAC/C,KAAK,OAAO,aAAa,MAAM,EAC/B,KAAK,OAAO,aAAa,YAAc,GAGzC,KAAK,QAAU,EACf,KAAK,WAAa,EAClB,KAAK,YAAY,EACnB,CAUA,OAAO,EAAkB,EAAiB,EAAgD,CASxF,OARI,GAAS,UACX,KAAK,cAAgB,CACnB,WACA,KAAM,GAAQ,SACd,QAAS,GACT,aAAc,KAAK,SAAU,KAAK,KACpC,GAEK,MAAM,OAAO,EAAU,CAAI,CACpC,CAOA,iBAAiB,EAAkB,EAAuB,CACxD,KAAK,eAAiB,CAAE,WAAU,KAAM,GAAQ,QAAS,CAC3D,CAQA,aAAa,EAAkB,EAAgC,CAO7D,OAAO,KAAK,QAAQ,EAAU,CAAI,EAAE,SAAW,CACzC,KAAK,SAAW,WAAa,KAAK,QACpC,KAAK,KAAK,CAEd,CAAC,CACH,CAEA,KAAK,EAAoB,CACvB,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,UAAY,CAAC,KAAK,OAC1C,MAAU,MAAM,8CAA8C,EAEhE,GAAI,CAAC,OAAO,SAAS,CAAI,GAAK,EAAO,EACnC,MAAU,MAAM,6BAA6B,EAG/C,GACE,KAAK,OAAO,YAAc,aAC1B,iBAAkB,KAAK,SACtB,KAAK,OAAO,aAAa,UAAU,QAAU,KAAO,EAKrD,MAHI,KAAK,OAAO,aAAa,WAAa,IAC9B,MAAM,2DAA2D,EAEnE,MAAM,iEAAiE,EAGnF,IAAM,EAAa,KAAK,SAAW,UAC/B,GACF,KAAK,MAAM,EAGb,KAAK,QAAU,EAEX,iBAAkB,KAAK,QAAU,KAAK,OAAO,eAC/C,KAAK,OAAO,aAAa,YAAc,GAIrC,GACF,KAAK,KAAK,CAEd,CAEA,IAAI,aAAsB,CACxB,GAAI,KAAK,SAAW,UAAW,CAC7B,IAAM,GAAW,KAAK,QAAQ,YAAc,KAAK,YAAc,KAAK,cACpE,OAAO,KAAK,QAAU,CACxB,MACE,OAAO,KAAK,OAEhB,CAEA,kBAAiC,CAC/B,IAAM,EAAM,KAAK,OAAO,UAAU,QAAQ,IAAI,EAC1C,IAAQ,IACV,KAAK,OAAO,UAAU,OAAO,EAAK,CAAC,CAEvC,CAEA,gBAAyB,CACvB,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,QAAU,CAAC,KAAK,SAAW,CAAC,KAAK,SACzD,MAAU,MAAM,4DAA4D,EAE9E,GAAI,CACE,KAAK,SAIH,EAAE,iBAAkB,KAAK,SAAW,YAAa,KAAK,SACxD,KAAK,OAAO,QAAU,MAExB,KAAK,OAAO,WAAW,GAEzB,KAAK,OAAS,KAAK,QAAQ,mBAAmB,EAC9C,KAAK,OAAO,OAAS,KAAK,OAC1B,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAM,EACrD,KAAK,OAAO,QAAU,KAAK,UAC3B,KAAK,aAAe,KAAK,aAC3B,OAAS,EAAO,CAOd,MANA,KAAK,UAAU,QAAS,CACf,QACP,UAAW,SACX,UAAW,KAAK,IAAI,EACpB,YAAa,EACf,CAAC,EACK,CACR,CACF,CAMA,IAAI,WAAW,EAAe,CAC5B,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,qDAAqD,EAEnE,SAAU,KAAK,SACjB,KAAK,OAAO,KAAO,GAEjB,iBAAkB,KAAK,QAAU,KAAK,OAAO,eAC/C,KAAK,OAAO,aAAa,KAAO,EAEpC,CAOA,SAAgB,CACT,QAAK,OAGV,IAAI,iBAAkB,KAAK,QAAU,KAAK,OAAO,aAAc,CAC7D,IAAM,EAAe,KAAK,OAAO,aACjC,EAAa,MAAM,EACnB,GAAI,CACF,EAAa,YAAc,CAC7B,MAAQ,CAER,CACA,EAAa,KAAO,GACpB,EAAa,QAAU,KACvB,EAAa,IAAM,GACnB,EAAa,KAAK,CACpB,MAAW,YAAa,KAAK,SAC3B,KAAK,OAAO,QAAU,MAExB,KAAK,QAAU,EACf,KAAK,WAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAc,EACnB,KAAK,OAAO,WAAW,EACvB,KAAK,OAAS,IAAA,GACd,MAAM,QAAQ,CARU,CAS1B,CAEA,oBAAmC,CACjC,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,UAAY,CAAC,KAAK,OAC1C,MAAU,MAAM,8DAA8D,CAElF,CAEA,UAAU,EAAgC,CACxC,KAAK,mBAAmB,EACxB,MAAM,UAAU,CAAM,CACxB,CAEA,aAAa,EAAgC,CAC3C,KAAK,mBAAmB,EACxB,MAAM,aAAa,CAAM,CAC3B,CASA,KAAK,EAAkC,CACrC,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,8CAA8C,EAMhE,GAJI,IAAc,IAAA,KAChB,KAAK,UAAY,IAAc,WAAa,WAAa,KAAK,IAAI,EAAG,CAAS,EAC9E,KAAK,YAAc,GAEjB,iBAAkB,KAAK,QAAU,KAAK,OAAO,aAAc,CAC7D,IAAM,EAAe,KAAK,OAAO,aACjC,EAAa,KAAO,KAAK,YAAc,UACzC,MAAO,GAAI,SAAU,KAAK,OACxB,KAAK,OAAO,KAAO,KAAK,YAAc,WAClC,KAAK,OAAO,SACd,KAAK,OAAO,QAAU,KAAK,OAAO,OAAO,SACzC,KAAK,OAAO,UAAY,QAG1B,MAAU,MAAM,yBAAyB,EAE3C,OAAO,KAAK,SACd,CAiBA,MAAM,cAAc,EAA+B,CAEjD,GADA,KAAK,mBAAmB,EACpB,CAAC,OAAO,SAAS,CAAM,GAAK,GAAU,EACxC,MAAU,MAAM,2CAA2C,EAE7D,KAAK,aAAe,EACpB,IAAM,EAAc,KAAK,aACzB,GAAI,CAAC,EACH,MAAU,MAAM,sEAAsE,EAExF,IAAI,EAAiB,EAAY,MAAM,KAAM,GAAS,CACpD,IAAM,EAAc,EAA0B,WAC9C,OAAO,GAAc,OAAO,EAAW,KAAQ,YAAc,EAAW,IAAI,aAAa,IAAM,IAAA,EACjG,CAAC,EASD,GAAI,IAAW,EAAG,CACZ,GACF,EAAY,OAAO,CAAc,EAEnC,MACF,CAEA,GAAI,CAAC,EAAgB,CACnB,IAAM,EAAY,KAAK,OAAO,UAC9B,GAAI,CAAC,EACH,MAAU,MAAM,qEAAqE,EAEvF,EAAiB,MAAM,EAAU,mBAAmB,EAAS,aAAc,CAAC,EAAG,KAAK,OAAO,EAC3F,EAAY,IAAI,CAAc,CAChC,CAEA,IAAM,EAAa,EAAe,YAAY,IAAI,aAAa,EAC3D,IACF,EAAW,MAAQ,EAEvB,CAKA,IAAI,YAAqB,CACvB,OAAO,KAAK,YACd,CAgBA,IAAI,YAAuB,CACzB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,kEAAkE,EAEpF,OAAO,KAAK,QACd,CAaA,QAAQ,EAAgD,CACtD,OAAO,KAAK,WAAW,QAAQ,CAAkB,CACnD,CAiBA,WAAW,EAA4C,CACjD,EACF,KAAK,WAAW,WAAW,CAAkB,EAE7C,KAAK,WAAW,WAAW,CAE/B,CAUA,MAAM,EAA6C,CAAC,EAAa,CAC/D,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,UAAY,CAAC,KAAK,QAC1C,MAAU,MAAM,+CAA+C,EAEjE,IAAM,EAAU,EAAU,SAAW,KAAK,QAEpC,EAAW,KAAK,QAAQ,WAAW,EACzC,EAAS,QAAQ,KAAK,OAAO,wBAAwB,CAAC,EAEtD,IAAI,EACJ,GAAI,WAAY,KAAK,QAAU,KAAK,OAAO,OACzC,EAAS,KAAK,QAAQ,mBAAmB,EACzC,EAAO,OAAS,KAAK,OAAO,YACvB,GAAI,iBAAkB,KAAK,QAAU,KAAK,OAAO,aAAc,CACpE,GAAI,CAAC,KAAK,QAAQ,yBAChB,MAAU,MAAM,gEAAgE,EAOlF,IAAM,EADkB,KAAK,OAAO,aACE,UAAU,EAAI,EACpD,EAAS,KAAK,QAAQ,yBAAyB,CAAa,CAC9D,MACE,MAAU,MAAM,yBAAyB,EAE3C,IAAM,EAAY,EAAU,YAAc,IAAA,GAAkC,KAAK,UAA3B,EAAU,UAC1D,EAAQ,IAAI,EAAS,KAAK,OAAQ,EAAQ,CAAQ,EA2CxD,MAxCA,GAAM,UAAY,EAClB,EAAM,YAAc,KAAK,YACzB,EAAM,WAAW,EAAS,KAAK,OAAO,EACtC,EAAM,OAAS,KAAK,OACpB,EAAM,aAAe,KAAK,cAC1B,EAAM,QAAU,KAAK,QACjB,KAAK,gBACP,EAAM,cAAgB,CAAE,GAAG,KAAK,aAAc,GAE5C,KAAK,iBACP,EAAM,eAAiB,CAAE,GAAG,KAAK,cAAe,GAE9C,KAAK,SAAW,SAClB,EAAM,WAAW,EACR,KAAK,SAAW,WACzB,EAAM,YAAY,EAIpB,KAAK,SAAS,QAAS,GAAW,CAChC,IAAM,EAAe,KAAK,QAAQ,mBAAmB,EACrD,EAAa,KAAO,EAAO,KAC3B,EAAa,UAAU,MAAQ,EAAO,UAAU,MAChD,EAAa,EAAE,MAAQ,EAAO,EAAE,MAChC,EAAa,KAAK,MAAQ,EAAO,KAAK,MACtC,EAAM,UAAU,CAA2C,CAC7D,CAAC,EAEG,KAAK,eAAiB,GACxB,EAAW,cAAc,KAAK,YAAY,EAAE,UAAY,CAExD,CAAC,EAIC,KAAK,SAAW,WAClB,EAAM,KAAK,EAGb,KAAK,OAAO,UAAU,KAAK,CAAK,EACzB,CACT,CACF,ECjvBa,EAAb,MAAa,UAAc,CAAoC,CA4BpD,IAGG,eACH,UACA,QACC,WAEA,6BAlCV,OACA,mBACA,QACA,UAAuB,EACvB,cAAgC,EAOhC,aAA+B,EAC/B,aAAuD,IAAI,EAC3D,UAA0C,CAAE,QAAS,CAAC,EAAG,UAAW,CAAC,EAAG,cAAe,CAAC,CAAE,EAC1F,iBAAmC,CAAC,EACpC,sBASA,sBAAsE,IAAI,QAC1E,YACE,EACA,EACA,EACA,EACA,EAA8B,SAC9B,EAA0B,OAC1B,EACA,EACA,EACA,CACA,MAAM,EAVC,KAAA,IAAA,EAGG,KAAA,eAAA,EACH,KAAA,UAAA,EACA,KAAA,QAAA,EACC,KAAA,WAAA,EAEA,KAAA,6BAAA,EAGR,KAAK,OAAS,EACd,KAAK,QAAU,EACf,KAAK,sBAAwB,EAC7B,KAAK,YAAY,wBAAwB,KAAM,KAAK,UAAW,KAAK,gBAAgB,CACtF,CAEA,IAAI,WAAmC,CACrC,OAAO,KAAK,UACd,CAEA,IAAI,QAAiB,CACnB,OAAO,MAAM,MACf,CAMA,GAAgC,EAAU,EAAsD,CAC9F,OAAO,KAAK,aAAa,GAAG,EAAO,CAAQ,CAC7C,CAKA,IAAiC,EAAU,EAAgD,CACzF,KAAK,aAAa,IAAI,EAAO,CAAQ,CACvC,CAEA,KAA4C,EAAU,EAA4B,CAChF,KAAK,aAAa,KAAK,EAAO,CAAI,CACpC,CAEA,MAAgB,UAAuC,EAAU,EAAqC,CACpG,OAAO,KAAK,aAAa,UAAU,EAAO,CAAI,CAChD,CAMA,gBAAgB,EAAc,EAA4B,CACxD,KAAU,UAAU,aAAc,CAChC,IAAK,KAAK,IACV,QACA,UAAW,OACX,UAAW,KAAK,IAAI,EACpB,aACF,CAAC,CACH,CAcA,MAAM,EAA0C,CAAC,EAAU,CACzD,IAAM,EAAU,EAAU,SAAW,KAAK,QACpC,EAAY,EAAU,YAAc,IAAA,GAAkC,KAAK,UAA3B,EAAU,UAC1D,EAAY,EAAU,YAAc,IAAA,GAAkC,KAAK,UAA3B,EAAU,UAC1D,EAAe,EAAU,cAAgB,KAAK,aAC9C,EAAS,EAAU,SAAW,IAAA,GAA+B,KAAK,OAAxB,EAAU,OACpD,EAAW,EAAU,WAAa,IAAA,GAAiC,KAAK,SAA1B,EAAU,SACxD,EAAU,EAAU,SAAS,OAAS,EAAU,QAAU,KAAK,SAE/D,EAAQ,IAAI,EAChB,KAAK,IACL,KAAK,OACL,KAAK,QACL,KAAK,eACL,KAAK,UACL,EACA,KAAK,SACP,EAgBA,OAfA,EAAM,KAAK,CAAS,EACpB,EAAM,aAAe,EACrB,EAAM,OAAS,EACX,IAAY,QAEV,EAAU,gBAAkB,IAAA,GAErB,KAAK,UAAY,SAC1B,EAAM,cAAgB,KAAK,eAF3B,EAAM,cAAgB,EAAU,cAIlC,EAAM,SAAW,GAEjB,EAAM,UAAY,EAEpB,EAAM,WAAW,CAAO,EACjB,CACT,CAOA,SAAsB,CAIpB,IAAM,EAAa,KAAK,UAAU,QAAQ,OACpC,EAAe,KAAK,UAAU,UAAU,OACxC,EAAmB,KAAK,UAAU,cAAc,OAChD,EAAe,KAAK,UAAU,OAIhC,EACA,EACJ,GAAI,CACF,IAAI,EACJ,GAAI,KAAK,OACP,EAAS,KAAK,QAAQ,mBAAmB,EACzC,EAAO,OAAS,KAAK,WAChB,CACL,GAAI,CAAC,KAAK,QAAQ,yBAChB,MAAU,MACR,4HACF,EAEF,IAAM,EAAQ,KAAK,uBAAyB,IAAI,MAChD,EAAM,YAAc,YACpB,EAAM,IAAM,KAAK,IACjB,EAAM,QAAU,OAEhB,EAAS,KAAK,QAAQ,yBAAyB,CAAK,EAChD,IAAU,KAAK,wBACjB,KAAK,sBAAwB,IAAA,GAEjC,CACA,EAAgB,EAChB,IAAM,EAAW,KAAK,QAAQ,WAAW,EACzC,EAAkB,EAIlB,IAAM,EAAoB,KAAK,wBAAwB,EACvD,EAAS,QAAQ,CAAiB,EAClC,IAAM,EAAW,IAAI,GAAS,KAAM,EAAQ,CAAQ,EACpD,KAAK,iBAAiB,CAAQ,EAC9B,KAAK,UAAU,QAAQ,KAAK,CAAM,EAClC,KAAK,UAAU,UAAU,KAAK,CAAQ,EAClC,iBAAkB,GAAU,EAAO,cACrC,KAAK,UAAU,cAAc,KAAK,EAAO,YAAY,EAEvD,EAAS,YAAY,CAAQ,EAC7B,EAAS,OAAS,KAAK,OACvB,EAAS,aAAe,KAAK,aAC7B,EAAS,KAAK,KAAK,SAAS,EAKxB,KAAK,eAAiB,GACxB,EAAc,cAAc,KAAK,YAAY,EAAE,UAAY,CAE3D,CAAC,EAEC,KAAK,UAAY,QACnB,EAAS,cAAgB,KAAK,cAC9B,EAAS,SAAW,KAAK,UAChB,KAAK,UAAY,WAC1B,EAAS,UAAY,KAAK,WAO5B,IAAM,EAAa,EAAS,GAAG,YAAe,CAC5C,KAAK,KAAK,QAAS,IAAA,EAAS,EAG5B,KAAK,yBAAyB,CAAQ,CACxC,CAAC,EACD,EAAS,qBAAyB,CAChC,KAAK,KAAK,UAAW,IAAA,EAAS,CAChC,EACA,IAAM,EAAa,EAAS,GAAG,QAAU,GAAe,CACtD,KAAK,UAAU,aAAc,CAC3B,IAAK,KAAK,IACV,MAAO,EAAW,MAClB,UAAW,WACX,UAAW,EAAW,UACtB,YAAa,EAAW,WAC1B,CAAC,CACH,CAAC,EAWD,OAHA,KAAK,sBAAsB,IAAI,EAAU,CAAC,EAAY,MALtB,CAC9B,AACE,EAAS,mBAAmB,IAAA,EAEhC,CACmF,CAAC,EAEpF,KAAK,UAAU,KAAK,CAAQ,EACrB,CAAC,CAAQ,CAClB,OAAS,EAAO,CASd,GAJA,KAAK,UAAU,QAAQ,OAAS,EAChC,KAAK,UAAU,UAAU,OAAS,EAClC,KAAK,UAAU,cAAc,OAAS,EACtC,KAAK,UAAU,OAAS,EACpB,EACF,GAAI,CACF,EAAgB,WAAW,CAC7B,MAAQ,CAGR,CAEF,GAAI,EACF,GAAI,CACF,EAAc,WAAW,CAC3B,MAAQ,CAER,CAEF,IAAM,EAAa,CACjB,IAAK,KAAK,IACH,QACP,UAAW,WACX,UAAW,KAAK,IAAI,EACpB,YAAa,EACf,EAEA,MADA,KAAK,UAAU,aAAc,CAAU,EACjC,CACR,CACF,CAQA,yBAAiC,EAA0B,CACzD,IAAM,EAAS,KAAK,sBAAsB,IAAI,CAAQ,EACjD,KAGL,MAAK,sBAAsB,OAAO,CAAQ,EAC1C,IAAK,IAAM,KAAS,EAClB,EAAM,CAFkC,CAI5C,CAEA,KAAK,EAAoD,CACvD,IAAM,EAAY,KAAK,QAAQ,EAE/B,IAAK,IAAM,KAAY,EAAW,CAChC,IAAM,EAAiB,GAA0C,CAC/D,EAAqB,EACrB,KAAK,KAAK,OAAQ,CAAgB,CACpC,EACM,MAA6B,CACjC,EAAS,IAAI,OAAQ,CAAa,EAClC,EAAS,IAAI,QAAS,CAAoB,CAC5C,EAEA,EAAS,GAAG,OAAQ,CAAa,EACjC,EAAS,GAAG,QAAS,CAAoB,EAEzC,GAAI,CACF,EAAS,KAAK,CAChB,OAAS,EAAO,CACd,EAAqB,EAKrB,IAAM,EAAM,KAAK,UAAU,QAAQ,CAAQ,EAK3C,MAJI,IAAQ,IACV,KAAK,UAAU,OAAO,EAAK,CAAC,EAE9B,KAAK,yBAAyB,CAAQ,EAChC,CACR,CACF,CAEA,GAAI,EACF,IAAK,IAAM,KAAY,EACjB,EAAQ,SAAW,IAAA,IACrB,EAAS,OAAO,EAAQ,OAAQ,EAAQ,SAAU,CAAE,QAAS,EAAQ,aAAc,CAAC,EAElF,EAAQ,UAAY,IAAA,IACtB,EAAS,iBAAiB,EAAQ,QAAS,EAAQ,QAAQ,EAKjE,OAAO,CACT,CAEA,MAAa,CACX,KAAK,UAAU,QAAQ,OAAS,EAChC,KAAK,UAAU,UAAU,OAAS,EAClC,KAAK,UAAU,cAAc,OAAS,EACtC,MAAM,KAAK,EACX,KAAK,KAAK,OAAQ,IAAA,EAAS,CAC7B,CAEA,OAAc,CACZ,MAAM,MAAM,EACZ,KAAK,KAAK,QAAS,IAAA,EAAS,CAC9B,CAEA,QAAe,CACb,KAAK,UAAU,OAAQ,GAAa,EAAS,QAAQ,EAAE,QAAS,GAAa,EAAS,KAAK,CAAC,EAC5F,KAAK,KAAK,SAAU,IAAA,EAAS,CAC/B,CAQA,KAAK,EAAoB,CACvB,KAAK,UAAU,QAAS,GAAa,EAAS,KAAK,CAAI,CAAC,CAC1D,CASA,IAAI,UAAW,CAIb,OAHI,KAAK,UAAU,OAAS,EACnB,KAAK,UAAU,GAAG,SAEpB,KAAK,QAAQ,UAAY,GAClC,CAkBA,YAAY,EAAgB,EAAqC,CAC/D,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,yEAAyE,EAE3F,GAAI,CAAC,KAAK,WACR,MAAU,MAAM,2DAA2D,EAE7E,IAAM,EAAY,KAAK,WAAW,kBAAkB,KAAK,OAAQ,EAAQ,CAAO,EAChF,OAAO,IAAI,EAAM,KAAK,IAAK,EAAW,KAAK,QAAS,KAAK,eAAgB,SAAU,KAAK,QAAS,KAAK,UAAU,CAClH,CAWA,KAAK,EAAkC,CAMrC,OALI,IAAc,IAAA,GACT,KAAK,WAEd,KAAK,UAAY,EACjB,KAAK,UAAU,QAAS,GAAM,EAAE,KAAK,CAAS,CAAC,EACxC,KAAK,UACd,CAEA,IAAI,cAAuB,CACzB,OAAO,KAAK,aACd,CAEA,IAAI,aAAa,EAAc,CAC7B,KAAK,cAAgB,EACrB,KAAK,UAAU,QAAS,GAAO,EAAE,aAAe,CAAK,EACrD,KAAK,KAAK,aAAc,CAAI,CAC9B,CAEA,IAAI,OAAO,EAAgB,CACzB,MAAM,OAAS,EACf,KAAK,KAAK,eAAgB,CAAM,CAClC,CAKA,IAAI,YAAqB,CACvB,OAAO,KAAK,YACd,CAaA,MAAM,cAAc,EAA+B,CAMjD,GAAI,CAAC,OAAO,SAAS,CAAM,GAAK,GAAU,EACxC,MAAU,MAAM,2CAA2C,EAE7D,KAAK,aAAe,EACpB,MAAM,QAAQ,IAAI,KAAK,UAAU,IAAK,GAAM,EAAE,cAAc,CAAM,CAAC,CAAC,CACtE,CAEA,SAAgB,CACd,KAAK,YAAY,uBAAuB,KAAK,gBAAgB,EAC7D,KAAK,+BAA+B,EACpC,KAAK,6BAA+B,IAAA,GACpC,AAIE,KAAK,yBAHL,KAAK,sBAAsB,MAAM,EACjC,KAAK,sBAAsB,IAAM,GACjC,KAAK,sBAAsB,KAAK,EACH,IAAA,IAE/B,KAAK,UAAU,QAAQ,OAAS,EAChC,KAAK,UAAU,UAAU,OAAS,EAClC,KAAK,UAAU,cAAc,OAAS,EAKtC,KAAK,UAAU,QAAS,GAAM,CAC5B,KAAK,yBAAyB,CAAC,EAC/B,EAAE,QAAQ,CACZ,CAAC,EACD,KAAK,UAAY,CAAC,EAClB,KAAK,aAAa,mBAAmB,EACrC,MAAM,QAAQ,CAChB,CACF,ECjiBsB,GAAtB,cAA8C,CAAa,CACzD,mBAAiD,CAAC,EAGlD,IAAI,mBAAgD,CAClD,OAAO,KAAK,kBACd,CAEA,IAAI,kBAAkB,EAAqC,CACzD,KAAK,mBAAqB,EACtB,KAAK,SACH,KAAK,kBAAkB,SAAW,IAAA,KAAW,KAAK,OAAO,OAAO,MAAQ,KAAK,kBAAkB,QAC/F,KAAK,kBAAkB,YAAc,IAAA,KACvC,KAAK,OAAO,UAAU,MAAQ,KAAK,kBAAkB,WACnD,KAAK,kBAAkB,OAAM,KAAK,OAAO,KAAO,KAAK,kBAAkB,MAE/E,CAEA,IAAI,WAAoB,CACtB,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,OAAO,KAAK,OAAO,UAAU,KAC/B,CAEA,IAAI,UAAU,EAAmB,CAC/B,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,KAAK,OAAO,UAAU,MAAQ,EAC9B,KAAK,kBAAkB,UAAY,CACrC,CAEA,IAAI,QAAiB,CACnB,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,OAAO,KAAK,OAAO,OAAO,KAC5B,CAEA,IAAI,OAAO,EAAgB,CACzB,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,KAAK,OAAO,OAAO,MAAQ,EAC3B,KAAK,kBAAkB,OAAS,CAClC,CAEA,IAAI,MAAuB,CACzB,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,OAAO,KAAK,OAAO,IACrB,CAEA,IAAI,KAAK,EAAsB,CAC7B,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,KAAK,OAAO,KAAO,EACnB,KAAK,kBAAkB,KAAO,CAChC,CACF,EChEa,GAAb,cAAmC,EAAqC,CAS7D,OART,QAOA,YACE,EACA,EACA,EACA,CACA,MAAM,CAAM,EAJL,KAAA,OAAA,EAKP,KAAK,QAAU,EAAO,QACtB,KAAK,OAAS,EACd,KAAK,WAAW,EAAO,QAAS,EAAO,OAAO,EAG9C,IAAM,EAAS,KAAK,OACpB,GAAI,CAAC,EACH,MAAU,MAAM,0CAA0C,EAE5D,KAAK,YAAY,CAAQ,EACzB,KAAK,wBAAwB,EAAQ,CAAM,EAC3C,EAAO,QAAQ,CAAQ,EACvB,KAAK,kBAAoB,CACvB,OAAQ,EAAO,OAAO,MACtB,UAAW,EAAO,UAAU,MAC5B,KAAM,EAAO,IACf,CACF,CAEA,MAAe,CACb,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,OACxB,MAAU,MAAM,8CAA8C,EAGhE,GAAI,KAAK,SAAW,UAClB,MAAO,CAAC,IAAI,EAGd,IAAM,EAAW,KAAK,SAAW,SAWjC,OAVI,GAAY,KAAK,SAAW,YAC9B,KAAK,eAAe,EAGlB,KAAK,kBAAkB,SAAW,IAAA,KAAW,KAAK,OAAO,OAAO,MAAQ,KAAK,kBAAkB,QAC/F,KAAK,kBAAkB,YAAc,IAAA,KAAW,KAAK,OAAO,UAAU,MAAQ,KAAK,kBAAkB,WACrG,KAAK,kBAAkB,OAAM,KAAK,OAAO,KAAO,KAAK,kBAAkB,MAE3E,KAAK,OAAO,MAAM,EAClB,KAAK,gBAAgB,CAAQ,EACtB,CAAC,IAAI,CACd,CAEA,OAAc,CACR,CAAC,KAAK,QAAU,KAAK,SAAW,YAIpC,KAAK,OAAO,KAAK,EACjB,KAAK,WAAW,EAClB,CAEA,MAAa,CACP,CAAC,KAAK,QAAU,KAAK,SAAW,YAAc,KAAK,SAAW,YAI9D,KAAK,SAAW,WAClB,KAAK,OAAO,KAAK,EAGnB,KAAK,YAAY,EACnB,CAEA,SAAgB,CACT,QAAK,OAIV,IAAI,KAAK,QAAU,KAAK,SACtB,GAAI,CACF,KAAK,OAAO,WAAW,CACzB,MAAQ,CAAC,CAEX,KAAK,OAAS,IAAA,GACd,KAAK,OAAS,IAAA,GACd,KAAK,YAAY,EACjB,KAAK,aAAa,mBAAmB,EAGrC,MAAM,QAAQ,CARH,CASb,CASA,IAAI,YAAuB,CACzB,GAAI,CAAC,KAAK,SACR,MAAU,MAAM,wEAAwE,EAE1F,OAAO,KAAK,QACd,CASA,QAAQ,EAAgD,CACtD,OAAO,KAAK,WAAW,QAAQ,CAAkB,CACnD,CAQA,WAAW,EAA4C,CACjD,EACF,KAAK,WAAW,WAAW,CAAkB,EAE7C,KAAK,WAAW,WAAW,CAE/B,CAEA,gBAA+B,CAC7B,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,OACxB,MAAU,MAAM,4DAA4D,EAG9E,KAAK,OAAO,WAAW,EACvB,KAAK,OAAS,KAAK,QAAQ,iBAAiB,EAC5C,KAAK,wBAAwB,KAAK,OAAQ,KAAK,MAAM,CACvD,CACF,ECxIa,GAAb,MAAa,UAAc,CAAoC,CA4BpD,QACG,eACH,UACA,QAEC,WAhCV,mBACA,UAA6B,CAAC,EAC9B,aAAuD,IAAI,EAK3D,GAAgC,EAAU,EAAsD,CAC9F,OAAO,KAAK,aAAa,GAAG,EAAO,CAAQ,CAC7C,CAKA,IAAiC,EAAU,EAAgD,CACzF,KAAK,aAAa,IAAI,EAAO,CAAQ,CACvC,CAEA,KAA4C,EAAU,EAA4B,CAChF,KAAK,aAAa,KAAK,EAAO,CAAI,CACpC,CAEA,MAAgB,UAAuC,EAAU,EAAqC,CACpG,OAAO,KAAK,aAAa,UAAU,EAAO,CAAI,CAChD,CAEA,YACE,EACA,EACA,EAA8B,aAC9B,EAA0B,OAC1B,EAAgD,CAAC,EACjD,EACA,CACA,MAAM,EAPC,KAAA,QAAA,EACG,KAAA,eAAA,EACH,KAAA,UAAA,EACA,KAAA,QAAA,EAEC,KAAA,WAAA,EAGR,KAAK,QAAU,EACf,KAAK,mBAAqB,CAC5B,CAEA,IAAI,WAAmC,CACrC,OAAO,KAAK,UACd,CAaA,MAAM,EAA0C,CAAC,EAAU,CACzD,IAAM,EAAU,EAAU,SAAW,KAAK,QACpC,EAAY,EAAU,YAAc,IAAA,GAAkC,KAAK,UAA3B,EAAU,UAC1D,EAAS,EAAU,SAAW,IAAA,GAA+B,KAAK,OAAxB,EAAU,OACpD,EAAW,EAAU,WAAa,IAAA,GAAiC,KAAK,SAA1B,EAAU,SACxD,EAAU,EAAU,UAAY,IAAA,GAAgC,KAAK,SAAzB,EAAU,QACtD,EAAoB,CAAE,GAAI,EAAU,mBAAqB,KAAK,kBAAoB,EAElF,EAAQ,IAAI,EAChB,KAAK,QACL,KAAK,eACL,KAAK,UACL,EACA,EACA,KAAK,UACP,EAgBA,MAfA,GAAM,QAAU,EAChB,EAAM,UAAY,CAAC,GAAG,CAAQ,EAC9B,EAAM,WAAa,EAIf,EAAU,gBAAkB,IAAA,GAErB,KAAK,UAAY,SAC1B,EAAM,cAAgB,KAAK,eAF3B,EAAM,cAAgB,EAAU,cAI9B,IAAY,SACd,EAAM,SAAW,CAAC,GAAG,CAAQ,GAE/B,EAAM,WAAW,CAAO,EACjB,CACT,CAMA,SAA2B,CACzB,IAAM,EAAa,KAAK,QAAQ,iBAAiB,EAEjD,OADkB,KAAK,eAAe,CAC/B,CACT,CAEA,eAAuB,EAA6C,CAC9D,KAAK,kBAAkB,SAAW,IAAA,KAAW,EAAW,OAAO,MAAQ,KAAK,kBAAkB,QAC9F,KAAK,kBAAkB,YAAc,IAAA,KAAW,EAAW,UAAU,MAAQ,KAAK,kBAAkB,WACpG,KAAK,kBAAkB,OAAM,EAAW,KAAO,KAAK,kBAAkB,MAE1E,IAAM,EAAW,KAAK,QAAQ,WAAW,EACnC,EAAoB,KAAK,wBAAwB,EACvD,EAAS,QAAQ,CAAiB,EAClC,IAAM,EAAW,IAAI,GAAc,KAAM,EAAY,CAAQ,EAU7D,OATA,KAAK,iBAAiB,CAAQ,EAC9B,EAAS,OAAS,KAAK,OACnB,KAAK,UAAY,QACnB,EAAS,cAAgB,KAAK,cAC9B,EAAS,SAAW,KAAK,UAChB,KAAK,UAAY,WAC1B,EAAS,UAAY,KAAK,WAE5B,KAAK,UAAU,KAAK,CAAQ,EACrB,CAAC,CAAQ,CAClB,CAEA,MAAoC,CAClC,IAAM,EAAY,MAAM,KAAK,EAE7B,OADA,KAAK,KAAK,OAAQ,EAAU,EAAE,EACvB,CACT,CAEA,MAAa,CACX,MAAM,KAAK,EACX,KAAK,KAAK,OAAQ,IAAA,EAAS,CAC7B,CAEA,OAAc,CACZ,MAAM,MAAM,EACZ,KAAK,KAAK,QAAS,IAAA,EAAS,CAC9B,CAEA,QAAe,CACb,KAAK,UAAU,OAAQ,GAAa,EAAS,QAAQ,EAAE,QAAS,GAAa,EAAS,KAAK,CAAC,EAC5F,KAAK,KAAK,SAAU,IAAA,EAAS,CAC/B,CAEA,IAAI,QAAiB,CACnB,OAAO,MAAM,MACf,CAEA,IAAI,OAAO,EAAgB,CACzB,MAAM,OAAS,EACf,KAAK,KAAK,eAAgB,CAAM,CAClC,CAEA,IAAI,mBAAgD,CAClD,OAAO,KAAK,kBACd,CAEA,IAAI,kBAAkB,EAAqC,CACzD,KAAK,mBAAqB,EAC1B,KAAK,UAAU,QAAS,GAAM,CACxB,EAAE,SACA,KAAK,kBAAkB,SAAW,IAAA,KAAW,EAAE,OAAO,OAAO,MAAQ,KAAK,kBAAkB,QAC5F,KAAK,kBAAkB,YAAc,IAAA,KAAW,EAAE,OAAO,UAAU,MAAQ,KAAK,kBAAkB,WAClG,KAAK,kBAAkB,OAAM,EAAE,OAAO,KAAO,KAAK,kBAAkB,MAE5E,CAAC,CACH,CAEA,IAAI,WAAoB,CACtB,OAAQ,KAAK,kBAAkB,WAAwB,GACzD,CAEA,IAAI,UAAU,EAAmB,CAC/B,KAAK,mBAAmB,UAAY,EACpC,KAAK,UAAU,QAAS,GAAO,EAAE,UAAY,CAAU,EACvD,KAAK,KAAK,kBAAmB,CAAS,CACxC,CAEA,IAAI,QAAiB,CACnB,OAAO,KAAK,kBAAkB,QAAU,CAC1C,CAEA,IAAI,OAAO,EAAgB,CACzB,KAAK,mBAAmB,OAAS,EACjC,KAAK,UAAU,QAAS,GAAO,EAAE,OAAS,CAAO,EACjD,KAAK,KAAK,eAAgB,CAAM,CAClC,CAEA,IAAI,MAAuB,CACzB,OAAQ,KAAK,kBAAkB,MAA2B,MAC5D,CAEA,IAAI,KAAK,EAAsB,CAC7B,KAAK,mBAAmB,KAAO,EAC/B,KAAK,UAAU,QAAS,GAAO,EAAE,KAAO,CAAK,EAC7C,KAAK,KAAK,aAAc,CAAI,CAC9B,CACF,ECnLa,GAAb,MAAa,CAAoD,CAY5C,IACA,iBACA,OAbnB,WACA,SAAmB,GACnB,aACA,QACA,MAEA,gBAA2C,CACzC,KAAK,QAAQ,CACf,EAEA,YACE,EACA,EACA,EACA,CAHiB,KAAA,IAAA,EACA,KAAA,iBAAA,EACA,KAAA,OAAA,EAEjB,KAAK,QAAQ,iBAAiB,QAAS,KAAK,YAAa,CAAE,KAAM,EAAK,CAAC,CACzE,CAEA,aAAa,KAAK,EAAa,EAA0B,EAA4D,CACnH,GAAQ,eAAe,EACvB,IAAM,EAAU,IAAI,EAAqB,EAAK,EAAkB,CAAM,EACtE,GAAI,CACF,IAAM,EAAS,MAAM,EAAQ,YAAY,EAMzC,OALI,EAAO,aAAe,CAAC,EAAO,SAChC,EAAQ,QAAQ,EACT,OAET,EAAQ,QAAU,EAAO,QAClB,EACT,OAAS,EAAO,CAGd,GAFA,EAAQ,QAAQ,EAChB,GAAQ,eAAe,EAClB,GAAoC,OAAS,8BAChD,OAAO,KAET,MAAM,CACR,CACF,CAEA,IAAI,cAAuB,CACzB,GAAI,CAAC,KAAK,QACR,MAAU,MAAM,6CAA6C,EAE/D,OAAO,KAAK,QAAQ,YACtB,CAEA,IAAI,cAAmC,CACrC,GAAI,CAAC,KAAK,QACR,MAAU,MAAM,6CAA6C,EAE/D,MAAO,CACL,SAAU,KAAK,QAAQ,SACvB,KAAM,KAAK,QAAQ,KACnB,SAAU,KAAK,QAAQ,SACvB,UAAW,WACb,CACF,CAEA,OAAO,EAA6C,CAClD,GAAI,KAAK,UAAY,CAAC,KAAK,QACzB,MAAU,MAAM,mDAAmD,EAKrE,MAHA,MAAK,MAAQ,EACb,EAAM,iBAAiB,KAAM,KAAK,YAAY,EAC9C,KAAK,UAAU,KAAK,QAAS,CAAC,EACvB,CACT,CAEA,KAAK,EAAoB,CACvB,GAAI,CAAC,OAAO,SAAS,CAAI,GAAK,EAAO,EACnC,MAAU,WAAW,uDAAuD,EAE9E,GAAI,KAAK,SACP,MAAU,MAAM,yDAAyD,EAE3E,GAAI,CAAC,KAAK,MACR,MAAU,MAAM,4DAA4D,EAE9E,GAAI,CAAC,KAAK,QACR,MAAU,MAAM,qDAAqD,EAEvE,GAAI,KAAK,QAAQ,KACf,MAAU,MAAM,qCAAqC,EAEvD,GAAI,CAAC,KAAK,QAAQ,SAChB,MAAU,MAAM,qDAAqD,EAGvE,IAAM,EAAa,KAAK,MAAM,UAC9B,KAAK,MAAM,KAAK,EACZ,GACF,KAAK,MAAM,KAAK,EAElB,KAAK,YAAY,MAAM,EACvB,KAAK,QAAQ,MAAM,QAAQ,EAC3B,KAAK,QAAU,IAAA,GACf,KAAU,cAAc,CAAI,CAC9B,CAEA,SAAgB,CACV,KAAK,WAGT,KAAK,SAAW,GAChB,KAAK,QAAQ,oBAAoB,QAAS,KAAK,WAAW,EAC1D,KAAK,YAAY,MAAM,EACvB,KAAK,cAAc,QAAQ,EAC3B,KAAK,aAAe,IAAA,GACpB,KAAK,SAAS,MAAM,QAAQ,EAC5B,KAAK,QAAU,IAAA,GACjB,CAEA,MAAc,cAAc,EAA6B,CACvD,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,YAAY,EACtC,GAAI,KAAK,SAAU,CACjB,EAAO,SAAS,MAAM,QAAQ,EAC9B,MACF,CACA,GAAI,CAAC,EAAO,QACV,OAEF,IAAM,EAAoB,KAAK,qBAAqB,EACpD,GAAI,IAAsB,IAAA,IAAa,EAAO,QAAQ,eAAiB,EAErE,MADA,EAAO,QAAQ,MAAM,QAAQ,EACnB,MAAM,+CAA+C,EAEjE,KAAK,QAAU,EAAO,QACtB,KAAK,UAAU,EAAO,QAAS,CAAI,CACrC,OAAS,EAAO,CACV,CAAC,KAAK,UAAY,CAAC,KAAK,QAAQ,SAClC,KAAK,OAAO,gBAAgB,CAAK,CAErC,CACF,CAEA,sBAAmD,CACjD,OAAO,KAAK,OAAO,iBACrB,CAEA,UAAkB,EAAyB,EAAyB,CAClE,KAAK,YAAY,MAAM,EACvB,IAAM,EAAM,IAAI,gBAChB,KAAK,WAAa,EAClB,KAAU,KAAK,EAAS,EAAW,EAAI,MAAM,EAAE,MAAO,GAAmB,CACnE,CAAC,EAAI,OAAO,SAAW,CAAC,KAAK,UAAY,CAAC,KAAK,QAAQ,SACzD,KAAK,OAAO,gBAAgB,CAAK,CAErC,CAAC,CACH,CAEA,MAAc,KAAK,EAAyB,EAAmB,EAAuC,CACpG,IAAM,EAAQ,KAAK,MACnB,GAAI,CAAC,EACH,OAEF,IAAM,EAAY,IAAI,GAAsB,EAAQ,WAAY,KAAK,iBAAkB,EAAQ,YAAY,EAE3G,UAAW,IAAM,KAAU,EAAQ,KAAK,QAAQ,CAAS,EAAG,CAC1D,GAAI,EAAU,SAAW,KAAK,SAAU,CACtC,EAAO,MAAM,EACb,MACF,CACA,GAAI,CACF,IAAM,EAAU,CAAE,OAAQ,MAAgB,WAAY,CAAE,EAClD,EAAO,IAAI,YAAY,EAAO,eAAe,CAAO,CAAC,EAC3D,EAAO,OAAO,EAAM,CAAO,EAC3B,IAAM,EAAS,EAAU,IAAI,IAAI,aAAa,CAAI,CAAC,EACnD,MAAM,KAAK,QAAQ,EAAO,EAAQ,EAAQ,aAAc,CAAS,CACnE,QAAU,CACR,EAAO,MAAM,CACf,CACF,CAEA,IAAM,EAAO,EAAU,SAAS,EAChC,MAAM,KAAK,QAAQ,EAAO,EAAM,EAAQ,aAAc,CAAS,EAC3D,CAAC,EAAU,SAAW,CAAC,KAAK,UAC9B,EAAM,IAAI,CAEd,CAEA,MAAc,QACZ,EACA,EACA,EACA,EACe,CAEf,IAAM,EADgB,KAAK,IAAI,EAAG,KAAK,MAAM,KAAK,iBAAmB,CAAC,CAC/C,EAAgB,EACvC,IAAK,IAAI,EAAS,EAAG,EAAS,EAAQ,OAAQ,GAAU,EAAgB,CACtE,IAAM,EAAQ,EAAQ,MAAM,EAAQ,KAAK,IAAI,EAAQ,OAAQ,EAAS,CAAc,CAAC,EACjF,EAAU,GACd,KAAO,CAAC,GAAS,CACf,EAAO,eAAe,EACtB,IAAM,EAAS,EAAM,iBACf,EAAc,EAAM,MAAM,CAAK,EACrC,EAAU,EAAM,iBAAmB,EAC9B,GACH,MAAM,GAAa,EAAO,CAAM,CAEpC,CACF,CACF,CAEA,MAAc,aAA0C,CACtD,KAAK,QAAQ,eAAe,EAC5B,IAAI,EAAmB,GACjB,EAAS,IAAI,EAAA,UAAU,KAAK,IAAK,CACrC,QAAS,MAAO,EAAO,IAAS,CAC9B,IAAM,EAAW,MAAM,WAAW,MAAM,EAAO,CAAI,EAInD,OAHI,EAAS,SAAW,KAAO,EAAS,QAAQ,IAAI,eAAe,GAAG,YAAY,IAAM,WACtF,EAAmB,IAEd,CACT,CACF,CAAC,EACK,EAAQ,IAAI,EAAA,MAAM,CAAE,QAAS,CAAC,EAAA,IAAK,EAAA,KAAM,EAAA,IAAK,EAAA,IAAK,EAAA,KAAM,EAAA,IAAI,EAAG,QAAO,CAAC,EAC9E,KAAK,aAAe,EACpB,GAAI,CACF,IAAM,EAAQ,MAAM,EAAM,qBAAqB,EAC/C,GAAI,CAAC,GAAS,CAAE,MAAM,EAAM,UAAU,EAGpC,MAFA,MAAK,aAAe,IAAA,GACpB,EAAM,QAAQ,EACP,CAAE,QAAS,KAAM,YAAa,EAAK,EAE5C,IAAM,EAAU,MAAM,KAAK,gBAAgB,EAAO,EAAO,CAAgB,EAQzE,OAPA,KAAK,QAAQ,eAAe,EACxB,KAAK,UACP,KAAK,aAAe,IAAA,GACpB,EAAM,QAAQ,EACP,CAAE,QAAS,KAAM,YAAa,EAAM,IAE7C,KAAK,aAAe,IAAA,GACb,CAAE,UAAS,YAAa,EAAM,EACvC,OAAS,EAAO,CAKd,MAJI,KAAK,eAAiB,IACxB,KAAK,aAAe,IAAA,GACpB,EAAM,QAAQ,GAEV,CACR,CACF,CAEA,MAAc,gBACZ,EACA,EACA,EACyB,CACzB,GAAM,CAAC,EAAc,EAAY,EAAM,GAAoB,MAAM,QAAQ,IAAI,CAC3E,EAAM,oBAAoB,EAC1B,EAAM,cAAc,EACpB,EAAM,OAAO,EACb,EAAM,wBAAwB,CAAE,aAAc,EAAK,CAAC,CACtD,CAAC,EACD,MAAO,CACL,eACA,SAAU,GAAQ,IAAqB,KAAO,IAA2B,EACzE,QACA,OACA,aACA,SAAU,CAAC,GAAQ,EACnB,KAAM,IAAI,EAAA,gBAAgB,CAAK,CACjC,CACF,CACF,EAEA,SAAS,GAAa,EAAuB,EAAoC,CAE/E,OADA,EAAO,eAAe,EACf,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAc,EAAM,GAAG,YAAe,CAC1C,EAAQ,EACR,EAAQ,CACV,CAAC,EACK,MAAoB,CACxB,EAAQ,EACR,EAAO,EAAO,MAAM,CACtB,EACM,MAAgB,CACpB,EAAY,EACZ,EAAO,oBAAoB,QAAS,CAAW,CACjD,EACA,EAAO,iBAAiB,QAAS,EAAa,CAAE,KAAM,EAAK,CAAC,CAC9D,CAAC,CACH,CASA,IAAM,GAAN,KAA4B,CAMP,WACA,WACA,aAPnB,YAAsB,EACtB,mBAA6B,EAC7B,cAEA,YACE,EACA,EACA,EACA,CAHiB,KAAA,WAAA,EACA,KAAA,WAAA,EACA,KAAA,aAAA,CAChB,CAEH,IAAI,EAAmC,CACrC,GAAI,KAAK,aAAe,KAAK,WAE3B,MADA,MAAK,aAAe,EAAM,OAAS,KAAK,aACjC,EAET,IAAM,EAAa,EAAM,OAAS,KAAK,aACjC,EAAa,KAAK,YAClB,EAAY,EAAa,EAAa,EACtC,EAAwB,KAAK,WAAa,KAAK,WAC/C,EACJ,KAAK,mBAAqB,EACtB,EACA,KAAK,OAAO,EAAY,KAAK,oBAAsB,CAAqB,EAAI,EAC5E,EAAS,IAAI,aAAa,EAAmB,KAAK,YAAY,EAChE,EAAe,EAEnB,IAAK,IAAI,EAAc,EAAG,EAAc,EAAkB,GAAe,EAAG,CAC1E,IAAM,EAAY,KAAK,MAAM,KAAK,kBAAkB,EAC9C,EAAW,KAAK,mBAAqB,EAC3C,IAAK,IAAI,EAAU,EAAG,EAAU,KAAK,aAAc,GAAW,EAAG,CAC/D,IAAM,EAAO,KAAK,SAAS,EAAO,EAAY,EAAW,CAAO,EAEhE,EAAO,GAAgB,GADT,KAAK,SAAS,EAAO,EAAY,KAAK,IAAI,EAAY,EAAG,CAAS,EAAG,CACpD,EAAQ,GAAQ,EAC/C,GAAgB,CAClB,CACA,KAAK,oBAAsB,CAC7B,CAIA,MAFA,MAAK,aAAe,EACpB,KAAK,cAAgB,EAAM,MAAM,EAAM,OAAS,KAAK,YAAY,EAC1D,CACT,CAEA,UAAyB,CACvB,GAAI,KAAK,aAAe,KAAK,YAAc,CAAC,KAAK,cAC/C,OAAO,IAAI,aAEb,IAAM,EAAwB,KAAK,WAAa,KAAK,WAC/C,EAAwB,KAAK,YAAc,KAAK,mBAChD,EAAmB,EAAwB,EAAI,KAAK,KAAK,EAAwB,CAAqB,EAAI,EAC1G,EAAS,IAAI,aAAa,EAAmB,KAAK,YAAY,EACpE,IAAK,IAAI,EAAc,EAAG,EAAc,EAAkB,GAAe,EACvE,EAAO,IAAI,KAAK,cAAe,EAAc,KAAK,YAAY,EAC9D,KAAK,oBAAsB,EAE7B,OAAO,CACT,CAEA,SAAiB,EAAqB,EAAoB,EAAe,EAAyB,CAIhG,OAHI,EAAQ,EACH,KAAK,gBAAgB,IAAY,EAAM,IAAY,EAErD,GAAO,EAAQ,GAAc,KAAK,aAAe,IAAY,CACtE,CACF,ECjKM,EAAqB,sBAOrB,GAAuD,OAAO,OAAO,CACzE,KAAM,aACN,IAAK,aACL,IAAK,YACL,IAAK,YACL,KAAM,aACN,IAAK,YACL,IAAK,YACL,KAAM,wBACR,CAAC,EAMD,SAAS,GAAe,EAA4B,CAClD,IAAM,EAAe,EAAI,MAAM,OAAQ,CAAC,EAAE,GACpC,EAAW,EAAa,YAAY,GAAG,EAK7C,OAJI,EAAW,EACN,KAGF,GADK,EAAa,MAAM,EAAW,CAAC,EAAE,YACnB,IAAQ,IACpC,CAGA,IAAM,GAAsC,CAAC,EAAG,EAAG,CAAC,EAC9C,GAAqC,CAAC,EAAG,EAAG,EAAE,EAC9C,GAAgC,CAAC,EAAG,EAAG,CAAC,EAiB9C,SAAS,EAAa,EAAqC,CACzD,OAAO,OAAO,GAAU,YAAY,GAAkB,UAAW,GAAS,OAAO,EAAM,OAAU,QACnG,CAEA,SAAS,EAAwB,EAAqC,CACpE,OAAO,EAAa,CAAK,GAAK,mBAAoB,GAAS,OAAO,EAAM,gBAAmB,UAC7F,CAMA,SAAS,GAA6B,EAAgF,CACpH,OACE,EAAa,EAAS,QAAQ,GAC9B,EAAa,EAAS,QAAQ,GAC9B,EAAa,EAAS,QAAQ,GAC9B,EAAa,EAAS,GAAG,GACzB,EAAa,EAAS,GAAG,GACzB,EAAa,EAAS,GAAG,CAE7B,CAEA,SAAS,GAA0B,EAA6E,CAC9G,OACE,EAAwB,EAAS,SAAS,GAC1C,EAAwB,EAAS,SAAS,GAC1C,EAAwB,EAAS,SAAS,CAE9C,CAEA,SAAS,GAAwB,EAA0E,CACzG,MAAO,CACL,QAAS,CAAC,EAAS,SAAS,MAAO,EAAS,SAAS,MAAO,EAAS,SAAS,KAAK,EACnF,GAAI,CAAC,EAAS,IAAI,MAAO,EAAS,IAAI,MAAO,EAAS,IAAI,KAAK,CACjE,CACF,CAEA,SAAS,GAAqB,EAA4C,CACxE,MAAO,CAAC,EAAS,UAAU,MAAO,EAAS,UAAU,MAAO,EAAS,UAAU,KAAK,CACtF,CAMA,SAAS,GAAyB,EAAyB,EAAmB,EAAoB,CAChG,GAAM,CAAC,EAAU,EAAU,GAAY,EACjC,CAAC,EAAK,EAAK,GAAO,EACxB,GAAI,GAA6B,CAAQ,EAAG,CAC1C,EAAS,SAAS,MAAQ,EAC1B,EAAS,SAAS,MAAQ,EAC1B,EAAS,SAAS,MAAQ,EAC1B,EAAS,IAAI,MAAQ,EACrB,EAAS,IAAI,MAAQ,EACrB,EAAS,IAAI,MAAQ,EACrB,MACF,CACA,GAAI,OAAO,EAAS,gBAAmB,WAAY,CACjD,EAAS,eAAe,EAAU,EAAU,EAAU,EAAK,EAAK,CAAG,EACnE,MACF,CACA,MAAU,MAAM,oDAAoD,CACtE,CAMA,SAAS,GAAsB,EAAyB,EAAoB,EAA2B,CACrG,GAAM,CAAC,EAAG,EAAG,GAAK,EAClB,GAAI,GAA0B,CAAQ,EAAG,CACvC,EAAS,UAAU,eAAe,EAAG,CAAW,EAChD,EAAS,UAAU,eAAe,EAAG,CAAW,EAChD,EAAS,UAAU,eAAe,EAAG,CAAW,EAChD,MACF,CACA,GAAI,OAAO,EAAS,aAAgB,WAAY,CAC9C,EAAS,YAAY,EAAG,EAAG,CAAC,EAC5B,MACF,CACA,MAAU,MAAM,iDAAiD,CACnE,CAEA,IAAa,GAAb,MAAa,CAAU,CACrB,QACA,eAOA,OACA,SAKA,uBAA2C,CAAC,GAAG,EAAyB,EACxE,sBAA0C,CAAC,GAAG,EAAwB,EACtE,iBAAqC,CAAC,GAAG,EAAmB,EAC5D,WAA6B,EAC7B,QAA2B,GAU3B,oBAAiE,IAAI,QASrE,aAAmE,IAAI,QAMvE,qBAAwF,IAAI,QAC5F,qBACA,aAA2D,IAAI,EAC/D,MACA,uBACA,kBACA,OAKA,aAAyC,IAAI,IAI7C,aAA4D,UAG5D,0BAAkD,CAAC,EAoBnD,YAAY,EAAuB,EAAgB,EAAiC,CAAC,EAAG,CAMtF,GALA,KAAK,QAAU,GAAW,IAAI,aAC9B,KAAK,SAAW,KAAK,QAAQ,SACzB,GAA0B,KAAK,QAAQ,IACzC,KAAK,uBAAyB,GAAqB,KAAK,QAAQ,GAE9D,GAA6B,KAAK,QAAQ,EAAG,CAC/C,IAAM,EAAc,GAAwB,KAAK,QAAQ,EACzD,KAAK,sBAAwB,EAAY,QACzC,KAAK,iBAAmB,EAAY,EACtC,CACA,KAAK,eAAiB,KAAK,QAAQ,WAAW,EAW9C,KAAK,OAAS,IAAI,EAAI,KAAK,QAAS,SAAU,KAAK,eAAgB,IAAA,GAAW,EAAK,EACnF,KAAK,OAAO,OAAO,QAAQ,KAAK,QAAQ,WAAW,EACnD,KAAK,MAAQ,GAAS,IAAI,GAC1B,KAAK,uBACH,EAAe,0BACb,EAAgB,EAAM,IAAY,IAAI,iBAAiB,EAAuB,EAAM,CAAO,GAC/F,KAAK,kBAAoB,EAAe,kBACxC,KAAK,OAAS,EAAe,SAAW,EAAe,MAAQ,GAAa,IAE5E,KAAK,qBAAuB,IAAI,qBAAsB,GAAa,CACjE,IAAK,IAAM,KAAU,EAAS,QAC5B,EAAO,WAAW,EAEpB,IAAK,IAAM,KAAY,EAAS,UAC9B,EAAS,WAAW,EAEtB,IAAK,IAAM,KAAS,EAAS,cAC3B,EAAM,MAAM,EACZ,EAAM,gBAAgB,KAAK,EAC3B,EAAM,KAAK,CAEf,CAAC,EAMkB,EAAe,aAAe,IAC/B,CAAC,KAAK,YACtB,KAAK,sBAAwB,GAAsB,CACjD,QAAS,KAAK,QACd,aAAgB,CACd,KAAK,aAAe,UACpB,KAAK,KAAK,SAAU,IAAA,EAAS,CAC/B,CACF,CAAC,EAEL,CAYA,IAAI,QAAkB,CAEpB,OADe,KAAK,QAA0C,QAC7C,WACnB,CAGA,wBAAwB,EAAe,EAAgC,EAA+B,CACpG,KAAK,qBAAqB,SAAS,EAAO,EAAU,CAAe,CACrE,CAGA,uBAAuB,EAA+B,CACpD,KAAK,qBAAqB,WAAW,CAAe,CACtD,CAYA,OAAO,cAAc,EAAyB,EAAgB,EAA4C,CAGxG,OAAO,IAAI,EADT,EAAQ,SAAW,IAAI,oBAAoB,EAAQ,iBAAkB,EAAQ,OAAQ,EAAQ,UAAU,EACpE,EAAO,CAAc,CAC5D,CAMA,IAAI,WAAqB,CACvB,OAAO,OAAO,KAAK,QAAQ,gBAAmB,UAChD,CASA,MAAM,gBAAuC,CAC3C,GAAI,OAAO,KAAK,QAAQ,gBAAmB,WACzC,MAAU,MACR,4GACF,EAEF,OAAO,KAAK,QAAQ,eAAe,CACrC,CA2BA,kBAAkB,EAAqB,EAAgB,EAA2C,CAChG,GAAI,OAAO,KAAK,QAAQ,cAAiB,WACvC,MAAU,MAAM,2EAA2E,EAE7F,GAAI,EAAE,EAAS,GACb,MAAU,MAAM,8CAA8C,GAAQ,EAGxE,IAAM,EAAmB,EAAO,iBAC1B,EAAY,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,OAAS,CAAM,CAAC,EAC1D,EAAS,KAAK,QAAQ,aAAa,EAAkB,EAAW,EAAO,UAAU,EAEvF,IAAK,IAAI,EAAK,EAAG,EAAK,EAAkB,IAAM,CAG5C,IAAM,EAAS,EAAO,eAAe,CAAE,EAEjC,EAAY,EADJ,aAAkB,aAAe,EAAS,aAAa,KAAK,CAAM,EAC3C,EAAQ,CAAO,EAGpD,EAAO,cAAc,EAAU,SAAS,EAAG,CAAS,EAAG,CAAE,CAC3D,CAEA,OAAO,CACT,CAMA,GAAoC,EAAU,EAA0D,CACtG,OAAO,KAAK,aAAa,GAAG,EAAO,CAAQ,CAC7C,CAKA,IAAqC,EAAU,EAAoD,CACjG,KAAK,aAAa,IAAI,EAAO,CAAQ,CACvC,CAEA,KAAsC,EAAU,EAAgC,CAC9E,KAAK,aAAa,KAAK,EAAO,CAAI,CACpC,CAEA,MAAM,UAA2C,EAAU,EAAyC,CAClG,OAAO,KAAK,aAAa,UAAU,EAAO,CAAI,CAChD,CASA,MAAM,aAAa,EAAqC,CACtD,GAAI,CAAC,KAAK,QAAQ,aAAc,CAC9B,KAAK,OAAO,KAAK,4BAA4B,EAC7C,MACF,CACA,IAAK,IAAM,KAAW,EACpB,MAAM,KAAK,uBAAuB,EAAQ,KAAM,EAAQ,IAAK,CAAM,CAEvE,CAEA,MAAM,2BAA2B,EAAqC,CACpE,MAAM,KAAK,uBAAuB,EAAS,gBAAgB,KAAM,EAAS,gBAAgB,IAAK,CAAM,CACvG,CAUA,MAAM,mBACJ,EACA,EACA,EACA,EAC2B,CAE3B,OADA,MAAM,KAAK,uBAAuB,EAAQ,KAAM,EAAQ,IAAK,IAAA,GAAW,CAAO,EACxE,KAAK,kBACV,EAAQ,KACR,EAAQ,IACR,IAAA,GACA,CAAE,GAAG,EAAa,cAAe,CAAE,GAAI,GAAa,eAAiB,CAAC,EAAI,GAAG,CAAc,CAAE,EAC7F,CACF,CACF,CAkBA,MAAM,YAAY,EAA6C,CAC7D,IAAM,EAAM,GAAW,KAAK,QACtB,EAAS,KAAK,aAAa,IAAI,CAAG,EACxC,GAAI,EACF,OAAO,EAET,IAAM,GAAW,SAAY,CAE3B,IAAM,EAAU,MAAM,MADC,MAAM,EAAU,GACR,YAAY,EAC3C,OAAO,EAAI,gBAAgB,CAAO,CACpC,GAAG,EACH,KAAK,aAAa,IAAI,EAAK,CAAO,EAClC,GAAI,CACF,OAAO,MAAM,CACf,OAAS,EAAK,CAGZ,MADA,KAAK,aAAa,OAAO,CAAG,EACtB,CACR,CACF,CAaA,MAAM,0BAA0B,EAAa,EAAuB,EAA4C,CAC9G,IAAM,EAAM,GAAW,KAAK,QACxB,EAAkB,KAAK,qBAAqB,IAAI,CAAG,EAClD,IACH,EAAkB,IAAI,IACtB,KAAK,qBAAqB,IAAI,EAAK,CAAe,GAEpD,IAAM,EAAS,EAAgB,IAAI,CAAG,EACtC,GAAI,EACF,OAAO,KAAK,2BAA2B,EAAQ,CAAM,EAEvD,GAAI,GAAQ,QACV,MAAM,KAAK,iBAAiB,EAG9B,IAAM,GAAW,SAAY,CAC3B,IAAM,EAAW,MAAM,MAAM,CAAG,EAChC,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,wCAAwC,EAAI,IAAI,EAAS,OAAO,GAAG,EAAS,YAAY,EAE1G,IAAM,EAAU,MAAM,EAAS,YAAY,EAC3C,OAAO,EAAI,gBAAgB,CAAO,CACpC,GAAG,EAOH,OANA,EAAgB,IAAI,EAAK,CAAO,EAChC,EAAa,UAAY,CACnB,EAAgB,IAAI,CAAG,IAAM,GAC/B,EAAgB,OAAO,CAAG,CAE9B,CAAC,EACM,KAAK,2BAA2B,EAAS,CAAM,CACxD,CAEA,MAAM,kBACJ,EACA,EACA,EACA,EACA,EAC2B,CAK3B,IAAM,EAAM,GAAW,KAAK,QAE5B,GAAI,CAAC,EAAI,aACP,MAAU,MAAM,4BAA4B,EAE9C,GAAI,CACF,IAAM,EAAO,KAAK,uBAAuB,EAAK,EAAM,CAAO,EAK3D,OAJA,KAAK,OAAO,KAAK,GAAG,EAAmB,sBAAuB,CAC5D,OACA,OAAQ,KAAK,kBAAkB,EAAK,CAAI,CAC1C,CAAC,EACM,CACT,OAAS,EAAK,CACZ,KAAK,OAAO,KAAK,GAAG,EAAmB,mBAAoB,CACzD,OACA,OAAQ,KAAK,kBAAkB,EAAK,CAAI,EACxC,MAAO,CACT,CAAC,EACD,GAAI,CACF,MAAM,KAAK,uBAAuB,EAAM,EAAK,EAAQ,CAAG,CAC1D,OAAS,EAAK,CAKZ,MAJA,KAAK,OAAO,MAAM,GAAG,EAAmB,cAAe,CACrD,OACA,MAAO,CACT,CAAC,EACK,CACR,CAEA,GAAI,CACF,IAAM,EAAO,KAAK,uBAAuB,EAAK,EAAM,CAAO,EAE3D,OADA,KAAK,OAAO,KAAK,GAAG,EAAmB,iCAAkC,CAAE,MAAK,CAAC,EAC1E,CACT,OAAS,EAAK,CAKZ,MAJA,KAAK,OAAO,MAAM,GAAG,EAAmB,8BAA+B,CACrE,OACA,MAAO,CACT,CAAC,EACK,CACR,CACF,CACF,CAEA,MAAM,0BAA0B,EAAiD,CAC/E,OAAO,KAAK,kBAAkB,EAAS,gBAAgB,KAAM,EAAS,gBAAgB,IAAK,EAAQ,CACjG,eAAgB,EAChB,gBAAiB,EACjB,mBAAoB,CAAC,CAAC,EACtB,aAAc,EACd,iBAAkB,WAClB,sBAAuB,UACzB,CAAC,CACH,CAMA,kBAA0B,EAAkB,EAAuB,CACjE,OAAO,KAAK,oBAAoB,IAAI,CAAG,GAAG,IAAI,CAAI,GAAK,EACzD,CAKA,oBAA4B,EAAkB,EAAoB,CAChE,IAAI,EAAQ,KAAK,oBAAoB,IAAI,CAAG,EACvC,IACH,EAAQ,IAAI,IACZ,KAAK,oBAAoB,IAAI,EAAK,CAAK,GAEzC,EAAM,IAAI,CAAI,CAChB,CAEA,MAAc,uBACZ,EACA,EACA,EACA,EACe,CACf,IAAM,EAAM,GAAW,KAAK,QAC5B,GAAI,CAAC,EAAI,aACP,MAAU,MAAM,4BAA4B,EAM9C,GAAI,KAAK,kBAAkB,EAAK,CAAI,EAAG,CACrC,KAAK,OAAO,KAAK,GAAG,EAAmB,eAAgB,CAAE,MAAK,CAAC,EAC/D,MACF,CAGA,IAAM,EAAc,KAAK,kBAAoB,MAAM,KAAK,kBAAkB,EAAM,CAAG,EAAI,EACvF,KAAK,OAAO,KAAK,GAAG,EAAmB,kBAAmB,CACxD,OACA,IAAK,EACL,QAAS,GAAQ,SAAW,EAC9B,CAAC,EACD,GAAI,CACF,MAAM,EAAI,aAAa,UAAU,EAAa,CAC5C,YAAa,cACb,GAAI,GAAU,CAAE,QAAO,CACzB,CAAC,EACD,KAAK,oBAAoB,EAAK,CAAI,EAClC,KAAK,OAAO,KAAK,GAAG,EAAmB,qBAAsB,CAAE,MAAK,CAAC,CACvE,OAAS,EAAK,CAKZ,MAJA,KAAK,OAAO,MAAM,GAAG,EAAmB,qBAAsB,CAC5D,OACA,MAAO,CACT,CAAC,EACK,CACR,CACF,CAEA,kBAAyC,CACvC,OAAO,IAAI,aAAa,wBAAyB,YAAY,CAC/D,CAEA,2BAAmC,EAA+B,EAA4C,CAO5G,OANK,EAGD,EAAO,QACF,QAAQ,OAAO,KAAK,iBAAiB,CAAC,EAExC,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,MAAoB,CACxB,EAAQ,EACR,EAAO,KAAK,iBAAiB,CAAC,CAChC,EACM,MAAgB,CACpB,EAAO,oBAAoB,QAAS,CAAW,CACjD,EACA,EAAO,iBAAiB,QAAS,EAAa,CAAE,KAAM,EAAK,CAAC,EAC5D,EAAQ,KACL,GAAW,CACV,EAAQ,EACR,EAAQ,CAAM,CAChB,EACC,GAAiB,CAChB,EAAQ,EACR,EAAO,CAAG,CACZ,CACF,CACF,CAAC,EAxBQ,CAyBX,CAEA,iBACE,EACA,EACA,EACA,EACA,EACA,EAAoB,GACJ,CAKhB,OAJI,GAAQ,QACH,QAAQ,OAAO,KAAK,iBAAiB,CAAC,EAGxC,IAAI,SAAgB,EAAS,IAAW,CAC7C,IAAM,EAAQ,GAAiB,IAAI,MAC/B,EACA,EACA,EAAU,GACR,EAAkE,CAAC,EAEnE,MAAgB,CACpB,EAAM,oBAAoB,iBAAkB,CAAoB,EAChE,EAAM,oBAAoB,QAAS,CAAW,EAC9C,GAAQ,oBAAoB,QAAS,CAAW,CAClD,EAEM,MAAiB,CACrB,GAAY,QAAQ,EACpB,EAAM,MAAM,EACZ,EAAM,IAAM,GACZ,EAAM,KAAK,CACb,EAEM,EAAU,GAAyB,CACnC,IAGJ,EAAU,GACV,EAAQ,EACR,EAAS,EACX,EAEM,MAA6B,CACjC,MAAa,CACX,EAAQ,IAAI,EACV,EACA,IAAA,GACA,KAAK,QACL,KAAK,eACL,EACA,EACA,KACA,EACA,MAAmB,GAAY,QAAQ,EAAI,IAAA,EAC7C,EACI,IAAc,cAChB,EAAM,mBAAqB,CACzB,SAAU,OAAO,SAAS,EAAM,QAAQ,EAAI,EAAM,SAAW,IAC7D,KAAM,EAAM,WAAa,IACzB,UAAW,EAAM,UAAU,QAAU,GAAK,EAC1C,UAAW,eACb,GAEF,IAAK,GAAM,CAAE,QAAO,iBAAiB,EACnC,EAAM,gBAAgB,EAAO,CAAW,EAE1C,EAAQ,CAAK,CACf,CAAC,CACH,EAEM,GAAkB,EAAc,IAAyB,CACzD,EACF,EAAM,gBAAgB,EAAO,CAAW,EAC/B,EACT,EAAiB,KAAK,CAAE,QAAO,aAAY,CAAC,EAE5C,MAAa,CACX,EAAS,EACT,EAAO,CAAK,CACd,CAAC,CAEL,EAEM,MAAoB,CACxB,IAAM,EAAY,MAAM,6BAA6B,GAAK,EAC1D,MAAa,CACX,EAAS,EACT,EAAO,CAAK,CACd,CAAC,CACH,EAEM,MAAoB,CACxB,IAAM,EAAQ,KAAK,iBAAiB,EACpC,MAAa,CACX,EAAS,EACT,EAAO,CAAK,CACd,CAAC,CACH,EAEA,EAAM,YAAc,YACpB,EAAM,QAAU,OAChB,EAAM,iBAAiB,iBAAkB,CAAoB,EAC7D,EAAM,iBAAiB,QAAS,CAAW,EAC3C,GAAQ,iBAAiB,QAAS,EAAa,CAAE,KAAM,EAAK,CAAC,EACzD,EACF,GAAgB,OAAO,CAAc,EAAE,KACpC,GAAY,CAEX,GADA,EAAa,EACT,EAAS,CACX,EAAQ,QAAQ,EAChB,MACF,CACA,GAAI,CACF,EAAQ,OAAO,EAAO,CAAG,CAC3B,OAAS,EAAO,CACd,MAAa,CACX,EAAS,EACT,EAAO,CAAK,CACd,CAAC,CACH,CACF,EACC,GAAmB,CAClB,MAAa,CACX,EAAS,EACT,EAAO,CAAK,CACd,CAAC,CACH,CACF,GAEA,EAAM,IAAM,EACZ,EAAM,KAAK,EAEf,CAAC,CACH,CAEA,eAAuB,EAAa,EAAkB,EAAsC,CAC1F,IAAM,EAAQ,IAAI,MACZ,EAAY,EAAM,YAAY,+BAA+B,GAAK,EAAM,YAAY,uBAAuB,EACjH,OAAO,KAAK,iBAAiB,EAAK,YAAa,EAAS,EAAQ,EAAO,CAAC,CAAS,CACnF,CAEA,kBAAyB,CACvB,KAAK,MAAM,iBAAiB,CAC9B,CAEA,iBAAiB,EAA4B,EAAmB,OAAe,CAE7E,OAAO,IADW,GAAM,KAAK,QAAS,KAAK,eAAgB,aAAc,EAAS,EAAS,IACpF,CACT,CA+CA,MAAM,YACJ,EACA,EAAuB,SACvB,EAAmB,OACnB,EACgB,CAChB,GAAI,MAAM,QAAQ,CAAW,EAC3B,OAAO,KAAK,wBAAwB,EAAa,EAAW,EAAS,CAAM,EAE7E,GAAI,OAAO,GAAgB,SACzB,OAAO,QAAQ,QAAQ,IAAI,EAAM,GAAI,EAAa,KAAK,QAAS,KAAK,eAAgB,EAAW,EAAS,IAAI,CAAC,EAEhH,IAAM,EAAM,EAOZ,OANI,IAAc,OACT,KAAK,iBAAiB,EAAK,OAAQ,EAAS,CAAM,EAEvD,IAAc,YACT,KAAK,iBAAiB,EAAK,YAAa,EAAS,CAAM,EAEzD,KAAK,gBAAgB,EAAK,EAAW,EAAS,CAAM,CAC7D,CAEA,MAAc,gBACZ,EACA,EACA,EACA,EACgB,CAUhB,OAAO,IAAI,EAAM,EAAK,MATD,KAAK,MAAM,eAAe,KAAK,QAAS,EAAK,EAAQ,CACxE,eAAiB,GAAU,KAAK,UAAU,eAAgB,CAAK,EAC/D,kBAAoB,GAAU,KAAK,UAAU,kBAAmB,CAAK,EACrE,kBAAoB,GAAU,KAAK,UAAU,kBAAmB,CAAK,EACrE,eAAiB,GAAU,KAAK,UAAU,eAAgB,CAAK,EAC/D,WAAa,GAAU,KAAK,UAAU,WAAY,CAAK,EACvD,YAAc,GAAU,KAAK,UAAU,YAAa,CAAK,EACzD,aAAe,GAAU,KAAK,UAAU,aAAc,CAAK,CAC7D,CAAC,EAC6B,KAAK,QAAS,KAAK,eAAgB,EAAW,EAAS,IAAI,CAC3F,CAWA,OAAe,cAAc,EAAyB,CAIpD,OAHI,OAAO,aAAiB,IACnB,GAEF,aAAiB,cAAgB,EAAM,OAAS,eACzD,CAEA,MAAc,wBACZ,EACA,EACA,EACA,EACgB,CAChB,GAAI,EAAK,SAAW,EAClB,MAAU,MAAM,2DAA2D,EAE7E,GAAI,IAAc,SAChB,MAAU,MACR,0CAA0C,EAAU,6FAEtD,EAEF,IAAM,EAAmB,IAAI,IACvB,EAAqB,CAAC,EAC5B,IAAK,IAAM,KAAO,EAChB,GAAI,KAAK,cAAc,CAAG,EACxB,EAAS,KAAK,CAAG,MACZ,CACL,IAAM,EAAM,EAAI,MAAM,0BAA0B,IAAI,IAAI,YAAY,EACpE,EAAiB,IAAI,EAAK,EAAM,uBAAuB,IAAQ,sCAAsC,CACvG,CAEF,GAAI,EAAS,SAAW,EAAG,CACzB,IAAM,EAAS,EAAK,IAAK,GAAM,GAAG,EAAE,IAAI,EAAiB,IAAI,CAAC,GAAK,qBAAqB,EAAE,KAAK,IAAI,EACnG,MAAU,MACR,gDAAgD,EAAK,KAAK,IAAI,EAAE,mGAElD,GAChB,CACF,CACA,IAAK,IAAM,KAAO,EAChB,GAAI,CAEF,OAAO,MAAM,KAAK,gBAAgB,EAAK,SAAU,EAAS,CAAM,CAClE,OAAS,EAAO,CAId,GAHK,GAAoC,OAAS,cAG9C,CAAC,EAAU,cAAc,CAAK,EAGhC,MAAM,EAER,EAAiB,IAAI,EAAK,kBAAmB,GAAiB,SAAW,OAAO,CAAK,GAAG,CAC1F,CAEF,IAAM,EAAS,EAAK,IAAK,GAAM,GAAG,EAAE,IAAI,EAAiB,IAAI,CAAC,GAAK,WAAW,EAAE,KAAK,IAAI,EACzF,MAAU,MACR,kEAAkE,EAAK,KAAK,IAAI,EAAE,cAAc,GAClG,CACF,CAEA,cAAsB,EAAsB,CAC1C,GAAI,OAAO,MAAU,IACnB,MAAO,GAET,IAAM,EAAO,GAAe,CAAG,EAC/B,GAAI,CAAC,EACH,MAAO,GAET,GAAI,CAEF,IAAM,EAAS,IADG,MACH,EAAM,YAAY,CAAI,EACrC,OAAO,IAAW,YAAc,IAAW,OAC7C,MAAQ,CACN,MAAO,EACT,CACF,CAEA,MAAM,YAAY,EAAiC,CACjD,IAAM,EAAQ,IAAI,EAElB,OADA,EAAO,QAAS,GAAU,EAAM,SAAS,CAAK,CAAC,EACxC,CACT,CAWA,MAAM,oBACJ,EACA,EAAuB,SACvB,EAAmB,OACnB,EACgB,CAChB,IAAM,EAAQ,IAAI,EAGlB,OADA,MADqB,QAAQ,IAAI,EAAK,IAAK,GAAQ,KAAK,YAAY,EAAK,EAAW,EAAS,CAAM,CAAC,CAAC,GAC9F,QAAS,GAAU,EAAM,SAAS,CAAK,CAAC,EACxC,CACT,CAaA,MAAM,aACJ,EACA,EACA,EAAmB,OACoB,CACvC,GAAI,oBAAoB,KAAK,CAAG,EAC9B,OAAO,KAAK,eAAe,EAAK,EAAS,CAAM,EAGjD,GAAI,OAAO,WAAW,cAAiB,WACrC,OAAO,KAAK,iBAAiB,EAAK,YAAa,EAAS,CAAM,EAGhE,IAAM,EAAU,MAAM,GAAqB,KAAK,EAAK,KAAK,QAAQ,WAAY,CAAM,EACpF,GAAI,CAAC,EACH,OAAO,KAAK,iBAAiB,EAAK,YAAa,EAAS,CAAM,EAEhE,GAAI,CACF,IAAM,EAAQ,MAAM,KAAK,qBAAqB,CAC5C,aAAc,EAAQ,aACtB,UACA,QACF,CAAC,EACD,OAAO,EAAQ,OAAO,CAAK,CAC7B,OAAS,EAAO,CAEd,MADA,EAAQ,QAAQ,EACV,CACR,CACF,CAEA,uBAAuB,EAAqB,EAAqD,CAC/F,OAAO,IAAI,EAAiB,EAAQ,KAAK,QAAS,KAAK,eAAgB,EAAS,IAAI,CACtF,CAOA,MAAM,qBAAqB,EAAiC,CAAC,EAA4B,CACvF,EAAQ,QAAQ,eAAe,EAC/B,IAAM,EAAe,EAAQ,cAAgB,EACvC,EAAiB,EAAQ,gBAAkB,EAC3C,EAAU,EAAQ,SAAW,IACnC,GAAI,CAAC,OAAO,UAAU,CAAY,GAAK,EAAe,GAAK,EAAe,GACxE,MAAU,WAAW,sDAAsD,EAE7E,GAAI,CAAC,OAAO,SAAS,CAAc,GAAK,GAAkB,EACxD,MAAU,WAAW,8CAA8C,EAErE,GAAI,CAAC,OAAO,SAAS,CAAO,GAAK,EAAU,GAAK,EAAU,EACxD,MAAU,WAAW,qDAAqD,EAG5E,IAAM,EAAiB,KAAK,KAAK,KAAK,QAAQ,WAAa,CAAc,EACnE,EAAgB,KAAK,KAAK,KAAK,QAAQ,WAAa,CAAO,EAa3D,EAAQ,IAAI,GAChB,MAbiB,KAAK,kBAAkB,EAAS,UAAU,KAAM,EAAS,UAAU,IAAK,EAAQ,OAAQ,CACzG,eAAgB,EAChB,gBAAiB,EACjB,mBAAoB,CAAC,CAAY,EACjC,eACA,iBAAkB,WAClB,iBAAkB,CAChB,iBACA,eACA,eACF,CACF,CAAC,EAGC,KAAK,QACL,KAAK,eACL,CACE,GAAG,EACH,eACA,iBACA,SACF,EACA,IACF,EAKA,OAJI,EAAQ,QAAQ,UAClB,EAAM,QAAQ,EACd,EAAQ,OAAO,eAAe,GAEzB,CACT,CAEA,oBAAsB,CAAE,OAAM,YAAW,OAAM,OAA+C,CACxF,IAAc,IAAA,KAChB,EAAY,KAEd,IAAM,EAAS,KAAK,QAAQ,mBAAmB,EAM/C,MALA,GAAO,KAAO,GAAQ,UACtB,EAAO,UAAU,MAAQ,EACzB,EAAO,KAAK,MAAQ,GAAQ,EAC5B,EAAO,EAAE,MAAQ,GAAK,EACtB,EAAsB,CAAM,EACrB,CACT,EASA,YAAY,EAAkC,CAC5C,OAAO,IAAI,EAAY,CAAI,CAC7B,CASA,aAAa,EAAyB,CAAC,EAAiB,CACtD,OAAO,IAAI,EAAa,KAAM,CAAO,CACvC,CASA,sBAAsB,EAA+B,EAAkC,CAAC,EAA0B,CAChH,OAAO,IAAI,EAAsB,KAAM,EAAQ,CAAO,CACxD,CAaA,gBAAgB,EAA4B,CAAC,EAAoB,CAC/D,OAAO,IAAI,EAAgB,KAAM,CAAO,CAC1C,CAUA,iBAAiB,EAA2B,CAAC,EAAmB,CAC9D,OAAO,IAAI,EAAe,KAAM,CAAO,CACzC,CASA,cAAc,EAA0C,CAAC,EAAmB,CAC1E,OAAO,IAAI,EAAe,KAAM,CAAE,GAAG,EAAS,MAAO,GAAK,CAAC,CAC7D,CAQA,WAAW,EAA2B,CAAC,EAAmB,CACxD,OAAO,IAAI,EAAe,KAAM,CAAE,MAAO,GAAoB,GAAG,CAAQ,CAAC,CAC3E,CAYA,iBAAiB,EAA6B,CAAC,EAAqB,CAClE,OAAO,IAAI,EAAiB,KAAM,CAAO,CAC3C,CAQA,iBAAiB,EAA6B,CAAC,EAAqB,CAClE,OAAO,IAAI,EAAiB,KAAM,CAAE,MAAO,EAAG,MAAO,EAAG,GAAG,CAAQ,CAAC,CACtE,CAUA,YAAY,EAAiC,CAAC,EAAyB,CACrE,OAAO,IAAI,EAAqB,KAAM,CACpC,MAAO,EACP,YAAa,EACb,SAAU,EACV,UAAW,IACX,MAAO,EACP,KAAM,EACN,GAAG,CACL,CAAC,CACH,CAeA,aAAa,EAAiC,CAAC,EAAyB,CACtE,OAAO,IAAI,EAAqB,KAAM,CACpC,MAAO,EACP,YAAa,EACb,SAAU,EACV,UAAW,EACX,MAAO,EACP,KAAM,GACN,GAAG,CACL,CAAC,CACH,CASA,cAAc,EAAiC,CAAC,EAAyB,CACvE,OAAO,IAAI,EAAqB,KAAM,CACpC,MAAO,EACP,YAAa,EACb,SAAU,CAAC,EACX,UAAW,EACX,MAAO,EACP,KAAM,IACN,GAAG,CACL,CAAC,CACH,CAQA,cAAc,EAAiC,CAAC,EAAyB,CACvE,OAAO,IAAI,EAAqB,KAAM,CACpC,MAAO,EACP,YAAa,EACb,SAAU,EACV,UAAW,EACX,MAAO,EACP,KAAM,EACN,GAAG,CACL,CAAC,CACH,CAQA,eAAe,EAAiC,CAAC,EAAyB,CACxE,OAAO,IAAI,EAAqB,KAAM,CACpC,MAAO,EACP,YAAa,EACb,SAAU,EACV,UAAW,GACX,MAAO,EACP,KAAM,GACN,GAAG,CACL,CAAC,CACH,CAUA,aAAa,EAAyB,CAAC,EAAiB,CACtD,OAAO,IAAI,EAAa,KAAM,CAAO,CACvC,CAYA,cAAc,EAA0B,CAAC,EAAkB,CACzD,OAAO,IAAI,EAAc,KAAM,CAAO,CACxC,CAUA,cAAc,EAA0B,CAAC,EAAkB,CACzD,OAAO,IAAI,EAAc,KAAM,CAAE,YAAa,IAAK,GAAG,CAAQ,CAAC,CACjE,CAOA,uBAAuB,EAAmC,CAAC,EAA2B,CACpF,OAAO,IAAI,EAAuB,KAAM,CAAO,CACjD,CAOA,iBAAiB,EAA6B,CAAC,EAAqB,CAClE,OAAO,IAAI,EAAiB,KAAM,CAAO,CAC3C,CAGA,iBAAiB,EAA6B,CAAC,EAAqB,CAClE,OAAO,IAAI,EAAiB,KAAM,CAAO,CAC3C,CAMA,qBAAqB,EAAiC,CAAC,EAAyB,CAC9E,OAAO,IAAI,EAAqB,KAAM,CAAO,CAC/C,CAGA,oBAAoB,EAAgC,CAAC,EAAwB,CAC3E,OAAO,IAAI,EAAoB,KAAM,CAAO,CAC9C,CAIA,gBAA8B,CAC5B,OAAO,KAAK,OACd,CAuBA,MAAM,iBAAiB,EAA6B,CAAC,EAAG,EAA4C,CAClG,OAAO,EAAW,OAAO,KAAM,EAAS,CAAO,CACjD,CAQA,uBAAuB,EAA6B,CAAC,EAAqB,CACxE,OAAO,IAAI,GAAiB,KAAM,CAAO,CAC3C,CAsBA,MAAM,oBAAoB,EAA0B,KAAK,OAAgC,CACvF,IAAM,EAAwB,aAAkB,EAAM,EAAO,OAAS,EAOhE,EAAiB,EAAW,SAAuC,KAAK,QAe9E,OAdA,MAAM,KAAK,uBACT,EAAS,cAAc,KACvB,EAAS,cAAc,IACvB,IAAA,GACA,CACF,EASO,IAAI,GAAc,MARN,KAAK,kBACtB,EAAS,cAAc,KACvB,EAAS,cAAc,IACvB,IAAA,GACA,CAAE,eAAgB,EAAG,gBAAiB,CAAE,EACxC,CACF,EAE+B,EADZ,EAAc,WACU,EAAY,EAAc,WAAW,CAClF,CAUA,UAAU,EAAoB,CAC5B,GAAI,IAAS,SACX,MAAU,MAAM,gFAAgF,EAElG,GAAI,IAAS,IAAA,IAAa,KAAK,aAAa,IAAI,CAAI,EAClD,MAAU,MAAM,gBAAgB,EAAK,kBAAkB,EAEzD,IAAM,EAAY,IAAS,IAAA,GAAmD,IAAA,OAAjC,KAAK,aAAa,OAAO,CAAI,EACpE,EAAM,IAAI,EAAI,KAAK,QAAS,GAAQ,KAAM,IAAA,GAAW,CAAS,EAOpE,OANI,IAAS,IAAA,IACX,KAAK,aAAa,IAAI,EAAM,CAAG,EAIjC,EAAI,QAAQ,KAAK,MAAM,EAChB,CACT,CAOA,OAAO,EAA+B,CAIpC,OAHI,IAAS,SACJ,KAAK,OAEP,KAAK,aAAa,IAAI,CAAI,CACnC,CAMA,WAAsB,CACpB,MAAO,CAAC,SAAU,GAAG,KAAK,aAAa,KAAK,CAAC,CAC/C,CAEA,eAAe,EAAsB,EAAwB,CAC3D,GAAI,CAAC,KAAK,QAAQ,sBAChB,MAAU,MAAM,mCAAmC,EAErD,OAAO,KAAK,QAAQ,sBAAsB,CAAW,CACvD,CAEA,aAAa,EAAsB,EAAsB,CACvD,GAAI,CAAC,KAAK,QAAQ,oBAChB,MAAU,MAAM,iCAAiC,EAEnD,OAAO,KAAK,QAAQ,oBAAoB,CAAW,CACrD,CAiBA,aAAa,CACX,iBACA,iBACA,gBACA,gBACA,cACA,eACA,mBACA,wBACA,eACA,cACA,gBACA,YACA,YACA,YACA,eACA,eACA,gBACqC,CACrC,IAAM,EAAS,KAAK,QAAQ,aAAa,EAkBzC,MAjBA,GAAO,eAAiB,GAAkB,IAC1C,EAAO,eAAiB,GAAkB,IAC1C,EAAO,cAAgB,GAAiB,EACxC,EAAO,cAAgB,GAAiB,UACxC,EAAO,YAAc,GAAe,IACpC,EAAO,aAAe,GAAgB,EACtC,EAAO,iBAAmB,GAAoB,cAC9C,EAAO,sBAAwB,GAAyB,WACxD,EAAO,aAAe,GAAgB,OACtC,EAAO,YAAc,GAAe,EACpC,EAAO,cAAgB,GAAiB,EACxC,EAAO,UAAU,MAAQ,GAAa,EACtC,EAAO,UAAU,MAAQ,GAAa,EACtC,EAAO,UAAU,MAAQ,GAAa,EACtC,EAAO,aAAa,MAAQ,GAAgB,EAC5C,EAAO,aAAa,MAAQ,GAAgB,EAC5C,EAAO,aAAa,MAAQ,GAAgB,EACrC,CACT,CAUA,MAAM,OAAuB,CACtB,KAAK,QAAQ,SAGd,KAAK,eAAiB,cAG1B,MAAM,KAAK,QAAQ,QAAQ,EAC3B,KAAK,aAAe,YACpB,KAAK,KAAK,UAAW,IAAA,EAAS,EAChC,CAWA,MAAM,QAAwB,CACvB,KAAK,QAAQ,SAGlB,MAAM,KAAK,QAAQ,OAAO,EAC1B,KAAK,aAAe,UACpB,KAAK,KAAK,SAAU,IAAA,EAAS,EAC/B,CAEA,gBAAgB,EAAgB,CAC9B,GAAI,KAAK,MAAO,CACd,KAAK,WAAa,EAClB,MACF,CACI,KAAK,eAAe,KAAK,QAAU,IAGvC,KAAK,eAAe,KAAK,MAAQ,EACjC,KAAK,KAAK,eAAgB,CAAM,EAClC,CAEA,IAAI,QAAiB,CACnB,OAAO,KAAK,eAAe,KAAK,KAClC,CAEA,IAAI,OAAO,EAAgB,CACzB,GAAI,KAAK,MAAO,CACd,KAAK,WAAa,EAClB,MACF,CACA,KAAK,gBAAgB,CAAM,CAC7B,CAEA,MAAO,CACA,KAAK,QACR,KAAK,WAAa,KAAK,eAAe,KAAK,MAC3C,KAAK,QAAU,GACX,KAAK,eAAe,KAAK,QAAU,IACrC,KAAK,eAAe,KAAK,MAAQ,EACjC,KAAK,KAAK,eAAgB,CAAC,GAE7B,KAAK,KAAK,OAAQ,IAAA,EAAS,EAE/B,CAEA,QAAS,CACH,KAAK,QACP,KAAK,QAAU,GACf,KAAK,gBAAgB,KAAK,UAAU,EACpC,KAAK,KAAK,SAAU,IAAA,EAAS,EAEjC,CAEA,IAAI,OAAiB,CACnB,OAAO,KAAK,OACd,CAEA,IAAI,MAAM,EAAgB,CACpB,IAAU,KAAK,QACb,EACF,KAAK,KAAK,EAEV,KAAK,OAAO,EAGlB,CAEA,MAAM,oBAAoB,EAAmC,CAAC,EAA8B,CAC1F,OAAO,GAAiB,QAAQ,KAAK,QAAS,KAAK,eAAgB,EAAS,IAAI,CAClF,CAEA,IAAI,qBAAmC,CACrC,MAAO,CACL,QAAS,CAAC,GAAG,KAAK,qBAAqB,EACvC,GAAI,CAAC,GAAG,KAAK,gBAAgB,CAC/B,CACF,CAEA,IAAI,oBAAoB,EAA0B,CAChD,IAAM,EAAoB,CAAC,EAAY,QAAQ,GAAI,EAAY,QAAQ,GAAI,EAAY,QAAQ,EAAE,EAC3F,EAAe,CAAC,EAAY,GAAG,GAAI,EAAY,GAAG,GAAI,EAAY,GAAG,EAAE,EAC7E,GAAyB,KAAK,SAAU,EAAS,CAAE,EACnD,KAAK,sBAAwB,EAC7B,KAAK,iBAAmB,CAC1B,CAEA,IAAI,uBAAkC,CACpC,MAAO,CAAC,GAAG,KAAK,gBAAgB,CAClC,CAEA,IAAI,sBAAsB,EAAc,CACtC,IAAM,EAAmB,CAAC,EAAG,GAAI,EAAG,GAAI,EAAG,EAAE,EAC7C,GAAyB,KAAK,SAAU,KAAK,sBAAuB,CAAM,EAC1E,KAAK,iBAAmB,CAC1B,CAEA,IAAI,4BAAuC,CACzC,MAAO,CAAC,GAAG,KAAK,qBAAqB,CACvC,CAEA,IAAI,2BAA2B,EAAmB,CAChD,IAAM,EAAwB,CAAC,EAAQ,GAAI,EAAQ,GAAI,EAAQ,EAAE,EACjE,GAAyB,KAAK,SAAU,EAAa,KAAK,gBAAgB,EAC1E,KAAK,sBAAwB,CAC/B,CAEA,IAAI,kBAA6B,CAC/B,MAAO,CAAC,GAAG,KAAK,sBAAsB,CACxC,CAEA,IAAI,iBAAiB,EAAoB,CACvC,IAAM,EAAyB,CAAC,EAAS,GAAI,EAAS,GAAI,EAAS,EAAE,EACrE,GAAsB,KAAK,SAAU,EAAc,KAAK,QAAQ,WAAW,EAC3E,KAAK,uBAAyB,CAChC,CACF"}
|