diff --git a/KoLmafia/dependencies.txt b/KoLmafia/dependencies.txt deleted file mode 100644 index e4f6c6a..0000000 --- a/KoLmafia/dependencies.txt +++ /dev/null @@ -1,4 +0,0 @@ -https://github.com/Ezandora/Bastille/branches/Release/ -https://github.com/Ezandora/Briefcase/branches/Release/ -https://github.com/Ezandora/Voting-Booth/trunk/Release/ -https://github.com/Ezandora/Detective-Solver/branches/Release/ \ No newline at end of file diff --git a/KoLmafia/relay/relay_folgerCS.js b/KoLmafia/relay/relay_folgerCS.js new file mode 100644 index 0000000..c54ebd4 --- /dev/null +++ b/KoLmafia/relay/relay_folgerCS.js @@ -0,0 +1,6655 @@ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 139: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + +/******/(()=>{// webpackBootstrap +/******/"use strict";/******/var __webpack_modules__={/***/447:/***/module=>{module.exports="data:application/javascript;base64,KGZ1bmN0aW9uIHdlYnBhY2tVbml2ZXJzYWxNb2R1bGVEZWZpbml0aW9uKHJvb3QsIGZhY3RvcnkpIHsKCWlmKHR5cGVvZiBleHBvcnRzID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0JykKCQltb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKTsKCWVsc2UgaWYodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKQoJCWRlZmluZShbXSwgZmFjdG9yeSk7CgllbHNlIHsKCQl2YXIgYSA9IGZhY3RvcnkoKTsKCQlmb3IodmFyIGkgaW4gYSkgKHR5cGVvZiBleHBvcnRzID09PSAnb2JqZWN0JyA/IGV4cG9ydHMgOiByb290KVtpXSA9IGFbaV07Cgl9Cn0pKHNlbGYsICgpID0+IHsKcmV0dXJuIC8qKioqKiovICgoKSA9PiB7IC8vIHdlYnBhY2tCb290c3RyYXAKLyoqKioqKi8gCSJ1c2Ugc3RyaWN0IjsKLyoqKioqKi8gCXZhciBfX3dlYnBhY2tfbW9kdWxlc19fID0gKHsKCi8qKiovIDI0MjoKLyoqKi8gKChfX3VudXNlZF93ZWJwYWNrX21vZHVsZSwgX193ZWJwYWNrX2V4cG9ydHNfXywgX193ZWJwYWNrX3JlcXVpcmVfXykgPT4gewoKCi8vIEVYUE9SVFMKX193ZWJwYWNrX3JlcXVpcmVfXy5kKF9fd2VicGFja19leHBvcnRzX18sIHsKICAiWiI6ICgpID0+ICgvKiBiaW5kaW5nICovIHJlbGF5X0FwcCkKfSk7Cgo7Ly8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9ub2RlX21vZHVsZXMvQHJlbWl4LXJ1bi9yb3V0ZXIvZGlzdC9yb3V0ZXIuanMKLyoqCiAqIEByZW1peC1ydW4vcm91dGVyIHYxLjAuMwogKgogKiBDb3B5cmlnaHQgKGMpIFJlbWl4IFNvZnR3YXJlIEluYy4KICoKICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlCiAqIExJQ0VOU0UubWQgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS4KICoKICogQGxpY2Vuc2UgTUlUCiAqLwpmdW5jdGlvbiBfZXh0ZW5kcygpIHsKICBfZXh0ZW5kcyA9IE9iamVjdC5hc3NpZ24gPyBPYmplY3QuYXNzaWduLmJpbmQoKSA6IGZ1bmN0aW9uICh0YXJnZXQpIHsKICAgIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBzb3VyY2UgPSBhcmd1bWVudHNbaV07CgogICAgICBmb3IgKHZhciBrZXkgaW4gc291cmNlKSB7CiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzb3VyY2UsIGtleSkpIHsKICAgICAgICAgIHRhcmdldFtrZXldID0gc291cmNlW2tleV07CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgcmV0dXJuIHRhcmdldDsKICB9OwogIHJldHVybiBfZXh0ZW5kcy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwp9CgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyNyZWdpb24gVHlwZXMgYW5kIENvbnN0YW50cwovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKLyoqCiAqIEFjdGlvbnMgcmVwcmVzZW50IHRoZSB0eXBlIG9mIGNoYW5nZSB0byBhIGxvY2F0aW9uIHZhbHVlLgogKi8KdmFyIEFjdGlvbjsKCihmdW5jdGlvbiAoQWN0aW9uKSB7CiAgLyoqCiAgICogQSBQT1AgaW5kaWNhdGVzIGEgY2hhbmdlIHRvIGFuIGFyYml0cmFyeSBpbmRleCBpbiB0aGUgaGlzdG9yeSBzdGFjaywgc3VjaAogICAqIGFzIGEgYmFjayBvciBmb3J3YXJkIG5hdmlnYXRpb24uIEl0IGRvZXMgbm90IGRlc2NyaWJlIHRoZSBkaXJlY3Rpb24gb2YgdGhlCiAgICogbmF2aWdhdGlvbiwgb25seSB0aGF0IHRoZSBjdXJyZW50IGluZGV4IGNoYW5nZWQuCiAgICoKICAgKiBOb3RlOiBUaGlzIGlzIHRoZSBkZWZhdWx0IGFjdGlvbiBmb3IgbmV3bHkgY3JlYXRlZCBoaXN0b3J5IG9iamVjdHMuCiAgICovCiAgQWN0aW9uWyJQb3AiXSA9ICJQT1AiOwogIC8qKgogICAqIEEgUFVTSCBpbmRpY2F0ZXMgYSBuZXcgZW50cnkgYmVpbmcgYWRkZWQgdG8gdGhlIGhpc3Rvcnkgc3RhY2ssIHN1Y2ggYXMgd2hlbgogICAqIGEgbGluayBpcyBjbGlja2VkIGFuZCBhIG5ldyBwYWdlIGxvYWRzLiBXaGVuIHRoaXMgaGFwcGVucywgYWxsIHN1YnNlcXVlbnQKICAgKiBlbnRyaWVzIGluIHRoZSBzdGFjayBhcmUgbG9zdC4KICAgKi8KCiAgQWN0aW9uWyJQdXNoIl0gPSAiUFVTSCI7CiAgLyoqCiAgICogQSBSRVBMQUNFIGluZGljYXRlcyB0aGUgZW50cnkgYXQgdGhlIGN1cnJlbnQgaW5kZXggaW4gdGhlIGhpc3Rvcnkgc3RhY2sKICAgKiBiZWluZyByZXBsYWNlZCBieSBhIG5ldyBvbmUuCiAgICovCgogIEFjdGlvblsiUmVwbGFjZSJdID0gIlJFUExBQ0UiOwp9KShBY3Rpb24gfHwgKEFjdGlvbiA9IHt9KSk7Cgpjb25zdCBQb3BTdGF0ZUV2ZW50VHlwZSA9ICJwb3BzdGF0ZSI7Ci8qKgogKiBNZW1vcnkgaGlzdG9yeSBzdG9yZXMgdGhlIGN1cnJlbnQgbG9jYXRpb24gaW4gbWVtb3J5LiBJdCBpcyBkZXNpZ25lZCBmb3IgdXNlCiAqIGluIHN0YXRlZnVsIG5vbi1icm93c2VyIGVudmlyb25tZW50cyBsaWtlIHRlc3RzIGFuZCBSZWFjdCBOYXRpdmUuCiAqLwoKZnVuY3Rpb24gcm91dGVyX2NyZWF0ZU1lbW9yeUhpc3Rvcnkob3B0aW9ucykgewogIGlmIChvcHRpb25zID09PSB2b2lkIDApIHsKICAgIG9wdGlvbnMgPSB7fTsKICB9CgogIGxldCB7CiAgICBpbml0aWFsRW50cmllcyA9IFsiLyJdLAogICAgaW5pdGlhbEluZGV4LAogICAgdjVDb21wYXQgPSBmYWxzZQogIH0gPSBvcHRpb25zOwogIGxldCBlbnRyaWVzOyAvLyBEZWNsYXJlIHNvIHdlIGNhbiBhY2Nlc3MgZnJvbSBjcmVhdGVNZW1vcnlMb2NhdGlvbgoKICBlbnRyaWVzID0gaW5pdGlhbEVudHJpZXMubWFwKChlbnRyeSwgaW5kZXgpID0+IGNyZWF0ZU1lbW9yeUxvY2F0aW9uKGVudHJ5LCB0eXBlb2YgZW50cnkgPT09ICJzdHJpbmciID8gbnVsbCA6IGVudHJ5LnN0YXRlLCBpbmRleCA9PT0gMCA/ICJkZWZhdWx0IiA6IHVuZGVmaW5lZCkpOwogIGxldCBpbmRleCA9IGNsYW1wSW5kZXgoaW5pdGlhbEluZGV4ID09IG51bGwgPyBlbnRyaWVzLmxlbmd0aCAtIDEgOiBpbml0aWFsSW5kZXgpOwogIGxldCBhY3Rpb24gPSBBY3Rpb24uUG9wOwogIGxldCBsaXN0ZW5lciA9IG51bGw7CgogIGZ1bmN0aW9uIGNsYW1wSW5kZXgobikgewogICAgcmV0dXJuIE1hdGgubWluKE1hdGgubWF4KG4sIDApLCBlbnRyaWVzLmxlbmd0aCAtIDEpOwogIH0KCiAgZnVuY3Rpb24gZ2V0Q3VycmVudExvY2F0aW9uKCkgewogICAgcmV0dXJuIGVudHJpZXNbaW5kZXhdOwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlTWVtb3J5TG9jYXRpb24odG8sIHN0YXRlLCBrZXkpIHsKICAgIGlmIChzdGF0ZSA9PT0gdm9pZCAwKSB7CiAgICAgIHN0YXRlID0gbnVsbDsKICAgIH0KCiAgICBsZXQgbG9jYXRpb24gPSBjcmVhdGVMb2NhdGlvbihlbnRyaWVzID8gZ2V0Q3VycmVudExvY2F0aW9uKCkucGF0aG5hbWUgOiAiLyIsIHRvLCBzdGF0ZSwga2V5KTsKICAgIHdhcm5pbmckMShsb2NhdGlvbi5wYXRobmFtZS5jaGFyQXQoMCkgPT09ICIvIiwgInJlbGF0aXZlIHBhdGhuYW1lcyBhcmUgbm90IHN1cHBvcnRlZCBpbiBtZW1vcnkgaGlzdG9yeTogIiArIEpTT04uc3RyaW5naWZ5KHRvKSk7CiAgICByZXR1cm4gbG9jYXRpb247CiAgfQoKICBsZXQgaGlzdG9yeSA9IHsKICAgIGdldCBpbmRleCgpIHsKICAgICAgcmV0dXJuIGluZGV4OwogICAgfSwKCiAgICBnZXQgYWN0aW9uKCkgewogICAgICByZXR1cm4gYWN0aW9uOwogICAgfSwKCiAgICBnZXQgbG9jYXRpb24oKSB7CiAgICAgIHJldHVybiBnZXRDdXJyZW50TG9jYXRpb24oKTsKICAgIH0sCgogICAgY3JlYXRlSHJlZih0bykgewogICAgICByZXR1cm4gdHlwZW9mIHRvID09PSAic3RyaW5nIiA/IHRvIDogcm91dGVyX2NyZWF0ZVBhdGgodG8pOwogICAgfSwKCiAgICBlbmNvZGVMb2NhdGlvbihsb2NhdGlvbikgewogICAgICByZXR1cm4gbG9jYXRpb247CiAgICB9LAoKICAgIHB1c2godG8sIHN0YXRlKSB7CiAgICAgIGFjdGlvbiA9IEFjdGlvbi5QdXNoOwogICAgICBsZXQgbmV4dExvY2F0aW9uID0gY3JlYXRlTWVtb3J5TG9jYXRpb24odG8sIHN0YXRlKTsKICAgICAgaW5kZXggKz0gMTsKICAgICAgZW50cmllcy5zcGxpY2UoaW5kZXgsIGVudHJpZXMubGVuZ3RoLCBuZXh0TG9jYXRpb24pOwoKICAgICAgaWYgKHY1Q29tcGF0ICYmIGxpc3RlbmVyKSB7CiAgICAgICAgbGlzdGVuZXIoewogICAgICAgICAgYWN0aW9uLAogICAgICAgICAgbG9jYXRpb246IG5leHRMb2NhdGlvbgogICAgICAgIH0pOwogICAgICB9CiAgICB9LAoKICAgIHJlcGxhY2UodG8sIHN0YXRlKSB7CiAgICAgIGFjdGlvbiA9IEFjdGlvbi5SZXBsYWNlOwogICAgICBsZXQgbmV4dExvY2F0aW9uID0gY3JlYXRlTWVtb3J5TG9jYXRpb24odG8sIHN0YXRlKTsKICAgICAgZW50cmllc1tpbmRleF0gPSBuZXh0TG9jYXRpb247CgogICAgICBpZiAodjVDb21wYXQgJiYgbGlzdGVuZXIpIHsKICAgICAgICBsaXN0ZW5lcih7CiAgICAgICAgICBhY3Rpb24sCiAgICAgICAgICBsb2NhdGlvbjogbmV4dExvY2F0aW9uCiAgICAgICAgfSk7CiAgICAgIH0KICAgIH0sCgogICAgZ28oZGVsdGEpIHsKICAgICAgYWN0aW9uID0gQWN0aW9uLlBvcDsKICAgICAgaW5kZXggPSBjbGFtcEluZGV4KGluZGV4ICsgZGVsdGEpOwoKICAgICAgaWYgKGxpc3RlbmVyKSB7CiAgICAgICAgbGlzdGVuZXIoewogICAgICAgICAgYWN0aW9uLAogICAgICAgICAgbG9jYXRpb246IGdldEN1cnJlbnRMb2NhdGlvbigpCiAgICAgICAgfSk7CiAgICAgIH0KICAgIH0sCgogICAgbGlzdGVuKGZuKSB7CiAgICAgIGxpc3RlbmVyID0gZm47CiAgICAgIHJldHVybiAoKSA9PiB7CiAgICAgICAgbGlzdGVuZXIgPSBudWxsOwogICAgICB9OwogICAgfQoKICB9OwogIHJldHVybiBoaXN0b3J5Owp9Ci8qKgogKiBCcm93c2VyIGhpc3Rvcnkgc3RvcmVzIHRoZSBsb2NhdGlvbiBpbiByZWd1bGFyIFVSTHMuIFRoaXMgaXMgdGhlIHN0YW5kYXJkIGZvcgogKiBtb3N0IHdlYiBhcHBzLCBidXQgaXQgcmVxdWlyZXMgc29tZSBjb25maWd1cmF0aW9uIG9uIHRoZSBzZXJ2ZXIgdG8gZW5zdXJlIHlvdQogKiBzZXJ2ZSB0aGUgc2FtZSBhcHAgYXQgbXVsdGlwbGUgVVJMcy4KICoKICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vcmVtaXgtcnVuL2hpc3RvcnkvdHJlZS9tYWluL2RvY3MvYXBpLXJlZmVyZW5jZS5tZCNjcmVhdGVicm93c2VyaGlzdG9yeQogKi8KCmZ1bmN0aW9uIHJvdXRlcl9jcmVhdGVCcm93c2VySGlzdG9yeShvcHRpb25zKSB7CiAgaWYgKG9wdGlvbnMgPT09IHZvaWQgMCkgewogICAgb3B0aW9ucyA9IHt9OwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlQnJvd3NlckxvY2F0aW9uKHdpbmRvdywgZ2xvYmFsSGlzdG9yeSkgewogICAgbGV0IHsKICAgICAgcGF0aG5hbWUsCiAgICAgIHNlYXJjaCwKICAgICAgaGFzaAogICAgfSA9IHdpbmRvdy5sb2NhdGlvbjsKICAgIHJldHVybiBjcmVhdGVMb2NhdGlvbigiIiwgewogICAgICBwYXRobmFtZSwKICAgICAgc2VhcmNoLAogICAgICBoYXNoCiAgICB9LCAvLyBzdGF0ZSBkZWZhdWx0cyB0byBgbnVsbGAgYmVjYXVzZSBgd2luZG93Lmhpc3Rvcnkuc3RhdGVgIGRvZXMKICAgIGdsb2JhbEhpc3Rvcnkuc3RhdGUgJiYgZ2xvYmFsSGlzdG9yeS5zdGF0ZS51c3IgfHwgbnVsbCwgZ2xvYmFsSGlzdG9yeS5zdGF0ZSAmJiBnbG9iYWxIaXN0b3J5LnN0YXRlLmtleSB8fCAiZGVmYXVsdCIpOwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlQnJvd3NlckhyZWYod2luZG93LCB0bykgewogICAgcmV0dXJuIHR5cGVvZiB0byA9PT0gInN0cmluZyIgPyB0byA6IHJvdXRlcl9jcmVhdGVQYXRoKHRvKTsKICB9CgogIHJldHVybiBnZXRVcmxCYXNlZEhpc3RvcnkoY3JlYXRlQnJvd3NlckxvY2F0aW9uLCBjcmVhdGVCcm93c2VySHJlZiwgbnVsbCwgb3B0aW9ucyk7Cn0KLyoqCiAqIEhhc2ggaGlzdG9yeSBzdG9yZXMgdGhlIGxvY2F0aW9uIGluIHdpbmRvdy5sb2NhdGlvbi5oYXNoLiBUaGlzIG1ha2VzIGl0IGlkZWFsCiAqIGZvciBzaXR1YXRpb25zIHdoZXJlIHlvdSBkb24ndCB3YW50IHRvIHNlbmQgdGhlIGxvY2F0aW9uIHRvIHRoZSBzZXJ2ZXIgZm9yCiAqIHNvbWUgcmVhc29uLCBlaXRoZXIgYmVjYXVzZSB5b3UgZG8gY2Fubm90IGNvbmZpZ3VyZSBpdCBvciB0aGUgVVJMIHNwYWNlIGlzCiAqIHJlc2VydmVkIGZvciBzb21ldGhpbmcgZWxzZS4KICoKICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vcmVtaXgtcnVuL2hpc3RvcnkvdHJlZS9tYWluL2RvY3MvYXBpLXJlZmVyZW5jZS5tZCNjcmVhdGVoYXNoaGlzdG9yeQogKi8KCmZ1bmN0aW9uIHJvdXRlcl9jcmVhdGVIYXNoSGlzdG9yeShvcHRpb25zKSB7CiAgaWYgKG9wdGlvbnMgPT09IHZvaWQgMCkgewogICAgb3B0aW9ucyA9IHt9OwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlSGFzaExvY2F0aW9uKHdpbmRvdywgZ2xvYmFsSGlzdG9yeSkgewogICAgbGV0IHsKICAgICAgcGF0aG5hbWUgPSAiLyIsCiAgICAgIHNlYXJjaCA9ICIiLAogICAgICBoYXNoID0gIiIKICAgIH0gPSBwYXJzZVBhdGgod2luZG93LmxvY2F0aW9uLmhhc2guc3Vic3RyKDEpKTsKICAgIHJldHVybiBjcmVhdGVMb2NhdGlvbigiIiwgewogICAgICBwYXRobmFtZSwKICAgICAgc2VhcmNoLAogICAgICBoYXNoCiAgICB9LCAvLyBzdGF0ZSBkZWZhdWx0cyB0byBgbnVsbGAgYmVjYXVzZSBgd2luZG93Lmhpc3Rvcnkuc3RhdGVgIGRvZXMKICAgIGdsb2JhbEhpc3Rvcnkuc3RhdGUgJiYgZ2xvYmFsSGlzdG9yeS5zdGF0ZS51c3IgfHwgbnVsbCwgZ2xvYmFsSGlzdG9yeS5zdGF0ZSAmJiBnbG9iYWxIaXN0b3J5LnN0YXRlLmtleSB8fCAiZGVmYXVsdCIpOwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlSGFzaEhyZWYod2luZG93LCB0bykgewogICAgbGV0IGJhc2UgPSB3aW5kb3cuZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiYmFzZSIpOwogICAgbGV0IGhyZWYgPSAiIjsKCiAgICBpZiAoYmFzZSAmJiBiYXNlLmdldEF0dHJpYnV0ZSgiaHJlZiIpKSB7CiAgICAgIGxldCB1cmwgPSB3aW5kb3cubG9jYXRpb24uaHJlZjsKICAgICAgbGV0IGhhc2hJbmRleCA9IHVybC5pbmRleE9mKCIjIik7CiAgICAgIGhyZWYgPSBoYXNoSW5kZXggPT09IC0xID8gdXJsIDogdXJsLnNsaWNlKDAsIGhhc2hJbmRleCk7CiAgICB9CgogICAgcmV0dXJuIGhyZWYgKyAiIyIgKyAodHlwZW9mIHRvID09PSAic3RyaW5nIiA/IHRvIDogcm91dGVyX2NyZWF0ZVBhdGgodG8pKTsKICB9CgogIGZ1bmN0aW9uIHZhbGlkYXRlSGFzaExvY2F0aW9uKGxvY2F0aW9uLCB0bykgewogICAgd2FybmluZyQxKGxvY2F0aW9uLnBhdGhuYW1lLmNoYXJBdCgwKSA9PT0gIi8iLCAicmVsYXRpdmUgcGF0aG5hbWVzIGFyZSBub3Qgc3VwcG9ydGVkIGluIGhhc2ggaGlzdG9yeS5wdXNoKCIgKyBKU09OLnN0cmluZ2lmeSh0bykgKyAiKSIpOwogIH0KCiAgcmV0dXJuIGdldFVybEJhc2VkSGlzdG9yeShjcmVhdGVIYXNoTG9jYXRpb24sIGNyZWF0ZUhhc2hIcmVmLCB2YWxpZGF0ZUhhc2hMb2NhdGlvbiwgb3B0aW9ucyk7Cn0gLy8jZW5kcmVnaW9uCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCi8vI3JlZ2lvbiBVVElMUwovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKZnVuY3Rpb24gd2FybmluZyQxKGNvbmQsIG1lc3NhZ2UpIHsKICBpZiAoIWNvbmQpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlCiAgICBpZiAodHlwZW9mIGNvbnNvbGUgIT09ICJ1bmRlZmluZWQiKSBjb25zb2xlLndhcm4obWVzc2FnZSk7CgogICAgdHJ5IHsKICAgICAgLy8gV2VsY29tZSB0byBkZWJ1Z2dpbmcgaGlzdG9yeSEKICAgICAgLy8KICAgICAgLy8gVGhpcyBlcnJvciBpcyB0aHJvd24gYXMgYSBjb252ZW5pZW5jZSBzbyB5b3UgY2FuIG1vcmUgZWFzaWx5CiAgICAgIC8vIGZpbmQgdGhlIHNvdXJjZSBmb3IgYSB3YXJuaW5nIHRoYXQgYXBwZWFycyBpbiB0aGUgY29uc29sZSBieQogICAgICAvLyBlbmFibGluZyAicGF1c2Ugb24gZXhjZXB0aW9ucyIgaW4geW91ciBKYXZhU2NyaXB0IGRlYnVnZ2VyLgogICAgICB0aHJvdyBuZXcgRXJyb3IobWVzc2FnZSk7IC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1lbXB0eQogICAgfSBjYXRjaCAoZSkge30KICB9Cn0KCmZ1bmN0aW9uIGNyZWF0ZUtleSgpIHsKICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDgpOwp9Ci8qKgogKiBGb3IgYnJvd3Nlci1iYXNlZCBoaXN0b3JpZXMsIHdlIGNvbWJpbmUgdGhlIHN0YXRlIGFuZCBrZXkgaW50byBhbiBvYmplY3QKICovCgoKZnVuY3Rpb24gZ2V0SGlzdG9yeVN0YXRlKGxvY2F0aW9uKSB7CiAgcmV0dXJuIHsKICAgIHVzcjogbG9jYXRpb24uc3RhdGUsCiAgICBrZXk6IGxvY2F0aW9uLmtleQogIH07Cn0KLyoqCiAqIENyZWF0ZXMgYSBMb2NhdGlvbiBvYmplY3Qgd2l0aCBhIHVuaXF1ZSBrZXkgZnJvbSB0aGUgZ2l2ZW4gUGF0aAogKi8KCgpmdW5jdGlvbiBjcmVhdGVMb2NhdGlvbihjdXJyZW50LCB0bywgc3RhdGUsIGtleSkgewogIGlmIChzdGF0ZSA9PT0gdm9pZCAwKSB7CiAgICBzdGF0ZSA9IG51bGw7CiAgfQoKICBsZXQgbG9jYXRpb24gPSBfZXh0ZW5kcyh7CiAgICBwYXRobmFtZTogdHlwZW9mIGN1cnJlbnQgPT09ICJzdHJpbmciID8gY3VycmVudCA6IGN1cnJlbnQucGF0aG5hbWUsCiAgICBzZWFyY2g6ICIiLAogICAgaGFzaDogIiIKICB9LCB0eXBlb2YgdG8gPT09ICJzdHJpbmciID8gcGFyc2VQYXRoKHRvKSA6IHRvLCB7CiAgICBzdGF0ZSwKICAgIC8vIFRPRE86IFRoaXMgY291bGQgYmUgY2xlYW5lZCB1cC4gIHB1c2gvcmVwbGFjZSBzaG91bGQgcHJvYmFibHkganVzdCB0YWtlCiAgICAvLyBmdWxsIExvY2F0aW9ucyBub3cgYW5kIGF2b2lkIHRoZSBuZWVkIHRvIHJ1biB0aHJvdWdoIHRoaXMgZmxvdyBhdCBhbGwKICAgIC8vIEJ1dCB0aGF0J3MgYSBwcmV0dHkgYmlnIHJlZmFjdG9yIHRvIHRoZSBjdXJyZW50IHRlc3Qgc3VpdGUgc28gZ29pbmcgdG8KICAgIC8vIGtlZXAgYXMgaXMgZm9yIHRoZSB0aW1lIGJlaW5nIGFuZCBqdXN0IGxldCBhbnkgaW5jb21pbmcga2V5cyB0YWtlIHByZWNlZGVuY2UKICAgIGtleTogdG8gJiYgdG8ua2V5IHx8IGtleSB8fCBjcmVhdGVLZXkoKQogIH0pOwoKICByZXR1cm4gbG9jYXRpb247Cn0KLyoqCiAqIENyZWF0ZXMgYSBzdHJpbmcgVVJMIHBhdGggZnJvbSB0aGUgZ2l2ZW4gcGF0aG5hbWUsIHNlYXJjaCwgYW5kIGhhc2ggY29tcG9uZW50cy4KICovCgpmdW5jdGlvbiByb3V0ZXJfY3JlYXRlUGF0aChfcmVmKSB7CiAgbGV0IHsKICAgIHBhdGhuYW1lID0gIi8iLAogICAgc2VhcmNoID0gIiIsCiAgICBoYXNoID0gIiIKICB9ID0gX3JlZjsKICBpZiAoc2VhcmNoICYmIHNlYXJjaCAhPT0gIj8iKSBwYXRobmFtZSArPSBzZWFyY2guY2hhckF0KDApID09PSAiPyIgPyBzZWFyY2ggOiAiPyIgKyBzZWFyY2g7CiAgaWYgKGhhc2ggJiYgaGFzaCAhPT0gIiMiKSBwYXRobmFtZSArPSBoYXNoLmNoYXJBdCgwKSA9PT0gIiMiID8gaGFzaCA6ICIjIiArIGhhc2g7CiAgcmV0dXJuIHBhdGhuYW1lOwp9Ci8qKgogKiBQYXJzZXMgYSBzdHJpbmcgVVJMIHBhdGggaW50byBpdHMgc2VwYXJhdGUgcGF0aG5hbWUsIHNlYXJjaCwgYW5kIGhhc2ggY29tcG9uZW50cy4KICovCgpmdW5jdGlvbiBwYXJzZVBhdGgocGF0aCkgewogIGxldCBwYXJzZWRQYXRoID0ge307CgogIGlmIChwYXRoKSB7CiAgICBsZXQgaGFzaEluZGV4ID0gcGF0aC5pbmRleE9mKCIjIik7CgogICAgaWYgKGhhc2hJbmRleCA+PSAwKSB7CiAgICAgIHBhcnNlZFBhdGguaGFzaCA9IHBhdGguc3Vic3RyKGhhc2hJbmRleCk7CiAgICAgIHBhdGggPSBwYXRoLnN1YnN0cigwLCBoYXNoSW5kZXgpOwogICAgfQoKICAgIGxldCBzZWFyY2hJbmRleCA9IHBhdGguaW5kZXhPZigiPyIpOwoKICAgIGlmIChzZWFyY2hJbmRleCA+PSAwKSB7CiAgICAgIHBhcnNlZFBhdGguc2VhcmNoID0gcGF0aC5zdWJzdHIoc2VhcmNoSW5kZXgpOwogICAgICBwYXRoID0gcGF0aC5zdWJzdHIoMCwgc2VhcmNoSW5kZXgpOwogICAgfQoKICAgIGlmIChwYXRoKSB7CiAgICAgIHBhcnNlZFBhdGgucGF0aG5hbWUgPSBwYXRoOwogICAgfQogIH0KCiAgcmV0dXJuIHBhcnNlZFBhdGg7Cn0KZnVuY3Rpb24gY3JlYXRlVVJMKGxvY2F0aW9uKSB7CiAgLy8gd2luZG93LmxvY2F0aW9uLm9yaWdpbiBpcyAibnVsbCIgKHRoZSBsaXRlcmFsIHN0cmluZyB2YWx1ZSkgaW4gRmlyZWZveAogIC8vIHVuZGVyIGNlcnRhaW4gY29uZGl0aW9ucywgbm90YWJseSB3aGVuIHNlcnZpbmcgZnJvbSBhIGxvY2FsIEhUTUwgZmlsZQogIC8vIFNlZSBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD04NzgyOTcKICBsZXQgYmFzZSA9IHR5cGVvZiB3aW5kb3cgIT09ICJ1bmRlZmluZWQiICYmIHR5cGVvZiB3aW5kb3cubG9jYXRpb24gIT09ICJ1bmRlZmluZWQiICYmIHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4gIT09ICJudWxsIiA/IHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4gOiAidW5rbm93bjovL3Vua25vd24iOwogIGxldCBocmVmID0gdHlwZW9mIGxvY2F0aW9uID09PSAic3RyaW5nIiA/IGxvY2F0aW9uIDogcm91dGVyX2NyZWF0ZVBhdGgobG9jYXRpb24pOwogIHJldHVybiBuZXcgVVJMKGhyZWYsIGJhc2UpOwp9CgpmdW5jdGlvbiBnZXRVcmxCYXNlZEhpc3RvcnkoZ2V0TG9jYXRpb24sIGNyZWF0ZUhyZWYsIHZhbGlkYXRlTG9jYXRpb24sIG9wdGlvbnMpIHsKICBpZiAob3B0aW9ucyA9PT0gdm9pZCAwKSB7CiAgICBvcHRpb25zID0ge307CiAgfQoKICBsZXQgewogICAgd2luZG93ID0gZG9jdW1lbnQuZGVmYXVsdFZpZXcsCiAgICB2NUNvbXBhdCA9IGZhbHNlCiAgfSA9IG9wdGlvbnM7CiAgbGV0IGdsb2JhbEhpc3RvcnkgPSB3aW5kb3cuaGlzdG9yeTsKICBsZXQgYWN0aW9uID0gQWN0aW9uLlBvcDsKICBsZXQgbGlzdGVuZXIgPSBudWxsOwoKICBmdW5jdGlvbiBoYW5kbGVQb3AoKSB7CiAgICBhY3Rpb24gPSBBY3Rpb24uUG9wOwoKICAgIGlmIChsaXN0ZW5lcikgewogICAgICBsaXN0ZW5lcih7CiAgICAgICAgYWN0aW9uLAogICAgICAgIGxvY2F0aW9uOiBoaXN0b3J5LmxvY2F0aW9uCiAgICAgIH0pOwogICAgfQogIH0KCiAgZnVuY3Rpb24gcHVzaCh0bywgc3RhdGUpIHsKICAgIGFjdGlvbiA9IEFjdGlvbi5QdXNoOwogICAgbGV0IGxvY2F0aW9uID0gY3JlYXRlTG9jYXRpb24oaGlzdG9yeS5sb2NhdGlvbiwgdG8sIHN0YXRlKTsKICAgIGlmICh2YWxpZGF0ZUxvY2F0aW9uKSB2YWxpZGF0ZUxvY2F0aW9uKGxvY2F0aW9uLCB0byk7CiAgICBsZXQgaGlzdG9yeVN0YXRlID0gZ2V0SGlzdG9yeVN0YXRlKGxvY2F0aW9uKTsKICAgIGxldCB1cmwgPSBoaXN0b3J5LmNyZWF0ZUhyZWYobG9jYXRpb24pOyAvLyB0cnkuLi5jYXRjaCBiZWNhdXNlIGlPUyBsaW1pdHMgdXMgdG8gMTAwIHB1c2hTdGF0ZSBjYWxscyA6LwoKICAgIHRyeSB7CiAgICAgIGdsb2JhbEhpc3RvcnkucHVzaFN0YXRlKGhpc3RvcnlTdGF0ZSwgIiIsIHVybCk7CiAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAvLyBUaGV5IGFyZSBnb2luZyB0byBsb3NlIHN0YXRlIGhlcmUsIGJ1dCB0aGVyZSBpcyBubyByZWFsCiAgICAgIC8vIHdheSB0byB3YXJuIHRoZW0gYWJvdXQgaXQgc2luY2UgdGhlIHBhZ2Ugd2lsbCByZWZyZXNoLi4uCiAgICAgIHdpbmRvdy5sb2NhdGlvbi5hc3NpZ24odXJsKTsKICAgIH0KCiAgICBpZiAodjVDb21wYXQgJiYgbGlzdGVuZXIpIHsKICAgICAgbGlzdGVuZXIoewogICAgICAgIGFjdGlvbiwKICAgICAgICBsb2NhdGlvbjogaGlzdG9yeS5sb2NhdGlvbgogICAgICB9KTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIHJlcGxhY2UodG8sIHN0YXRlKSB7CiAgICBhY3Rpb24gPSBBY3Rpb24uUmVwbGFjZTsKICAgIGxldCBsb2NhdGlvbiA9IGNyZWF0ZUxvY2F0aW9uKGhpc3RvcnkubG9jYXRpb24sIHRvLCBzdGF0ZSk7CiAgICBpZiAodmFsaWRhdGVMb2NhdGlvbikgdmFsaWRhdGVMb2NhdGlvbihsb2NhdGlvbiwgdG8pOwogICAgbGV0IGhpc3RvcnlTdGF0ZSA9IGdldEhpc3RvcnlTdGF0ZShsb2NhdGlvbik7CiAgICBsZXQgdXJsID0gaGlzdG9yeS5jcmVhdGVIcmVmKGxvY2F0aW9uKTsKICAgIGdsb2JhbEhpc3RvcnkucmVwbGFjZVN0YXRlKGhpc3RvcnlTdGF0ZSwgIiIsIHVybCk7CgogICAgaWYgKHY1Q29tcGF0ICYmIGxpc3RlbmVyKSB7CiAgICAgIGxpc3RlbmVyKHsKICAgICAgICBhY3Rpb24sCiAgICAgICAgbG9jYXRpb246IGhpc3RvcnkubG9jYXRpb24KICAgICAgfSk7CiAgICB9CiAgfQoKICBsZXQgaGlzdG9yeSA9IHsKICAgIGdldCBhY3Rpb24oKSB7CiAgICAgIHJldHVybiBhY3Rpb247CiAgICB9LAoKICAgIGdldCBsb2NhdGlvbigpIHsKICAgICAgcmV0dXJuIGdldExvY2F0aW9uKHdpbmRvdywgZ2xvYmFsSGlzdG9yeSk7CiAgICB9LAoKICAgIGxpc3RlbihmbikgewogICAgICBpZiAobGlzdGVuZXIpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoIkEgaGlzdG9yeSBvbmx5IGFjY2VwdHMgb25lIGFjdGl2ZSBsaXN0ZW5lciIpOwogICAgICB9CgogICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihQb3BTdGF0ZUV2ZW50VHlwZSwgaGFuZGxlUG9wKTsKICAgICAgbGlzdGVuZXIgPSBmbjsKICAgICAgcmV0dXJuICgpID0+IHsKICAgICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcihQb3BTdGF0ZUV2ZW50VHlwZSwgaGFuZGxlUG9wKTsKICAgICAgICBsaXN0ZW5lciA9IG51bGw7CiAgICAgIH07CiAgICB9LAoKICAgIGNyZWF0ZUhyZWYodG8pIHsKICAgICAgcmV0dXJuIGNyZWF0ZUhyZWYod2luZG93LCB0byk7CiAgICB9LAoKICAgIGVuY29kZUxvY2F0aW9uKGxvY2F0aW9uKSB7CiAgICAgIC8vIEVuY29kZSBhIExvY2F0aW9uIHRoZSBzYW1lIHdheSB3aW5kb3cubG9jYXRpb24gd291bGQKICAgICAgbGV0IHVybCA9IGNyZWF0ZVVSTChyb3V0ZXJfY3JlYXRlUGF0aChsb2NhdGlvbikpOwogICAgICByZXR1cm4gX2V4dGVuZHMoe30sIGxvY2F0aW9uLCB7CiAgICAgICAgcGF0aG5hbWU6IHVybC5wYXRobmFtZSwKICAgICAgICBzZWFyY2g6IHVybC5zZWFyY2gsCiAgICAgICAgaGFzaDogdXJsLmhhc2gKICAgICAgfSk7CiAgICB9LAoKICAgIHB1c2gsCiAgICByZXBsYWNlLAoKICAgIGdvKG4pIHsKICAgICAgcmV0dXJuIGdsb2JhbEhpc3RvcnkuZ28obik7CiAgICB9CgogIH07CiAgcmV0dXJuIGhpc3Rvcnk7Cn0gLy8jZW5kcmVnaW9uCgp2YXIgUmVzdWx0VHlwZTsKCihmdW5jdGlvbiAoUmVzdWx0VHlwZSkgewogIFJlc3VsdFR5cGVbImRhdGEiXSA9ICJkYXRhIjsKICBSZXN1bHRUeXBlWyJkZWZlcnJlZCJdID0gImRlZmVycmVkIjsKICBSZXN1bHRUeXBlWyJyZWRpcmVjdCJdID0gInJlZGlyZWN0IjsKICBSZXN1bHRUeXBlWyJlcnJvciJdID0gImVycm9yIjsKfSkoUmVzdWx0VHlwZSB8fCAoUmVzdWx0VHlwZSA9IHt9KSk7CgpmdW5jdGlvbiBpc0luZGV4Um91dGUocm91dGUpIHsKICByZXR1cm4gcm91dGUuaW5kZXggPT09IHRydWU7Cn0gLy8gV2FsayB0aGUgcm91dGUgdHJlZSBnZW5lcmF0aW5nIHVuaXF1ZSBJRHMgd2hlcmUgbmVjZXNzYXJ5IHNvIHdlIGFyZSB3b3JraW5nCi8vIHNvbGVseSB3aXRoIEFnbm9zdGljRGF0YVJvdXRlT2JqZWN0J3Mgd2l0aGluIHRoZSBSb3V0ZXIKCgpmdW5jdGlvbiBjb252ZXJ0Um91dGVzVG9EYXRhUm91dGVzKHJvdXRlcywgcGFyZW50UGF0aCwgYWxsSWRzKSB7CiAgaWYgKHBhcmVudFBhdGggPT09IHZvaWQgMCkgewogICAgcGFyZW50UGF0aCA9IFtdOwogIH0KCiAgaWYgKGFsbElkcyA9PT0gdm9pZCAwKSB7CiAgICBhbGxJZHMgPSBuZXcgU2V0KCk7CiAgfQoKICByZXR1cm4gcm91dGVzLm1hcCgocm91dGUsIGluZGV4KSA9PiB7CiAgICBsZXQgdHJlZVBhdGggPSBbLi4ucGFyZW50UGF0aCwgaW5kZXhdOwogICAgbGV0IGlkID0gdHlwZW9mIHJvdXRlLmlkID09PSAic3RyaW5nIiA/IHJvdXRlLmlkIDogdHJlZVBhdGguam9pbigiLSIpOwogICAgcm91dGVyX2ludmFyaWFudChyb3V0ZS5pbmRleCAhPT0gdHJ1ZSB8fCAhcm91dGUuY2hpbGRyZW4sICJDYW5ub3Qgc3BlY2lmeSBjaGlsZHJlbiBvbiBhbiBpbmRleCByb3V0ZSIpOwogICAgcm91dGVyX2ludmFyaWFudCghYWxsSWRzLmhhcyhpZCksICJGb3VuZCBhIHJvdXRlIGlkIGNvbGxpc2lvbiBvbiBpZCBcIiIgKyBpZCArICJcIi4gIFJvdXRlICIgKyAiaWQncyBtdXN0IGJlIGdsb2JhbGx5IHVuaXF1ZSB3aXRoaW4gRGF0YSBSb3V0ZXIgdXNhZ2VzIik7CiAgICBhbGxJZHMuYWRkKGlkKTsKCiAgICBpZiAoaXNJbmRleFJvdXRlKHJvdXRlKSkgewogICAgICBsZXQgaW5kZXhSb3V0ZSA9IF9leHRlbmRzKHt9LCByb3V0ZSwgewogICAgICAgIGlkCiAgICAgIH0pOwoKICAgICAgcmV0dXJuIGluZGV4Um91dGU7CiAgICB9IGVsc2UgewogICAgICBsZXQgcGF0aE9yTGF5b3V0Um91dGUgPSBfZXh0ZW5kcyh7fSwgcm91dGUsIHsKICAgICAgICBpZCwKICAgICAgICBjaGlsZHJlbjogcm91dGUuY2hpbGRyZW4gPyBjb252ZXJ0Um91dGVzVG9EYXRhUm91dGVzKHJvdXRlLmNoaWxkcmVuLCB0cmVlUGF0aCwgYWxsSWRzKSA6IHVuZGVmaW5lZAogICAgICB9KTsKCiAgICAgIHJldHVybiBwYXRoT3JMYXlvdXRSb3V0ZTsKICAgIH0KICB9KTsKfQovKioKICogTWF0Y2hlcyB0aGUgZ2l2ZW4gcm91dGVzIHRvIGEgbG9jYXRpb24gYW5kIHJldHVybnMgdGhlIG1hdGNoIGRhdGEuCiAqCiAqIEBzZWUgaHR0cHM6Ly9yZWFjdHJvdXRlci5jb20vZG9jcy9lbi92Ni91dGlscy9tYXRjaC1yb3V0ZXMKICovCgpmdW5jdGlvbiBtYXRjaFJvdXRlcyhyb3V0ZXMsIGxvY2F0aW9uQXJnLCBiYXNlbmFtZSkgewogIGlmIChiYXNlbmFtZSA9PT0gdm9pZCAwKSB7CiAgICBiYXNlbmFtZSA9ICIvIjsKICB9CgogIGxldCBsb2NhdGlvbiA9IHR5cGVvZiBsb2NhdGlvbkFyZyA9PT0gInN0cmluZyIgPyBwYXJzZVBhdGgobG9jYXRpb25BcmcpIDogbG9jYXRpb25Bcmc7CiAgbGV0IHBhdGhuYW1lID0gc3RyaXBCYXNlbmFtZShsb2NhdGlvbi5wYXRobmFtZSB8fCAiLyIsIGJhc2VuYW1lKTsKCiAgaWYgKHBhdGhuYW1lID09IG51bGwpIHsKICAgIHJldHVybiBudWxsOwogIH0KCiAgbGV0IGJyYW5jaGVzID0gZmxhdHRlblJvdXRlcyhyb3V0ZXMpOwogIHJhbmtSb3V0ZUJyYW5jaGVzKGJyYW5jaGVzKTsKICBsZXQgbWF0Y2hlcyA9IG51bGw7CgogIGZvciAobGV0IGkgPSAwOyBtYXRjaGVzID09IG51bGwgJiYgaSA8IGJyYW5jaGVzLmxlbmd0aDsgKytpKSB7CiAgICBtYXRjaGVzID0gbWF0Y2hSb3V0ZUJyYW5jaChicmFuY2hlc1tpXSwgLy8gSW5jb21pbmcgcGF0aG5hbWVzIGFyZSBnZW5lcmFsbHkgZW5jb2RlZCBmcm9tIGVpdGhlciB3aW5kb3cubG9jYXRpb24KICAgIC8vIG9yIGZyb20gcm91dGVyLm5hdmlnYXRlLCBidXQgd2Ugd2FudCB0byBtYXRjaCBhZ2FpbnN0IHRoZSB1bmVuY29kZWQKICAgIC8vIHBhdGhzIGluIHRoZSByb3V0ZSBkZWZpbml0aW9ucy4gIE1lbW9yeSByb3V0ZXIgbG9jYXRpb25zIHdvbid0IGJlCiAgICAvLyBlbmNvZGVkIGhlcmUgYnV0IHRoZXJlIGFsc28gc2hvdWxkbid0IGJlIGFueXRoaW5nIHRvIGRlY29kZSBzbyB0aGlzCiAgICAvLyBzaG91bGQgYmUgYSBzYWZlIG9wZXJhdGlvbi4gIFRoaXMgYXZvaWRzIG5lZWRpbmcgbWF0Y2hSb3V0ZXMgdG8gYmUKICAgIC8vIGhpc3RvcnktYXdhcmUuCiAgICBzYWZlbHlEZWNvZGVVUkkocGF0aG5hbWUpKTsKICB9CgogIHJldHVybiBtYXRjaGVzOwp9CgpmdW5jdGlvbiBmbGF0dGVuUm91dGVzKHJvdXRlcywgYnJhbmNoZXMsIHBhcmVudHNNZXRhLCBwYXJlbnRQYXRoKSB7CiAgaWYgKGJyYW5jaGVzID09PSB2b2lkIDApIHsKICAgIGJyYW5jaGVzID0gW107CiAgfQoKICBpZiAocGFyZW50c01ldGEgPT09IHZvaWQgMCkgewogICAgcGFyZW50c01ldGEgPSBbXTsKICB9CgogIGlmIChwYXJlbnRQYXRoID09PSB2b2lkIDApIHsKICAgIHBhcmVudFBhdGggPSAiIjsKICB9CgogIHJvdXRlcy5mb3JFYWNoKChyb3V0ZSwgaW5kZXgpID0+IHsKICAgIGxldCBtZXRhID0gewogICAgICByZWxhdGl2ZVBhdGg6IHJvdXRlLnBhdGggfHwgIiIsCiAgICAgIGNhc2VTZW5zaXRpdmU6IHJvdXRlLmNhc2VTZW5zaXRpdmUgPT09IHRydWUsCiAgICAgIGNoaWxkcmVuSW5kZXg6IGluZGV4LAogICAgICByb3V0ZQogICAgfTsKCiAgICBpZiAobWV0YS5yZWxhdGl2ZVBhdGguc3RhcnRzV2l0aCgiLyIpKSB7CiAgICAgIHJvdXRlcl9pbnZhcmlhbnQobWV0YS5yZWxhdGl2ZVBhdGguc3RhcnRzV2l0aChwYXJlbnRQYXRoKSwgIkFic29sdXRlIHJvdXRlIHBhdGggXCIiICsgbWV0YS5yZWxhdGl2ZVBhdGggKyAiXCIgbmVzdGVkIHVuZGVyIHBhdGggIiArICgiXCIiICsgcGFyZW50UGF0aCArICJcIiBpcyBub3QgdmFsaWQuIEFuIGFic29sdXRlIGNoaWxkIHJvdXRlIHBhdGggIikgKyAibXVzdCBzdGFydCB3aXRoIHRoZSBjb21iaW5lZCBwYXRoIG9mIGFsbCBpdHMgcGFyZW50IHJvdXRlcy4iKTsKICAgICAgbWV0YS5yZWxhdGl2ZVBhdGggPSBtZXRhLnJlbGF0aXZlUGF0aC5zbGljZShwYXJlbnRQYXRoLmxlbmd0aCk7CiAgICB9CgogICAgbGV0IHBhdGggPSByb3V0ZXJfam9pblBhdGhzKFtwYXJlbnRQYXRoLCBtZXRhLnJlbGF0aXZlUGF0aF0pOwogICAgbGV0IHJvdXRlc01ldGEgPSBwYXJlbnRzTWV0YS5jb25jYXQobWV0YSk7IC8vIEFkZCB0aGUgY2hpbGRyZW4gYmVmb3JlIGFkZGluZyB0aGlzIHJvdXRlIHRvIHRoZSBhcnJheSBzbyB3ZSB0cmF2ZXJzZSB0aGUKICAgIC8vIHJvdXRlIHRyZWUgZGVwdGgtZmlyc3QgYW5kIGNoaWxkIHJvdXRlcyBhcHBlYXIgYmVmb3JlIHRoZWlyIHBhcmVudHMgaW4KICAgIC8vIHRoZSAiZmxhdHRlbmVkIiB2ZXJzaW9uLgoKICAgIGlmIChyb3V0ZS5jaGlsZHJlbiAmJiByb3V0ZS5jaGlsZHJlbi5sZW5ndGggPiAwKSB7CiAgICAgIHJvdXRlcl9pbnZhcmlhbnQoIC8vIE91ciB0eXBlcyBrbm93IGJldHRlciwgYnV0IHJ1bnRpbWUgSlMgbWF5IG5vdCEKICAgICAgLy8gQHRzLWV4cGVjdC1lcnJvcgogICAgICByb3V0ZS5pbmRleCAhPT0gdHJ1ZSwgIkluZGV4IHJvdXRlcyBtdXN0IG5vdCBoYXZlIGNoaWxkIHJvdXRlcy4gUGxlYXNlIHJlbW92ZSAiICsgKCJhbGwgY2hpbGQgcm91dGVzIGZyb20gcm91dGUgcGF0aCBcIiIgKyBwYXRoICsgIlwiLiIpKTsKICAgICAgZmxhdHRlblJvdXRlcyhyb3V0ZS5jaGlsZHJlbiwgYnJhbmNoZXMsIHJvdXRlc01ldGEsIHBhdGgpOwogICAgfSAvLyBSb3V0ZXMgd2l0aG91dCBhIHBhdGggc2hvdWxkbid0IGV2ZXIgbWF0Y2ggYnkgdGhlbXNlbHZlcyB1bmxlc3MgdGhleSBhcmUKICAgIC8vIGluZGV4IHJvdXRlcywgc28gZG9uJ3QgYWRkIHRoZW0gdG8gdGhlIGxpc3Qgb2YgcG9zc2libGUgYnJhbmNoZXMuCgoKICAgIGlmIChyb3V0ZS5wYXRoID09IG51bGwgJiYgIXJvdXRlLmluZGV4KSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBicmFuY2hlcy5wdXNoKHsKICAgICAgcGF0aCwKICAgICAgc2NvcmU6IGNvbXB1dGVTY29yZShwYXRoLCByb3V0ZS5pbmRleCksCiAgICAgIHJvdXRlc01ldGEKICAgIH0pOwogIH0pOwogIHJldHVybiBicmFuY2hlczsKfQoKZnVuY3Rpb24gcmFua1JvdXRlQnJhbmNoZXMoYnJhbmNoZXMpIHsKICBicmFuY2hlcy5zb3J0KChhLCBiKSA9PiBhLnNjb3JlICE9PSBiLnNjb3JlID8gYi5zY29yZSAtIGEuc2NvcmUgLy8gSGlnaGVyIHNjb3JlIGZpcnN0CiAgOiBjb21wYXJlSW5kZXhlcyhhLnJvdXRlc01ldGEubWFwKG1ldGEgPT4gbWV0YS5jaGlsZHJlbkluZGV4KSwgYi5yb3V0ZXNNZXRhLm1hcChtZXRhID0+IG1ldGEuY2hpbGRyZW5JbmRleCkpKTsKfQoKY29uc3QgcGFyYW1SZSA9IC9eOlx3KyQvOwpjb25zdCBkeW5hbWljU2VnbWVudFZhbHVlID0gMzsKY29uc3QgaW5kZXhSb3V0ZVZhbHVlID0gMjsKY29uc3QgZW1wdHlTZWdtZW50VmFsdWUgPSAxOwpjb25zdCBzdGF0aWNTZWdtZW50VmFsdWUgPSAxMDsKY29uc3Qgc3BsYXRQZW5hbHR5ID0gLTI7Cgpjb25zdCBpc1NwbGF0ID0gcyA9PiBzID09PSAiKiI7CgpmdW5jdGlvbiBjb21wdXRlU2NvcmUocGF0aCwgaW5kZXgpIHsKICBsZXQgc2VnbWVudHMgPSBwYXRoLnNwbGl0KCIvIik7CiAgbGV0IGluaXRpYWxTY29yZSA9IHNlZ21lbnRzLmxlbmd0aDsKCiAgaWYgKHNlZ21lbnRzLnNvbWUoaXNTcGxhdCkpIHsKICAgIGluaXRpYWxTY29yZSArPSBzcGxhdFBlbmFsdHk7CiAgfQoKICBpZiAoaW5kZXgpIHsKICAgIGluaXRpYWxTY29yZSArPSBpbmRleFJvdXRlVmFsdWU7CiAgfQoKICByZXR1cm4gc2VnbWVudHMuZmlsdGVyKHMgPT4gIWlzU3BsYXQocykpLnJlZHVjZSgoc2NvcmUsIHNlZ21lbnQpID0+IHNjb3JlICsgKHBhcmFtUmUudGVzdChzZWdtZW50KSA/IGR5bmFtaWNTZWdtZW50VmFsdWUgOiBzZWdtZW50ID09PSAiIiA/IGVtcHR5U2VnbWVudFZhbHVlIDogc3RhdGljU2VnbWVudFZhbHVlKSwgaW5pdGlhbFNjb3JlKTsKfQoKZnVuY3Rpb24gY29tcGFyZUluZGV4ZXMoYSwgYikgewogIGxldCBzaWJsaW5ncyA9IGEubGVuZ3RoID09PSBiLmxlbmd0aCAmJiBhLnNsaWNlKDAsIC0xKS5ldmVyeSgobiwgaSkgPT4gbiA9PT0gYltpXSk7CiAgcmV0dXJuIHNpYmxpbmdzID8gLy8gSWYgdHdvIHJvdXRlcyBhcmUgc2libGluZ3MsIHdlIHNob3VsZCB0cnkgdG8gbWF0Y2ggdGhlIGVhcmxpZXIgc2libGluZwogIC8vIGZpcnN0LiBUaGlzIGFsbG93cyBwZW9wbGUgdG8gaGF2ZSBmaW5lLWdyYWluZWQgY29udHJvbCBvdmVyIHRoZSBtYXRjaGluZwogIC8vIGJlaGF2aW9yIGJ5IHNpbXBseSBwdXR0aW5nIHJvdXRlcyB3aXRoIGlkZW50aWNhbCBwYXRocyBpbiB0aGUgb3JkZXIgdGhleQogIC8vIHdhbnQgdGhlbSB0cmllZC4KICBhW2EubGVuZ3RoIC0gMV0gLSBiW2IubGVuZ3RoIC0gMV0gOiAvLyBPdGhlcndpc2UsIGl0IGRvZXNuJ3QgcmVhbGx5IG1ha2Ugc2Vuc2UgdG8gcmFuayBub24tc2libGluZ3MgYnkgaW5kZXgsCiAgLy8gc28gdGhleSBzb3J0IGVxdWFsbHkuCiAgMDsKfQoKZnVuY3Rpb24gbWF0Y2hSb3V0ZUJyYW5jaChicmFuY2gsIHBhdGhuYW1lKSB7CiAgbGV0IHsKICAgIHJvdXRlc01ldGEKICB9ID0gYnJhbmNoOwogIGxldCBtYXRjaGVkUGFyYW1zID0ge307CiAgbGV0IG1hdGNoZWRQYXRobmFtZSA9ICIvIjsKICBsZXQgbWF0Y2hlcyA9IFtdOwoKICBmb3IgKGxldCBpID0gMDsgaSA8IHJvdXRlc01ldGEubGVuZ3RoOyArK2kpIHsKICAgIGxldCBtZXRhID0gcm91dGVzTWV0YVtpXTsKICAgIGxldCBlbmQgPSBpID09PSByb3V0ZXNNZXRhLmxlbmd0aCAtIDE7CiAgICBsZXQgcmVtYWluaW5nUGF0aG5hbWUgPSBtYXRjaGVkUGF0aG5hbWUgPT09ICIvIiA/IHBhdGhuYW1lIDogcGF0aG5hbWUuc2xpY2UobWF0Y2hlZFBhdGhuYW1lLmxlbmd0aCkgfHwgIi8iOwogICAgbGV0IG1hdGNoID0gcm91dGVyX21hdGNoUGF0aCh7CiAgICAgIHBhdGg6IG1ldGEucmVsYXRpdmVQYXRoLAogICAgICBjYXNlU2Vuc2l0aXZlOiBtZXRhLmNhc2VTZW5zaXRpdmUsCiAgICAgIGVuZAogICAgfSwgcmVtYWluaW5nUGF0aG5hbWUpOwogICAgaWYgKCFtYXRjaCkgcmV0dXJuIG51bGw7CiAgICBPYmplY3QuYXNzaWduKG1hdGNoZWRQYXJhbXMsIG1hdGNoLnBhcmFtcyk7CiAgICBsZXQgcm91dGUgPSBtZXRhLnJvdXRlOwogICAgbWF0Y2hlcy5wdXNoKHsKICAgICAgLy8gVE9ETzogQ2FuIHRoaXMgYXMgYmUgYXZvaWRlZD8KICAgICAgcGFyYW1zOiBtYXRjaGVkUGFyYW1zLAogICAgICBwYXRobmFtZTogcm91dGVyX2pvaW5QYXRocyhbbWF0Y2hlZFBhdGhuYW1lLCBtYXRjaC5wYXRobmFtZV0pLAogICAgICBwYXRobmFtZUJhc2U6IG5vcm1hbGl6ZVBhdGhuYW1lKHJvdXRlcl9qb2luUGF0aHMoW21hdGNoZWRQYXRobmFtZSwgbWF0Y2gucGF0aG5hbWVCYXNlXSkpLAogICAgICByb3V0ZQogICAgfSk7CgogICAgaWYgKG1hdGNoLnBhdGhuYW1lQmFzZSAhPT0gIi8iKSB7CiAgICAgIG1hdGNoZWRQYXRobmFtZSA9IHJvdXRlcl9qb2luUGF0aHMoW21hdGNoZWRQYXRobmFtZSwgbWF0Y2gucGF0aG5hbWVCYXNlXSk7CiAgICB9CiAgfQoKICByZXR1cm4gbWF0Y2hlczsKfQovKioKICogUmV0dXJucyBhIHBhdGggd2l0aCBwYXJhbXMgaW50ZXJwb2xhdGVkLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvdXRpbHMvZ2VuZXJhdGUtcGF0aAogKi8KCgpmdW5jdGlvbiBnZW5lcmF0ZVBhdGgocGF0aCwgcGFyYW1zKSB7CiAgaWYgKHBhcmFtcyA9PT0gdm9pZCAwKSB7CiAgICBwYXJhbXMgPSB7fTsKICB9CgogIHJldHVybiBwYXRoLnJlcGxhY2UoLzooXHcrKS9nLCAoXywga2V5KSA9PiB7CiAgICByb3V0ZXJfaW52YXJpYW50KHBhcmFtc1trZXldICE9IG51bGwsICJNaXNzaW5nIFwiOiIgKyBrZXkgKyAiXCIgcGFyYW0iKTsKICAgIHJldHVybiBwYXJhbXNba2V5XTsKICB9KS5yZXBsYWNlKC8oXC8/KVwqLywgKF8sIHByZWZpeCwgX18sIHN0cikgPT4gewogICAgY29uc3Qgc3RhciA9ICIqIjsKCiAgICBpZiAocGFyYW1zW3N0YXJdID09IG51bGwpIHsKICAgICAgLy8gSWYgbm8gc3BsYXQgd2FzIHByb3ZpZGVkLCB0cmltIHRoZSB0cmFpbGluZyBzbGFzaCBfdW5sZXNzXyBpdCdzCiAgICAgIC8vIHRoZSBlbnRpcmUgcGF0aAogICAgICByZXR1cm4gc3RyID09PSAiLyoiID8gIi8iIDogIiI7CiAgICB9IC8vIEFwcGx5IHRoZSBzcGxhdAoKCiAgICByZXR1cm4gIiIgKyBwcmVmaXggKyBwYXJhbXNbc3Rhcl07CiAgfSk7Cn0KLyoqCiAqIFBlcmZvcm1zIHBhdHRlcm4gbWF0Y2hpbmcgb24gYSBVUkwgcGF0aG5hbWUgYW5kIHJldHVybnMgaW5mb3JtYXRpb24gYWJvdXQKICogdGhlIG1hdGNoLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvdXRpbHMvbWF0Y2gtcGF0aAogKi8KCmZ1bmN0aW9uIHJvdXRlcl9tYXRjaFBhdGgocGF0dGVybiwgcGF0aG5hbWUpIHsKICBpZiAodHlwZW9mIHBhdHRlcm4gPT09ICJzdHJpbmciKSB7CiAgICBwYXR0ZXJuID0gewogICAgICBwYXRoOiBwYXR0ZXJuLAogICAgICBjYXNlU2Vuc2l0aXZlOiBmYWxzZSwKICAgICAgZW5kOiB0cnVlCiAgICB9OwogIH0KCiAgbGV0IFttYXRjaGVyLCBwYXJhbU5hbWVzXSA9IGNvbXBpbGVQYXRoKHBhdHRlcm4ucGF0aCwgcGF0dGVybi5jYXNlU2Vuc2l0aXZlLCBwYXR0ZXJuLmVuZCk7CiAgbGV0IG1hdGNoID0gcGF0aG5hbWUubWF0Y2gobWF0Y2hlcik7CiAgaWYgKCFtYXRjaCkgcmV0dXJuIG51bGw7CiAgbGV0IG1hdGNoZWRQYXRobmFtZSA9IG1hdGNoWzBdOwogIGxldCBwYXRobmFtZUJhc2UgPSBtYXRjaGVkUGF0aG5hbWUucmVwbGFjZSgvKC4pXC8rJC8sICIkMSIpOwogIGxldCBjYXB0dXJlR3JvdXBzID0gbWF0Y2guc2xpY2UoMSk7CiAgbGV0IHBhcmFtcyA9IHBhcmFtTmFtZXMucmVkdWNlKChtZW1vLCBwYXJhbU5hbWUsIGluZGV4KSA9PiB7CiAgICAvLyBXZSBuZWVkIHRvIGNvbXB1dGUgdGhlIHBhdGhuYW1lQmFzZSBoZXJlIHVzaW5nIHRoZSByYXcgc3BsYXQgdmFsdWUKICAgIC8vIGluc3RlYWQgb2YgdXNpbmcgcGFyYW1zWyIqIl0gbGF0ZXIgYmVjYXVzZSBpdCB3aWxsIGJlIGRlY29kZWQgdGhlbgogICAgaWYgKHBhcmFtTmFtZSA9PT0gIioiKSB7CiAgICAgIGxldCBzcGxhdFZhbHVlID0gY2FwdHVyZUdyb3Vwc1tpbmRleF0gfHwgIiI7CiAgICAgIHBhdGhuYW1lQmFzZSA9IG1hdGNoZWRQYXRobmFtZS5zbGljZSgwLCBtYXRjaGVkUGF0aG5hbWUubGVuZ3RoIC0gc3BsYXRWYWx1ZS5sZW5ndGgpLnJlcGxhY2UoLyguKVwvKyQvLCAiJDEiKTsKICAgIH0KCiAgICBtZW1vW3BhcmFtTmFtZV0gPSBzYWZlbHlEZWNvZGVVUklDb21wb25lbnQoY2FwdHVyZUdyb3Vwc1tpbmRleF0gfHwgIiIsIHBhcmFtTmFtZSk7CiAgICByZXR1cm4gbWVtbzsKICB9LCB7fSk7CiAgcmV0dXJuIHsKICAgIHBhcmFtcywKICAgIHBhdGhuYW1lOiBtYXRjaGVkUGF0aG5hbWUsCiAgICBwYXRobmFtZUJhc2UsCiAgICBwYXR0ZXJuCiAgfTsKfQoKZnVuY3Rpb24gY29tcGlsZVBhdGgocGF0aCwgY2FzZVNlbnNpdGl2ZSwgZW5kKSB7CiAgaWYgKGNhc2VTZW5zaXRpdmUgPT09IHZvaWQgMCkgewogICAgY2FzZVNlbnNpdGl2ZSA9IGZhbHNlOwogIH0KCiAgaWYgKGVuZCA9PT0gdm9pZCAwKSB7CiAgICBlbmQgPSB0cnVlOwogIH0KCiAgd2FybmluZyhwYXRoID09PSAiKiIgfHwgIXBhdGguZW5kc1dpdGgoIioiKSB8fCBwYXRoLmVuZHNXaXRoKCIvKiIpLCAiUm91dGUgcGF0aCBcIiIgKyBwYXRoICsgIlwiIHdpbGwgYmUgdHJlYXRlZCBhcyBpZiBpdCB3ZXJlICIgKyAoIlwiIiArIHBhdGgucmVwbGFjZSgvXCokLywgIi8qIikgKyAiXCIgYmVjYXVzZSB0aGUgYCpgIGNoYXJhY3RlciBtdXN0ICIpICsgImFsd2F5cyBmb2xsb3cgYSBgL2AgaW4gdGhlIHBhdHRlcm4uIFRvIGdldCByaWQgb2YgdGhpcyB3YXJuaW5nLCAiICsgKCJwbGVhc2UgY2hhbmdlIHRoZSByb3V0ZSBwYXRoIHRvIFwiIiArIHBhdGgucmVwbGFjZSgvXCokLywgIi8qIikgKyAiXCIuIikpOwogIGxldCBwYXJhbU5hbWVzID0gW107CiAgbGV0IHJlZ2V4cFNvdXJjZSA9ICJeIiArIHBhdGgucmVwbGFjZSgvXC8qXCo/JC8sICIiKSAvLyBJZ25vcmUgdHJhaWxpbmcgLyBhbmQgLyosIHdlJ2xsIGhhbmRsZSBpdCBiZWxvdwogIC5yZXBsYWNlKC9eXC8qLywgIi8iKSAvLyBNYWtlIHN1cmUgaXQgaGFzIGEgbGVhZGluZyAvCiAgLnJlcGxhY2UoL1tcXC4qK14kP3t9fCgpW1xdXS9nLCAiXFwkJiIpIC8vIEVzY2FwZSBzcGVjaWFsIHJlZ2V4IGNoYXJzCiAgLnJlcGxhY2UoLzooXHcrKS9nLCAoXywgcGFyYW1OYW1lKSA9PiB7CiAgICBwYXJhbU5hbWVzLnB1c2gocGFyYW1OYW1lKTsKICAgIHJldHVybiAiKFteXFwvXSspIjsKICB9KTsKCiAgaWYgKHBhdGguZW5kc1dpdGgoIioiKSkgewogICAgcGFyYW1OYW1lcy5wdXNoKCIqIik7CiAgICByZWdleHBTb3VyY2UgKz0gcGF0aCA9PT0gIioiIHx8IHBhdGggPT09ICIvKiIgPyAiKC4qKSQiIC8vIEFscmVhZHkgbWF0Y2hlZCB0aGUgaW5pdGlhbCAvLCBqdXN0IG1hdGNoIHRoZSByZXN0CiAgICA6ICIoPzpcXC8oLispfFxcLyopJCI7IC8vIERvbid0IGluY2x1ZGUgdGhlIC8gaW4gcGFyYW1zWyIqIl0KICB9IGVsc2UgaWYgKGVuZCkgewogICAgLy8gV2hlbiBtYXRjaGluZyB0byB0aGUgZW5kLCBpZ25vcmUgdHJhaWxpbmcgc2xhc2hlcwogICAgcmVnZXhwU291cmNlICs9ICJcXC8qJCI7CiAgfSBlbHNlIGlmIChwYXRoICE9PSAiIiAmJiBwYXRoICE9PSAiLyIpIHsKICAgIC8vIElmIG91ciBwYXRoIGlzIG5vbi1lbXB0eSBhbmQgY29udGFpbnMgYW55dGhpbmcgYmV5b25kIGFuIGluaXRpYWwgc2xhc2gsCiAgICAvLyB0aGVuIHdlIGhhdmUgX3NvbWVfIGZvcm0gb2YgcGF0aCBpbiBvdXIgcmVnZXggc28gd2Ugc2hvdWxkIGV4cGVjdCB0bwogICAgLy8gbWF0Y2ggb25seSBpZiB3ZSBmaW5kIHRoZSBlbmQgb2YgdGhpcyBwYXRoIHNlZ21lbnQuICBMb29rIGZvciBhbiBvcHRpb25hbAogICAgLy8gbm9uLWNhcHR1cmVkIHRyYWlsaW5nIHNsYXNoICh0byBtYXRjaCBhIHBvcnRpb24gb2YgdGhlIFVSTCkgb3IgdGhlIGVuZAogICAgLy8gb2YgdGhlIHBhdGggKGlmIHdlJ3ZlIG1hdGNoZWQgdG8gdGhlIGVuZCkuICBXZSB1c2VkIHRvIGRvIHRoaXMgd2l0aCBhCiAgICAvLyB3b3JkIGJvdW5kYXJ5IGJ1dCB0aGF0IGdpdmVzIGZhbHNlIHBvc2l0aXZlcyBvbiByb3V0ZXMgbGlrZQogICAgLy8gL3VzZXItcHJlZmVyZW5jZXMgc2luY2UgYC1gIGNvdW50cyBhcyBhIHdvcmQgYm91bmRhcnkuCiAgICByZWdleHBTb3VyY2UgKz0gIig/Oig/PVxcL3wkKSkiOwogIH0gZWxzZSA7CgogIGxldCBtYXRjaGVyID0gbmV3IFJlZ0V4cChyZWdleHBTb3VyY2UsIGNhc2VTZW5zaXRpdmUgPyB1bmRlZmluZWQgOiAiaSIpOwogIHJldHVybiBbbWF0Y2hlciwgcGFyYW1OYW1lc107Cn0KCmZ1bmN0aW9uIHNhZmVseURlY29kZVVSSSh2YWx1ZSkgewogIHRyeSB7CiAgICByZXR1cm4gZGVjb2RlVVJJKHZhbHVlKTsKICB9IGNhdGNoIChlcnJvcikgewogICAgd2FybmluZyhmYWxzZSwgIlRoZSBVUkwgcGF0aCBcIiIgKyB2YWx1ZSArICJcIiBjb3VsZCBub3QgYmUgZGVjb2RlZCBiZWNhdXNlIGl0IGlzIGlzIGEgIiArICJtYWxmb3JtZWQgVVJMIHNlZ21lbnQuIFRoaXMgaXMgcHJvYmFibHkgZHVlIHRvIGEgYmFkIHBlcmNlbnQgIiArICgiZW5jb2RpbmcgKCIgKyBlcnJvciArICIpLiIpKTsKICAgIHJldHVybiB2YWx1ZTsKICB9Cn0KCmZ1bmN0aW9uIHNhZmVseURlY29kZVVSSUNvbXBvbmVudCh2YWx1ZSwgcGFyYW1OYW1lKSB7CiAgdHJ5IHsKICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQodmFsdWUpOwogIH0gY2F0Y2ggKGVycm9yKSB7CiAgICB3YXJuaW5nKGZhbHNlLCAiVGhlIHZhbHVlIGZvciB0aGUgVVJMIHBhcmFtIFwiIiArIHBhcmFtTmFtZSArICJcIiB3aWxsIG5vdCBiZSBkZWNvZGVkIGJlY2F1c2UiICsgKCIgdGhlIHN0cmluZyBcIiIgKyB2YWx1ZSArICJcIiBpcyBhIG1hbGZvcm1lZCBVUkwgc2VnbWVudC4gVGhpcyBpcyBwcm9iYWJseSIpICsgKCIgZHVlIHRvIGEgYmFkIHBlcmNlbnQgZW5jb2RpbmcgKCIgKyBlcnJvciArICIpLiIpKTsKICAgIHJldHVybiB2YWx1ZTsKICB9Cn0KLyoqCiAqIEBwcml2YXRlCiAqLwoKCmZ1bmN0aW9uIHN0cmlwQmFzZW5hbWUocGF0aG5hbWUsIGJhc2VuYW1lKSB7CiAgaWYgKGJhc2VuYW1lID09PSAiLyIpIHJldHVybiBwYXRobmFtZTsKCiAgaWYgKCFwYXRobmFtZS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoYmFzZW5hbWUudG9Mb3dlckNhc2UoKSkpIHsKICAgIHJldHVybiBudWxsOwogIH0gLy8gV2Ugd2FudCB0byBsZWF2ZSB0cmFpbGluZyBzbGFzaCBiZWhhdmlvciBpbiB0aGUgdXNlcidzIGNvbnRyb2wsIHNvIGlmIHRoZXkKICAvLyBzcGVjaWZ5IGEgYmFzZW5hbWUgd2l0aCBhIHRyYWlsaW5nIHNsYXNoLCB3ZSBzaG91bGQgc3VwcG9ydCBpdAoKCiAgbGV0IHN0YXJ0SW5kZXggPSBiYXNlbmFtZS5lbmRzV2l0aCgiLyIpID8gYmFzZW5hbWUubGVuZ3RoIC0gMSA6IGJhc2VuYW1lLmxlbmd0aDsKICBsZXQgbmV4dENoYXIgPSBwYXRobmFtZS5jaGFyQXQoc3RhcnRJbmRleCk7CgogIGlmIChuZXh0Q2hhciAmJiBuZXh0Q2hhciAhPT0gIi8iKSB7CiAgICAvLyBwYXRobmFtZSBkb2VzIG5vdCBzdGFydCB3aXRoIGJhc2VuYW1lLwogICAgcmV0dXJuIG51bGw7CiAgfQoKICByZXR1cm4gcGF0aG5hbWUuc2xpY2Uoc3RhcnRJbmRleCkgfHwgIi8iOwp9CmZ1bmN0aW9uIHJvdXRlcl9pbnZhcmlhbnQodmFsdWUsIG1lc3NhZ2UpIHsKICBpZiAodmFsdWUgPT09IGZhbHNlIHx8IHZhbHVlID09PSBudWxsIHx8IHR5cGVvZiB2YWx1ZSA9PT0gInVuZGVmaW5lZCIpIHsKICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTsKICB9Cn0KLyoqCiAqIEBwcml2YXRlCiAqLwoKZnVuY3Rpb24gd2FybmluZyhjb25kLCBtZXNzYWdlKSB7CiAgaWYgKCFjb25kKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZQogICAgaWYgKHR5cGVvZiBjb25zb2xlICE9PSAidW5kZWZpbmVkIikgY29uc29sZS53YXJuKG1lc3NhZ2UpOwoKICAgIHRyeSB7CiAgICAgIC8vIFdlbGNvbWUgdG8gZGVidWdnaW5nIFJlYWN0IFJvdXRlciEKICAgICAgLy8KICAgICAgLy8gVGhpcyBlcnJvciBpcyB0aHJvd24gYXMgYSBjb252ZW5pZW5jZSBzbyB5b3UgY2FuIG1vcmUgZWFzaWx5CiAgICAgIC8vIGZpbmQgdGhlIHNvdXJjZSBmb3IgYSB3YXJuaW5nIHRoYXQgYXBwZWFycyBpbiB0aGUgY29uc29sZSBieQogICAgICAvLyBlbmFibGluZyAicGF1c2Ugb24gZXhjZXB0aW9ucyIgaW4geW91ciBKYXZhU2NyaXB0IGRlYnVnZ2VyLgogICAgICB0aHJvdyBuZXcgRXJyb3IobWVzc2FnZSk7IC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1lbXB0eQogICAgfSBjYXRjaCAoZSkge30KICB9Cn0KLyoqCiAqIFJldHVybnMgYSByZXNvbHZlZCBwYXRoIG9iamVjdCByZWxhdGl2ZSB0byB0aGUgZ2l2ZW4gcGF0aG5hbWUuCiAqCiAqIEBzZWUgaHR0cHM6Ly9yZWFjdHJvdXRlci5jb20vZG9jcy9lbi92Ni91dGlscy9yZXNvbHZlLXBhdGgKICovCgpmdW5jdGlvbiByZXNvbHZlUGF0aCh0bywgZnJvbVBhdGhuYW1lKSB7CiAgaWYgKGZyb21QYXRobmFtZSA9PT0gdm9pZCAwKSB7CiAgICBmcm9tUGF0aG5hbWUgPSAiLyI7CiAgfQoKICBsZXQgewogICAgcGF0aG5hbWU6IHRvUGF0aG5hbWUsCiAgICBzZWFyY2ggPSAiIiwKICAgIGhhc2ggPSAiIgogIH0gPSB0eXBlb2YgdG8gPT09ICJzdHJpbmciID8gcGFyc2VQYXRoKHRvKSA6IHRvOwogIGxldCBwYXRobmFtZSA9IHRvUGF0aG5hbWUgPyB0b1BhdGhuYW1lLnN0YXJ0c1dpdGgoIi8iKSA/IHRvUGF0aG5hbWUgOiByZXNvbHZlUGF0aG5hbWUodG9QYXRobmFtZSwgZnJvbVBhdGhuYW1lKSA6IGZyb21QYXRobmFtZTsKICByZXR1cm4gewogICAgcGF0aG5hbWUsCiAgICBzZWFyY2g6IG5vcm1hbGl6ZVNlYXJjaChzZWFyY2gpLAogICAgaGFzaDogbm9ybWFsaXplSGFzaChoYXNoKQogIH07Cn0KCmZ1bmN0aW9uIHJlc29sdmVQYXRobmFtZShyZWxhdGl2ZVBhdGgsIGZyb21QYXRobmFtZSkgewogIGxldCBzZWdtZW50cyA9IGZyb21QYXRobmFtZS5yZXBsYWNlKC9cLyskLywgIiIpLnNwbGl0KCIvIik7CiAgbGV0IHJlbGF0aXZlU2VnbWVudHMgPSByZWxhdGl2ZVBhdGguc3BsaXQoIi8iKTsKICByZWxhdGl2ZVNlZ21lbnRzLmZvckVhY2goc2VnbWVudCA9PiB7CiAgICBpZiAoc2VnbWVudCA9PT0gIi4uIikgewogICAgICAvLyBLZWVwIHRoZSByb290ICIiIHNlZ21lbnQgc28gdGhlIHBhdGhuYW1lIHN0YXJ0cyBhdCAvCiAgICAgIGlmIChzZWdtZW50cy5sZW5ndGggPiAxKSBzZWdtZW50cy5wb3AoKTsKICAgIH0gZWxzZSBpZiAoc2VnbWVudCAhPT0gIi4iKSB7CiAgICAgIHNlZ21lbnRzLnB1c2goc2VnbWVudCk7CiAgICB9CiAgfSk7CiAgcmV0dXJuIHNlZ21lbnRzLmxlbmd0aCA+IDEgPyBzZWdtZW50cy5qb2luKCIvIikgOiAiLyI7Cn0KCmZ1bmN0aW9uIGdldEludmFsaWRQYXRoRXJyb3IoY2hhciwgZmllbGQsIGRlc3QsIHBhdGgpIHsKICByZXR1cm4gIkNhbm5vdCBpbmNsdWRlIGEgJyIgKyBjaGFyICsgIicgY2hhcmFjdGVyIGluIGEgbWFudWFsbHkgc3BlY2lmaWVkICIgKyAoImB0by4iICsgZmllbGQgKyAiYCBmaWVsZCBbIiArIEpTT04uc3RyaW5naWZ5KHBhdGgpICsgIl0uICBQbGVhc2Ugc2VwYXJhdGUgaXQgb3V0IHRvIHRoZSAiKSArICgiYHRvLiIgKyBkZXN0ICsgImAgZmllbGQuIEFsdGVybmF0aXZlbHkgeW91IG1heSBwcm92aWRlIHRoZSBmdWxsIHBhdGggYXMgIikgKyAiYSBzdHJpbmcgaW4gPExpbmsgdG89XCIuLi5cIj4gYW5kIHRoZSByb3V0ZXIgd2lsbCBwYXJzZSBpdCBmb3IgeW91LiI7Cn0KLyoqCiAqIEBwcml2YXRlCiAqCiAqIFdoZW4gcHJvY2Vzc2luZyByZWxhdGl2ZSBuYXZpZ2F0aW9uIHdlIHdhbnQgdG8gaWdub3JlIGFuY2VzdG9yIHJvdXRlcyB0aGF0CiAqIGRvIG5vdCBjb250cmlidXRlIHRvIHRoZSBwYXRoLCBzdWNoIHRoYXQgaW5kZXgvcGF0aGxlc3MgbGF5b3V0IHJvdXRlcyBkb24ndAogKiBpbnRlcmZlcmUuCiAqCiAqIEZvciBleGFtcGxlLCB3aGVuIG1vdmluZyBhIHJvdXRlIGVsZW1lbnQgaW50byBhbiBpbmRleCByb3V0ZSBhbmQvb3IgYQogKiBwYXRobGVzcyBsYXlvdXQgcm91dGUsIHJlbGF0aXZlIGxpbmsgYmVoYXZpb3IgY29udGFpbmVkIHdpdGhpbiBzaG91bGQgc3RheQogKiB0aGUgc2FtZS4gIEJvdGggb2YgdGhlIGZvbGxvd2luZyBleGFtcGxlcyBzaG91bGQgbGluayBiYWNrIHRvIHRoZSByb290OgogKgogKiAgIDxSb3V0ZSBwYXRoPSIvIj4KICogICAgIDxSb3V0ZSBwYXRoPSJhY2NvdW50cyIgZWxlbWVudD17PExpbmsgdG89Ii4uIn0+CiAqICAgPC9Sb3V0ZT4KICoKICogICA8Um91dGUgcGF0aD0iLyI+CiAqICAgICA8Um91dGUgcGF0aD0iYWNjb3VudHMiPgogKiAgICAgICA8Um91dGUgZWxlbWVudD17PEFjY291bnRzTGF5b3V0IC8+fT4gICAgICAgLy8gPC0tIERvZXMgbm90IGNvbnRyaWJ1dGUKICogICAgICAgICA8Um91dGUgaW5kZXggZWxlbWVudD17PExpbmsgdG89Ii4uIn0gLz4gIC8vIDwtLSBEb2VzIG5vdCBjb250cmlidXRlCiAqICAgICAgIDwvUm91dGUKICogICAgIDwvUm91dGU+CiAqICAgPC9Sb3V0ZT4KICovCgoKZnVuY3Rpb24gZ2V0UGF0aENvbnRyaWJ1dGluZ01hdGNoZXMobWF0Y2hlcykgewogIHJldHVybiBtYXRjaGVzLmZpbHRlcigobWF0Y2gsIGluZGV4KSA9PiBpbmRleCA9PT0gMCB8fCBtYXRjaC5yb3V0ZS5wYXRoICYmIG1hdGNoLnJvdXRlLnBhdGgubGVuZ3RoID4gMCk7Cn0KLyoqCiAqIEBwcml2YXRlCiAqLwoKZnVuY3Rpb24gcmVzb2x2ZVRvKHRvQXJnLCByb3V0ZVBhdGhuYW1lcywgbG9jYXRpb25QYXRobmFtZSwgaXNQYXRoUmVsYXRpdmUpIHsKICBpZiAoaXNQYXRoUmVsYXRpdmUgPT09IHZvaWQgMCkgewogICAgaXNQYXRoUmVsYXRpdmUgPSBmYWxzZTsKICB9CgogIGxldCB0bzsKCiAgaWYgKHR5cGVvZiB0b0FyZyA9PT0gInN0cmluZyIpIHsKICAgIHRvID0gcGFyc2VQYXRoKHRvQXJnKTsKICB9IGVsc2UgewogICAgdG8gPSBfZXh0ZW5kcyh7fSwgdG9BcmcpOwogICAgcm91dGVyX2ludmFyaWFudCghdG8ucGF0aG5hbWUgfHwgIXRvLnBhdGhuYW1lLmluY2x1ZGVzKCI/IiksIGdldEludmFsaWRQYXRoRXJyb3IoIj8iLCAicGF0aG5hbWUiLCAic2VhcmNoIiwgdG8pKTsKICAgIHJvdXRlcl9pbnZhcmlhbnQoIXRvLnBhdGhuYW1lIHx8ICF0by5wYXRobmFtZS5pbmNsdWRlcygiIyIpLCBnZXRJbnZhbGlkUGF0aEVycm9yKCIjIiwgInBhdGhuYW1lIiwgImhhc2giLCB0bykpOwogICAgcm91dGVyX2ludmFyaWFudCghdG8uc2VhcmNoIHx8ICF0by5zZWFyY2guaW5jbHVkZXMoIiMiKSwgZ2V0SW52YWxpZFBhdGhFcnJvcigiIyIsICJzZWFyY2giLCAiaGFzaCIsIHRvKSk7CiAgfQoKICBsZXQgaXNFbXB0eVBhdGggPSB0b0FyZyA9PT0gIiIgfHwgdG8ucGF0aG5hbWUgPT09ICIiOwogIGxldCB0b1BhdGhuYW1lID0gaXNFbXB0eVBhdGggPyAiLyIgOiB0by5wYXRobmFtZTsKICBsZXQgZnJvbTsgLy8gUm91dGluZyBpcyByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBwYXRobmFtZSBpZiBleHBsaWNpdGx5IHJlcXVlc3RlZC4KICAvLwogIC8vIElmIGEgcGF0aG5hbWUgaXMgZXhwbGljaXRseSBwcm92aWRlZCBpbiBgdG9gLCBpdCBzaG91bGQgYmUgcmVsYXRpdmUgdG8gdGhlCiAgLy8gcm91dGUgY29udGV4dC4gVGhpcyBpcyBleHBsYWluZWQgaW4gYE5vdGUgb24gYDxMaW5rIHRvPmAgdmFsdWVzYCBpbiBvdXIKICAvLyBtaWdyYXRpb24gZ3VpZGUgZnJvbSB2NSBhcyBhIG1lYW5zIG9mIGRpc2FtYmlndWF0aW9uIGJldHdlZW4gYHRvYCB2YWx1ZXMKICAvLyB0aGF0IGJlZ2luIHdpdGggYC9gIGFuZCB0aG9zZSB0aGF0IGRvIG5vdC4gSG93ZXZlciwgdGhpcyBpcyBwcm9ibGVtYXRpYyBmb3IKICAvLyBgdG9gIHZhbHVlcyB0aGF0IGRvIG5vdCBwcm92aWRlIGEgcGF0aG5hbWUuIGB0b2AgY2FuIHNpbXBseSBiZSBhIHNlYXJjaCBvcgogIC8vIGhhc2ggc3RyaW5nLCBpbiB3aGljaCBjYXNlIHdlIHNob3VsZCBhc3N1bWUgdGhhdCB0aGUgbmF2aWdhdGlvbiBpcyByZWxhdGl2ZQogIC8vIHRvIHRoZSBjdXJyZW50IGxvY2F0aW9uJ3MgcGF0aG5hbWUgYW5kICpub3QqIHRoZSByb3V0ZSBwYXRobmFtZS4KCiAgaWYgKGlzUGF0aFJlbGF0aXZlIHx8IHRvUGF0aG5hbWUgPT0gbnVsbCkgewogICAgZnJvbSA9IGxvY2F0aW9uUGF0aG5hbWU7CiAgfSBlbHNlIHsKICAgIGxldCByb3V0ZVBhdGhuYW1lSW5kZXggPSByb3V0ZVBhdGhuYW1lcy5sZW5ndGggLSAxOwoKICAgIGlmICh0b1BhdGhuYW1lLnN0YXJ0c1dpdGgoIi4uIikpIHsKICAgICAgbGV0IHRvU2VnbWVudHMgPSB0b1BhdGhuYW1lLnNwbGl0KCIvIik7IC8vIEVhY2ggbGVhZGluZyAuLiBzZWdtZW50IG1lYW5zICJnbyB1cCBvbmUgcm91dGUiIGluc3RlYWQgb2YgImdvIHVwIG9uZQogICAgICAvLyBVUkwgc2VnbWVudCIuICBUaGlzIGlzIGEga2V5IGRpZmZlcmVuY2UgZnJvbSBob3cgPGEgaHJlZj4gd29ya3MgYW5kIGEKICAgICAgLy8gbWFqb3IgcmVhc29uIHdlIGNhbGwgdGhpcyBhICJ0byIgdmFsdWUgaW5zdGVhZCBvZiBhICJocmVmIi4KCiAgICAgIHdoaWxlICh0b1NlZ21lbnRzWzBdID09PSAiLi4iKSB7CiAgICAgICAgdG9TZWdtZW50cy5zaGlmdCgpOwogICAgICAgIHJvdXRlUGF0aG5hbWVJbmRleCAtPSAxOwogICAgICB9CgogICAgICB0by5wYXRobmFtZSA9IHRvU2VnbWVudHMuam9pbigiLyIpOwogICAgfSAvLyBJZiB0aGVyZSBhcmUgbW9yZSAiLi4iIHNlZ21lbnRzIHRoYW4gcGFyZW50IHJvdXRlcywgcmVzb2x2ZSByZWxhdGl2ZSB0bwogICAgLy8gdGhlIHJvb3QgLyBVUkwuCgoKICAgIGZyb20gPSByb3V0ZVBhdGhuYW1lSW5kZXggPj0gMCA/IHJvdXRlUGF0aG5hbWVzW3JvdXRlUGF0aG5hbWVJbmRleF0gOiAiLyI7CiAgfQoKICBsZXQgcGF0aCA9IHJlc29sdmVQYXRoKHRvLCBmcm9tKTsgLy8gRW5zdXJlIHRoZSBwYXRobmFtZSBoYXMgYSB0cmFpbGluZyBzbGFzaCBpZiB0aGUgb3JpZ2luYWwgInRvIiBoYWQgb25lCgogIGxldCBoYXNFeHBsaWNpdFRyYWlsaW5nU2xhc2ggPSB0b1BhdGhuYW1lICYmIHRvUGF0aG5hbWUgIT09ICIvIiAmJiB0b1BhdGhuYW1lLmVuZHNXaXRoKCIvIik7IC8vIE9yIGlmIHRoaXMgd2FzIGEgbGluayB0byB0aGUgY3VycmVudCBwYXRoIHdoaWNoIGhhcyBhIHRyYWlsaW5nIHNsYXNoCgogIGxldCBoYXNDdXJyZW50VHJhaWxpbmdTbGFzaCA9IChpc0VtcHR5UGF0aCB8fCB0b1BhdGhuYW1lID09PSAiLiIpICYmIGxvY2F0aW9uUGF0aG5hbWUuZW5kc1dpdGgoIi8iKTsKCiAgaWYgKCFwYXRoLnBhdGhuYW1lLmVuZHNXaXRoKCIvIikgJiYgKGhhc0V4cGxpY2l0VHJhaWxpbmdTbGFzaCB8fCBoYXNDdXJyZW50VHJhaWxpbmdTbGFzaCkpIHsKICAgIHBhdGgucGF0aG5hbWUgKz0gIi8iOwogIH0KCiAgcmV0dXJuIHBhdGg7Cn0KLyoqCiAqIEBwcml2YXRlCiAqLwoKZnVuY3Rpb24gZ2V0VG9QYXRobmFtZSh0bykgewogIC8vIEVtcHR5IHN0cmluZ3Mgc2hvdWxkIGJlIHRyZWF0ZWQgdGhlIHNhbWUgYXMgLyBwYXRocwogIHJldHVybiB0byA9PT0gIiIgfHwgdG8ucGF0aG5hbWUgPT09ICIiID8gIi8iIDogdHlwZW9mIHRvID09PSAic3RyaW5nIiA/IHBhcnNlUGF0aCh0bykucGF0aG5hbWUgOiB0by5wYXRobmFtZTsKfQovKioKICogQHByaXZhdGUKICovCgpjb25zdCByb3V0ZXJfam9pblBhdGhzID0gcGF0aHMgPT4gcGF0aHMuam9pbigiLyIpLnJlcGxhY2UoL1wvXC8rL2csICIvIik7Ci8qKgogKiBAcHJpdmF0ZQogKi8KCmNvbnN0IG5vcm1hbGl6ZVBhdGhuYW1lID0gcGF0aG5hbWUgPT4gcGF0aG5hbWUucmVwbGFjZSgvXC8rJC8sICIiKS5yZXBsYWNlKC9eXC8qLywgIi8iKTsKLyoqCiAqIEBwcml2YXRlCiAqLwoKY29uc3Qgbm9ybWFsaXplU2VhcmNoID0gc2VhcmNoID0+ICFzZWFyY2ggfHwgc2VhcmNoID09PSAiPyIgPyAiIiA6IHNlYXJjaC5zdGFydHNXaXRoKCI/IikgPyBzZWFyY2ggOiAiPyIgKyBzZWFyY2g7Ci8qKgogKiBAcHJpdmF0ZQogKi8KCmNvbnN0IG5vcm1hbGl6ZUhhc2ggPSBoYXNoID0+ICFoYXNoIHx8IGhhc2ggPT09ICIjIiA/ICIiIDogaGFzaC5zdGFydHNXaXRoKCIjIikgPyBoYXNoIDogIiMiICsgaGFzaDsKLyoqCiAqIFRoaXMgaXMgYSBzaG9ydGN1dCBmb3IgY3JlYXRpbmcgYGFwcGxpY2F0aW9uL2pzb25gIHJlc3BvbnNlcy4gQ29udmVydHMgYGRhdGFgCiAqIHRvIEpTT04gYW5kIHNldHMgdGhlIGBDb250ZW50LVR5cGVgIGhlYWRlci4KICovCgpjb25zdCBqc29uID0gZnVuY3Rpb24ganNvbihkYXRhLCBpbml0KSB7CiAgaWYgKGluaXQgPT09IHZvaWQgMCkgewogICAgaW5pdCA9IHt9OwogIH0KCiAgbGV0IHJlc3BvbnNlSW5pdCA9IHR5cGVvZiBpbml0ID09PSAibnVtYmVyIiA/IHsKICAgIHN0YXR1czogaW5pdAogIH0gOiBpbml0OwogIGxldCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVzcG9uc2VJbml0LmhlYWRlcnMpOwoKICBpZiAoIWhlYWRlcnMuaGFzKCJDb250ZW50LVR5cGUiKSkgewogICAgaGVhZGVycy5zZXQoIkNvbnRlbnQtVHlwZSIsICJhcHBsaWNhdGlvbi9qc29uOyBjaGFyc2V0PXV0Zi04Iik7CiAgfQoKICByZXR1cm4gbmV3IFJlc3BvbnNlKEpTT04uc3RyaW5naWZ5KGRhdGEpLCBfZXh0ZW5kcyh7fSwgcmVzcG9uc2VJbml0LCB7CiAgICBoZWFkZXJzCiAgfSkpOwp9OwpjbGFzcyByb3V0ZXJfQWJvcnRlZERlZmVycmVkRXJyb3IgZXh0ZW5kcyBFcnJvciB7fQpjbGFzcyBEZWZlcnJlZERhdGEgewogIGNvbnN0cnVjdG9yKGRhdGEpIHsKICAgIHRoaXMucGVuZGluZ0tleXMgPSBuZXcgU2V0KCk7CiAgICB0aGlzLnN1YnNjcmliZXIgPSB1bmRlZmluZWQ7CiAgICByb3V0ZXJfaW52YXJpYW50KGRhdGEgJiYgdHlwZW9mIGRhdGEgPT09ICJvYmplY3QiICYmICFBcnJheS5pc0FycmF5KGRhdGEpLCAiZGVmZXIoKSBvbmx5IGFjY2VwdHMgcGxhaW4gb2JqZWN0cyIpOyAvLyBTZXQgdXAgYW4gQWJvcnRDb250cm9sbGVyICsgUHJvbWlzZSB3ZSBjYW4gcmFjZSBhZ2FpbnN0IHRvIGV4aXQgZWFybHkKICAgIC8vIGNhbmNlbGxhdGlvbgoKICAgIGxldCByZWplY3Q7CiAgICB0aGlzLmFib3J0UHJvbWlzZSA9IG5ldyBQcm9taXNlKChfLCByKSA9PiByZWplY3QgPSByKTsKICAgIHRoaXMuY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTsKCiAgICBsZXQgb25BYm9ydCA9ICgpID0+IHJlamVjdChuZXcgcm91dGVyX0Fib3J0ZWREZWZlcnJlZEVycm9yKCJEZWZlcnJlZCBkYXRhIGFib3J0ZWQiKSk7CgogICAgdGhpcy51bmxpc3RlbkFib3J0U2lnbmFsID0gKCkgPT4gdGhpcy5jb250cm9sbGVyLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKCJhYm9ydCIsIG9uQWJvcnQpOwoKICAgIHRoaXMuY29udHJvbGxlci5zaWduYWwuYWRkRXZlbnRMaXN0ZW5lcigiYWJvcnQiLCBvbkFib3J0KTsKICAgIHRoaXMuZGF0YSA9IE9iamVjdC5lbnRyaWVzKGRhdGEpLnJlZHVjZSgoYWNjLCBfcmVmKSA9PiB7CiAgICAgIGxldCBba2V5LCB2YWx1ZV0gPSBfcmVmOwogICAgICByZXR1cm4gT2JqZWN0LmFzc2lnbihhY2MsIHsKICAgICAgICBba2V5XTogdGhpcy50cmFja1Byb21pc2Uoa2V5LCB2YWx1ZSkKICAgICAgfSk7CiAgICB9LCB7fSk7CiAgfQoKICB0cmFja1Byb21pc2Uoa2V5LCB2YWx1ZSkgewogICAgaWYgKCEodmFsdWUgaW5zdGFuY2VvZiBQcm9taXNlKSkgewogICAgICByZXR1cm4gdmFsdWU7CiAgICB9CgogICAgdGhpcy5wZW5kaW5nS2V5cy5hZGQoa2V5KTsgLy8gV2Ugc3RvcmUgYSBsaXR0bGUgd3JhcHBlciBwcm9taXNlIHRoYXQgd2lsbCBiZSBleHRlbmRlZCB3aXRoCiAgICAvLyBfZGF0YS9fZXJyb3IgcHJvcHMgdXBvbiByZXNvbHZlL3JlamVjdAoKICAgIGxldCBwcm9taXNlID0gUHJvbWlzZS5yYWNlKFt2YWx1ZSwgdGhpcy5hYm9ydFByb21pc2VdKS50aGVuKGRhdGEgPT4gdGhpcy5vblNldHRsZShwcm9taXNlLCBrZXksIG51bGwsIGRhdGEpLCBlcnJvciA9PiB0aGlzLm9uU2V0dGxlKHByb21pc2UsIGtleSwgZXJyb3IpKTsgLy8gUmVnaXN0ZXIgcmVqZWN0aW9uIGxpc3RlbmVycyB0byBhdm9pZCB1bmNhdWdodCBwcm9taXNlIHJlamVjdGlvbnMgb24KICAgIC8vIGVycm9ycyBvciBhYm9ydGVkIGRlZmVycmVkIHZhbHVlcwoKICAgIHByb21pc2UuY2F0Y2goKCkgPT4ge30pOwogICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsICJfdHJhY2tlZCIsIHsKICAgICAgZ2V0OiAoKSA9PiB0cnVlCiAgICB9KTsKICAgIHJldHVybiBwcm9taXNlOwogIH0KCiAgb25TZXR0bGUocHJvbWlzZSwga2V5LCBlcnJvciwgZGF0YSkgewogICAgaWYgKHRoaXMuY29udHJvbGxlci5zaWduYWwuYWJvcnRlZCAmJiBlcnJvciBpbnN0YW5jZW9mIHJvdXRlcl9BYm9ydGVkRGVmZXJyZWRFcnJvcikgewogICAgICB0aGlzLnVubGlzdGVuQWJvcnRTaWduYWwoKTsKICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsICJfZXJyb3IiLCB7CiAgICAgICAgZ2V0OiAoKSA9PiBlcnJvcgogICAgICB9KTsKICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKTsKICAgIH0KCiAgICB0aGlzLnBlbmRpbmdLZXlzLmRlbGV0ZShrZXkpOwoKICAgIGlmICh0aGlzLmRvbmUpIHsKICAgICAgLy8gTm90aGluZyBsZWZ0IHRvIGFib3J0IQogICAgICB0aGlzLnVubGlzdGVuQWJvcnRTaWduYWwoKTsKICAgIH0KCiAgICBjb25zdCBzdWJzY3JpYmVyID0gdGhpcy5zdWJzY3JpYmVyOwoKICAgIGlmIChlcnJvcikgewogICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkocHJvbWlzZSwgIl9lcnJvciIsIHsKICAgICAgICBnZXQ6ICgpID0+IGVycm9yCiAgICAgIH0pOwogICAgICBzdWJzY3JpYmVyICYmIHN1YnNjcmliZXIoZmFsc2UpOwogICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpOwogICAgfQoKICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShwcm9taXNlLCAiX2RhdGEiLCB7CiAgICAgIGdldDogKCkgPT4gZGF0YQogICAgfSk7CiAgICBzdWJzY3JpYmVyICYmIHN1YnNjcmliZXIoZmFsc2UpOwogICAgcmV0dXJuIGRhdGE7CiAgfQoKICBzdWJzY3JpYmUoZm4pIHsKICAgIHRoaXMuc3Vic2NyaWJlciA9IGZuOwogIH0KCiAgY2FuY2VsKCkgewogICAgdGhpcy5jb250cm9sbGVyLmFib3J0KCk7CiAgICB0aGlzLnBlbmRpbmdLZXlzLmZvckVhY2goKHYsIGspID0+IHRoaXMucGVuZGluZ0tleXMuZGVsZXRlKGspKTsKICAgIGxldCBzdWJzY3JpYmVyID0gdGhpcy5zdWJzY3JpYmVyOwogICAgc3Vic2NyaWJlciAmJiBzdWJzY3JpYmVyKHRydWUpOwogIH0KCiAgYXN5bmMgcmVzb2x2ZURhdGEoc2lnbmFsKSB7CiAgICBsZXQgYWJvcnRlZCA9IGZhbHNlOwoKICAgIGlmICghdGhpcy5kb25lKSB7CiAgICAgIGxldCBvbkFib3J0ID0gKCkgPT4gdGhpcy5jYW5jZWwoKTsKCiAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCJhYm9ydCIsIG9uQWJvcnQpOwogICAgICBhYm9ydGVkID0gYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7CiAgICAgICAgdGhpcy5zdWJzY3JpYmUoYWJvcnRlZCA9PiB7CiAgICAgICAgICBzaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcigiYWJvcnQiLCBvbkFib3J0KTsKCiAgICAgICAgICBpZiAoYWJvcnRlZCB8fCB0aGlzLmRvbmUpIHsKICAgICAgICAgICAgcmVzb2x2ZShhYm9ydGVkKTsKICAgICAgICAgIH0KICAgICAgICB9KTsKICAgICAgfSk7CiAgICB9CgogICAgcmV0dXJuIGFib3J0ZWQ7CiAgfQoKICBnZXQgZG9uZSgpIHsKICAgIHJldHVybiB0aGlzLnBlbmRpbmdLZXlzLnNpemUgPT09IDA7CiAgfQoKICBnZXQgdW53cmFwcGVkRGF0YSgpIHsKICAgIHJvdXRlcl9pbnZhcmlhbnQodGhpcy5kYXRhICE9PSBudWxsICYmIHRoaXMuZG9uZSwgIkNhbiBvbmx5IHVud3JhcCBkYXRhIG9uIGluaXRpYWxpemVkIGFuZCBzZXR0bGVkIGRlZmVycmVkcyIpOwogICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKHRoaXMuZGF0YSkucmVkdWNlKChhY2MsIF9yZWYyKSA9PiB7CiAgICAgIGxldCBba2V5LCB2YWx1ZV0gPSBfcmVmMjsKICAgICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oYWNjLCB7CiAgICAgICAgW2tleV06IHVud3JhcFRyYWNrZWRQcm9taXNlKHZhbHVlKQogICAgICB9KTsKICAgIH0sIHt9KTsKICB9Cgp9CgpmdW5jdGlvbiBpc1RyYWNrZWRQcm9taXNlKHZhbHVlKSB7CiAgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUHJvbWlzZSAmJiB2YWx1ZS5fdHJhY2tlZCA9PT0gdHJ1ZTsKfQoKZnVuY3Rpb24gdW53cmFwVHJhY2tlZFByb21pc2UodmFsdWUpIHsKICBpZiAoIWlzVHJhY2tlZFByb21pc2UodmFsdWUpKSB7CiAgICByZXR1cm4gdmFsdWU7CiAgfQoKICBpZiAodmFsdWUuX2Vycm9yKSB7CiAgICB0aHJvdyB2YWx1ZS5fZXJyb3I7CiAgfQoKICByZXR1cm4gdmFsdWUuX2RhdGE7Cn0KCmZ1bmN0aW9uIGRlZmVyKGRhdGEpIHsKICByZXR1cm4gbmV3IERlZmVycmVkRGF0YShkYXRhKTsKfQovKioKICogQSByZWRpcmVjdCByZXNwb25zZS4gU2V0cyB0aGUgc3RhdHVzIGNvZGUgYW5kIHRoZSBgTG9jYXRpb25gIGhlYWRlci4KICogRGVmYXVsdHMgdG8gIjMwMiBGb3VuZCIuCiAqLwoKY29uc3QgcmVkaXJlY3QgPSBmdW5jdGlvbiByZWRpcmVjdCh1cmwsIGluaXQpIHsKICBpZiAoaW5pdCA9PT0gdm9pZCAwKSB7CiAgICBpbml0ID0gMzAyOwogIH0KCiAgbGV0IHJlc3BvbnNlSW5pdCA9IGluaXQ7CgogIGlmICh0eXBlb2YgcmVzcG9uc2VJbml0ID09PSAibnVtYmVyIikgewogICAgcmVzcG9uc2VJbml0ID0gewogICAgICBzdGF0dXM6IHJlc3BvbnNlSW5pdAogICAgfTsKICB9IGVsc2UgaWYgKHR5cGVvZiByZXNwb25zZUluaXQuc3RhdHVzID09PSAidW5kZWZpbmVkIikgewogICAgcmVzcG9uc2VJbml0LnN0YXR1cyA9IDMwMjsKICB9CgogIGxldCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVzcG9uc2VJbml0LmhlYWRlcnMpOwogIGhlYWRlcnMuc2V0KCJMb2NhdGlvbiIsIHVybCk7CiAgcmV0dXJuIG5ldyBSZXNwb25zZShudWxsLCBfZXh0ZW5kcyh7fSwgcmVzcG9uc2VJbml0LCB7CiAgICBoZWFkZXJzCiAgfSkpOwp9OwovKioKICogQHByaXZhdGUKICogVXRpbGl0eSBjbGFzcyB3ZSB1c2UgdG8gaG9sZCBhdXRvLXVud3JhcHBlZCA0eHgvNXh4IFJlc3BvbnNlIGJvZGllcwogKi8KCmNsYXNzIEVycm9yUmVzcG9uc2UgewogIGNvbnN0cnVjdG9yKHN0YXR1cywgc3RhdHVzVGV4dCwgZGF0YSkgewogICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7CiAgICB0aGlzLnN0YXR1c1RleHQgPSBzdGF0dXNUZXh0IHx8ICIiOwogICAgdGhpcy5kYXRhID0gZGF0YTsKICB9Cgp9Ci8qKgogKiBDaGVjayBpZiB0aGUgZ2l2ZW4gZXJyb3IgaXMgYW4gRXJyb3JSZXNwb25zZSBnZW5lcmF0ZWQgZnJvbSBhIDR4eC81eHgKICogUmVzcG9uc2UgdGhyb3cgZnJvbSBhbiBhY3Rpb24vbG9hZGVyCiAqLwoKZnVuY3Rpb24gaXNSb3V0ZUVycm9yUmVzcG9uc2UoZSkgewogIHJldHVybiBlIGluc3RhbmNlb2YgRXJyb3JSZXNwb25zZTsKfQoKY29uc3QgSURMRV9OQVZJR0FUSU9OID0gewogIHN0YXRlOiAiaWRsZSIsCiAgbG9jYXRpb246IHVuZGVmaW5lZCwKICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsCiAgZm9ybUFjdGlvbjogdW5kZWZpbmVkLAogIGZvcm1FbmNUeXBlOiB1bmRlZmluZWQsCiAgZm9ybURhdGE6IHVuZGVmaW5lZAp9Owpjb25zdCBJRExFX0ZFVENIRVIgPSB7CiAgc3RhdGU6ICJpZGxlIiwKICBkYXRhOiB1bmRlZmluZWQsCiAgZm9ybU1ldGhvZDogdW5kZWZpbmVkLAogIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICBmb3JtRW5jVHlwZTogdW5kZWZpbmVkLAogIGZvcm1EYXRhOiB1bmRlZmluZWQKfTsKY29uc3QgaXNCcm93c2VyID0gdHlwZW9mIHdpbmRvdyAhPT0gInVuZGVmaW5lZCIgJiYgdHlwZW9mIHdpbmRvdy5kb2N1bWVudCAhPT0gInVuZGVmaW5lZCIgJiYgdHlwZW9mIHdpbmRvdy5kb2N1bWVudC5jcmVhdGVFbGVtZW50ICE9PSAidW5kZWZpbmVkIjsKY29uc3QgaXNTZXJ2ZXIgPSAhaXNCcm93c2VyOyAvLyNlbmRyZWdpb24KLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KLy8jcmVnaW9uIGNyZWF0ZVJvdXRlcgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKLyoqCiAqIENyZWF0ZSBhIHJvdXRlciBhbmQgbGlzdGVuIHRvIGhpc3RvcnkgUE9QIG5hdmlnYXRpb25zCiAqLwoKZnVuY3Rpb24gcm91dGVyX2NyZWF0ZVJvdXRlcihpbml0KSB7CiAgcm91dGVyX2ludmFyaWFudChpbml0LnJvdXRlcy5sZW5ndGggPiAwLCAiWW91IG11c3QgcHJvdmlkZSBhIG5vbi1lbXB0eSByb3V0ZXMgYXJyYXkgdG8gY3JlYXRlUm91dGVyIik7CiAgbGV0IGRhdGFSb3V0ZXMgPSBjb252ZXJ0Um91dGVzVG9EYXRhUm91dGVzKGluaXQucm91dGVzKTsgLy8gQ2xlYW51cCBmdW5jdGlvbiBmb3IgaGlzdG9yeQoKICBsZXQgdW5saXN0ZW5IaXN0b3J5ID0gbnVsbDsgLy8gRXh0ZXJuYWxseS1wcm92aWRlZCBmdW5jdGlvbnMgdG8gY2FsbCBvbiBhbGwgc3RhdGUgY2hhbmdlcwoKICBsZXQgc3Vic2NyaWJlcnMgPSBuZXcgU2V0KCk7IC8vIEV4dGVybmFsbHktcHJvdmlkZWQgb2JqZWN0IHRvIGhvbGQgc2Nyb2xsIHJlc3RvcmF0aW9uIGxvY2F0aW9ucyBkdXJpbmcgcm91dGluZwoKICBsZXQgc2F2ZWRTY3JvbGxQb3NpdGlvbnMgPSBudWxsOyAvLyBFeHRlcm5hbGx5LXByb3ZpZGVkIGZ1bmN0aW9uIHRvIGdldCBzY3JvbGwgcmVzdG9yYXRpb24ga2V5cwoKICBsZXQgZ2V0U2Nyb2xsUmVzdG9yYXRpb25LZXkgPSBudWxsOyAvLyBFeHRlcm5hbGx5LXByb3ZpZGVkIGZ1bmN0aW9uIHRvIGdldCBjdXJyZW50IHNjcm9sbCBwb3NpdGlvbgoKICBsZXQgZ2V0U2Nyb2xsUG9zaXRpb24gPSBudWxsOyAvLyBPbmUtdGltZSBmbGFnIHRvIGNvbnRyb2wgdGhlIGluaXRpYWwgaHlkcmF0aW9uIHNjcm9sbCByZXN0b3JhdGlvbi4gIEJlY2F1c2UKICAvLyB3ZSBkb24ndCBnZXQgdGhlIHNhdmVkIHBvc2l0aW9ucyBmcm9tIDxTY3JvbGxSZXN0b3JhdGlvbiAvPiB1bnRpbCBfYWZ0ZXJfCiAgLy8gdGhlIGluaXRpYWwgcmVuZGVyLCB3ZSBuZWVkIHRvIG1hbnVhbGx5IHRyaWdnZXIgYSBzZXBhcmF0ZSB1cGRhdGVTdGF0ZSB0bwogIC8vIHNlbmQgYWxvbmcgdGhlIHJlc3RvcmVTY3JvbGxQb3NpdGlvbgoKICBsZXQgaW5pdGlhbFNjcm9sbFJlc3RvcmVkID0gZmFsc2U7CiAgbGV0IGluaXRpYWxNYXRjaGVzID0gbWF0Y2hSb3V0ZXMoZGF0YVJvdXRlcywgaW5pdC5oaXN0b3J5LmxvY2F0aW9uLCBpbml0LmJhc2VuYW1lKTsKICBsZXQgaW5pdGlhbEVycm9ycyA9IG51bGw7CgogIGlmIChpbml0aWFsTWF0Y2hlcyA9PSBudWxsKSB7CiAgICAvLyBJZiB3ZSBkbyBub3QgbWF0Y2ggYSB1c2VyLXByb3ZpZGVkLXJvdXRlLCBmYWxsIGJhY2sgdG8gdGhlIHJvb3QKICAgIC8vIHRvIGFsbG93IHRoZSBlcnJvciBib3VuZGFyeSB0byB0YWtlIG92ZXIKICAgIGxldCB7CiAgICAgIG1hdGNoZXMsCiAgICAgIHJvdXRlLAogICAgICBlcnJvcgogICAgfSA9IGdldE5vdEZvdW5kTWF0Y2hlcyhkYXRhUm91dGVzKTsKICAgIGluaXRpYWxNYXRjaGVzID0gbWF0Y2hlczsKICAgIGluaXRpYWxFcnJvcnMgPSB7CiAgICAgIFtyb3V0ZS5pZF06IGVycm9yCiAgICB9OwogIH0KCiAgbGV0IGluaXRpYWxpemVkID0gIWluaXRpYWxNYXRjaGVzLnNvbWUobSA9PiBtLnJvdXRlLmxvYWRlcikgfHwgaW5pdC5oeWRyYXRpb25EYXRhICE9IG51bGw7CiAgbGV0IHJvdXRlcjsKICBsZXQgc3RhdGUgPSB7CiAgICBoaXN0b3J5QWN0aW9uOiBpbml0Lmhpc3RvcnkuYWN0aW9uLAogICAgbG9jYXRpb246IGluaXQuaGlzdG9yeS5sb2NhdGlvbiwKICAgIG1hdGNoZXM6IGluaXRpYWxNYXRjaGVzLAogICAgaW5pdGlhbGl6ZWQsCiAgICBuYXZpZ2F0aW9uOiBJRExFX05BVklHQVRJT04sCiAgICByZXN0b3JlU2Nyb2xsUG9zaXRpb246IG51bGwsCiAgICBwcmV2ZW50U2Nyb2xsUmVzZXQ6IGZhbHNlLAogICAgcmV2YWxpZGF0aW9uOiAiaWRsZSIsCiAgICBsb2FkZXJEYXRhOiBpbml0Lmh5ZHJhdGlvbkRhdGEgJiYgaW5pdC5oeWRyYXRpb25EYXRhLmxvYWRlckRhdGEgfHwge30sCiAgICBhY3Rpb25EYXRhOiBpbml0Lmh5ZHJhdGlvbkRhdGEgJiYgaW5pdC5oeWRyYXRpb25EYXRhLmFjdGlvbkRhdGEgfHwgbnVsbCwKICAgIGVycm9yczogaW5pdC5oeWRyYXRpb25EYXRhICYmIGluaXQuaHlkcmF0aW9uRGF0YS5lcnJvcnMgfHwgaW5pdGlhbEVycm9ycywKICAgIGZldGNoZXJzOiBuZXcgTWFwKCkKICB9OyAvLyAtLSBTdGF0ZWZ1bCBpbnRlcm5hbCB2YXJpYWJsZXMgdG8gbWFuYWdlIG5hdmlnYXRpb25zIC0tCiAgLy8gQ3VycmVudCBuYXZpZ2F0aW9uIGluIHByb2dyZXNzICh0byBiZSBjb21taXR0ZWQgaW4gY29tcGxldGVOYXZpZ2F0aW9uKQoKICBsZXQgcGVuZGluZ0FjdGlvbiA9IEFjdGlvbi5Qb3A7IC8vIFNob3VsZCB0aGUgY3VycmVudCBuYXZpZ2F0aW9uIHByZXZlbnQgdGhlIHNjcm9sbCByZXNldCBpZiBzY3JvbGwgY2Fubm90CiAgLy8gYmUgcmVzdG9yZWQ/CgogIGxldCBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gZmFsc2U7IC8vIEFib3J0Q29udHJvbGxlciBmb3IgdGhlIGFjdGl2ZSBuYXZpZ2F0aW9uCgogIGxldCBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXI7IC8vIFdlIHVzZSB0aGlzIHRvIGF2b2lkIHRvdWNoaW5nIGhpc3RvcnkgaW4gY29tcGxldGVOYXZpZ2F0aW9uIGlmIGEKICAvLyByZXZhbGlkYXRpb24gaXMgZW50aXJlbHkgdW5pbnRlcnJ1cHRlZAoKICBsZXQgaXNVbmludGVycnVwdGVkUmV2YWxpZGF0aW9uID0gZmFsc2U7IC8vIFVzZSB0aGlzIGludGVybmFsIGZsYWcgdG8gZm9yY2UgcmV2YWxpZGF0aW9uIG9mIGFsbCBsb2FkZXJzOgogIC8vICAtIHN1Ym1pc3Npb25zIChjb21wbGV0ZWQgb3IgaW50ZXJydXB0ZWQpCiAgLy8gIC0gdXNlUmV2YWxpZGF0ZSgpCiAgLy8gIC0gWC1SZW1peC1SZXZhbGlkYXRlIChmcm9tIHJlZGlyZWN0KQoKICBsZXQgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCA9IGZhbHNlOyAvLyBVc2UgdGhpcyBpbnRlcm5hbCBhcnJheSB0byBjYXB0dXJlIHJvdXRlcyB0aGF0IHJlcXVpcmUgcmV2YWxpZGF0aW9uIGR1ZQogIC8vIHRvIGEgY2FuY2VsbGVkIGRlZmVycmVkIG9uIGFjdGlvbiBzdWJtaXNzaW9uCgogIGxldCBjYW5jZWxsZWREZWZlcnJlZFJvdXRlcyA9IFtdOyAvLyBVc2UgdGhpcyBpbnRlcm5hbCBhcnJheSB0byBjYXB0dXJlIGZldGNoZXIgbG9hZHMgdGhhdCB3ZXJlIGNhbmNlbGxlZCBieSBhbgogIC8vIGFjdGlvbiBuYXZpZ2F0aW9uIGFuZCByZXF1aXJlIHJldmFsaWRhdGlvbgoKICBsZXQgY2FuY2VsbGVkRmV0Y2hlckxvYWRzID0gW107IC8vIEFib3J0Q29udHJvbGxlcnMgZm9yIGFueSBpbi1mbGlnaHQgZmV0Y2hlcnMKCiAgbGV0IGZldGNoQ29udHJvbGxlcnMgPSBuZXcgTWFwKCk7IC8vIFRyYWNrIGxvYWRzIGJhc2VkIG9uIHRoZSBvcmRlciBpbiB3aGljaCB0aGV5IHN0YXJ0ZWQKCiAgbGV0IGluY3JlbWVudGluZ0xvYWRJZCA9IDA7IC8vIFRyYWNrIHRoZSBvdXRzdGFuZGluZyBwZW5kaW5nIG5hdmlnYXRpb24gZGF0YSBsb2FkIHRvIGJlIGNvbXBhcmVkIGFnYWluc3QKICAvLyB0aGUgZ2xvYmFsbHkgaW5jcmVtZW50aW5nIGxvYWQgd2hlbiBhIGZldGNoZXIgbG9hZCBsYW5kcyBhZnRlciBhIGNvbXBsZXRlZAogIC8vIG5hdmlnYXRpb24KCiAgbGV0IHBlbmRpbmdOYXZpZ2F0aW9uTG9hZElkID0gLTE7IC8vIEZldGNoZXJzIHRoYXQgdHJpZ2dlcmVkIGRhdGEgcmVsb2FkcyBhcyBhIHJlc3VsdCBvZiB0aGVpciBhY3Rpb25zCgogIGxldCBmZXRjaFJlbG9hZElkcyA9IG5ldyBNYXAoKTsgLy8gRmV0Y2hlcnMgdGhhdCB0cmlnZ2VyZWQgcmVkaXJlY3QgbmF2aWdhdGlvbnMgZnJvbSB0aGVpciBhY3Rpb25zCgogIGxldCBmZXRjaFJlZGlyZWN0SWRzID0gbmV3IFNldCgpOyAvLyBNb3N0IHJlY2VudCBocmVmL21hdGNoIGZvciBmZXRjaGVyLmxvYWQgY2FsbHMgZm9yIGZldGNoZXJzCgogIGxldCBmZXRjaExvYWRNYXRjaGVzID0gbmV3IE1hcCgpOyAvLyBTdG9yZSBEZWZlcnJlZERhdGEgaW5zdGFuY2VzIGZvciBhY3RpdmUgcm91dGUgbWF0Y2hlcy4gIFdoZW4gYQogIC8vIHJvdXRlIGxvYWRlciByZXR1cm5zIGRlZmVyKCkgd2Ugc3RpY2sgb25lIGluIGhlcmUuICBUaGVuLCB3aGVuIGEgbmVzdGVkCiAgLy8gcHJvbWlzZSByZXNvbHZlcyB3ZSB1cGRhdGUgbG9hZGVyRGF0YS4gIElmIGEgbmV3IG5hdmlnYXRpb24gc3RhcnRzIHdlCiAgLy8gY2FuY2VsIGFjdGl2ZSBkZWZlcnJlZHMgZm9yIGVsaW1pbmF0ZWQgcm91dGVzLgoKICBsZXQgYWN0aXZlRGVmZXJyZWRzID0gbmV3IE1hcCgpOyAvLyBJbml0aWFsaXplIHRoZSByb3V0ZXIsIGFsbCBzaWRlIGVmZmVjdHMgc2hvdWxkIGJlIGtpY2tlZCBvZmYgZnJvbSBoZXJlLgogIC8vIEltcGxlbWVudGVkIGFzIGEgRmx1ZW50IEFQSSBmb3IgZWFzZSBvZjoKICAvLyAgIGxldCByb3V0ZXIgPSBjcmVhdGVSb3V0ZXIoaW5pdCkuaW5pdGlhbGl6ZSgpOwoKICBmdW5jdGlvbiBpbml0aWFsaXplKCkgewogICAgLy8gSWYgaGlzdG9yeSBpbmZvcm1zIHVzIG9mIGEgUE9QIG5hdmlnYXRpb24sIHN0YXJ0IHRoZSBuYXZpZ2F0aW9uIGJ1dCBkbyBub3QgdXBkYXRlCiAgICAvLyBzdGF0ZS4gIFdlJ2xsIHVwZGF0ZSBvdXIgb3duIHN0YXRlIG9uY2UgdGhlIG5hdmlnYXRpb24gY29tcGxldGVzCiAgICB1bmxpc3Rlbkhpc3RvcnkgPSBpbml0Lmhpc3RvcnkubGlzdGVuKF9yZWYgPT4gewogICAgICBsZXQgewogICAgICAgIGFjdGlvbjogaGlzdG9yeUFjdGlvbiwKICAgICAgICBsb2NhdGlvbgogICAgICB9ID0gX3JlZjsKICAgICAgcmV0dXJuIHN0YXJ0TmF2aWdhdGlvbihoaXN0b3J5QWN0aW9uLCBsb2NhdGlvbik7CiAgICB9KTsgLy8gS2ljayBvZmYgaW5pdGlhbCBkYXRhIGxvYWQgaWYgbmVlZGVkLiAgVXNlIFBvcCB0byBhdm9pZCBtb2RpZnlpbmcgaGlzdG9yeQoKICAgIGlmICghc3RhdGUuaW5pdGlhbGl6ZWQpIHsKICAgICAgc3RhcnROYXZpZ2F0aW9uKEFjdGlvbi5Qb3AsIHN0YXRlLmxvY2F0aW9uKTsKICAgIH0KCiAgICByZXR1cm4gcm91dGVyOwogIH0gLy8gQ2xlYW4gdXAgYSByb3V0ZXIgYW5kIGl0J3Mgc2lkZSBlZmZlY3RzCgoKICBmdW5jdGlvbiBkaXNwb3NlKCkgewogICAgaWYgKHVubGlzdGVuSGlzdG9yeSkgewogICAgICB1bmxpc3Rlbkhpc3RvcnkoKTsKICAgIH0KCiAgICBzdWJzY3JpYmVycy5jbGVhcigpOwogICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyICYmIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlci5hYm9ydCgpOwogICAgc3RhdGUuZmV0Y2hlcnMuZm9yRWFjaCgoXywga2V5KSA9PiBkZWxldGVGZXRjaGVyKGtleSkpOwogIH0gLy8gU3Vic2NyaWJlIHRvIHN0YXRlIHVwZGF0ZXMgZm9yIHRoZSByb3V0ZXIKCgogIGZ1bmN0aW9uIHN1YnNjcmliZShmbikgewogICAgc3Vic2NyaWJlcnMuYWRkKGZuKTsKICAgIHJldHVybiAoKSA9PiBzdWJzY3JpYmVycy5kZWxldGUoZm4pOwogIH0gLy8gVXBkYXRlIG91ciBzdGF0ZSBhbmQgbm90aWZ5IHRoZSBjYWxsaW5nIGNvbnRleHQgb2YgdGhlIGNoYW5nZQoKCiAgZnVuY3Rpb24gdXBkYXRlU3RhdGUobmV3U3RhdGUpIHsKICAgIHN0YXRlID0gX2V4dGVuZHMoe30sIHN0YXRlLCBuZXdTdGF0ZSk7CiAgICBzdWJzY3JpYmVycy5mb3JFYWNoKHN1YnNjcmliZXIgPT4gc3Vic2NyaWJlcihzdGF0ZSkpOwogIH0gLy8gQ29tcGxldGUgYSBuYXZpZ2F0aW9uIHJldHVybmluZyB0aGUgc3RhdGUubmF2aWdhdGlvbiBiYWNrIHRvIHRoZSBJRExFX05BVklHQVRJT04KICAvLyBhbmQgc2V0dGluZyBzdGF0ZS5baGlzdG9yeUFjdGlvbi9sb2NhdGlvbi9tYXRjaGVzXSB0byB0aGUgbmV3IHJvdXRlLgogIC8vIC0gTG9jYXRpb24gaXMgYSByZXF1aXJlZCBwYXJhbQogIC8vIC0gTmF2aWdhdGlvbiB3aWxsIGFsd2F5cyBiZSBzZXQgdG8gSURMRV9OQVZJR0FUSU9OCiAgLy8gLSBDYW4gcGFzcyBhbnkgb3RoZXIgc3RhdGUgaW4gbmV3U3RhdGUKCgogIGZ1bmN0aW9uIGNvbXBsZXRlTmF2aWdhdGlvbihsb2NhdGlvbiwgbmV3U3RhdGUpIHsKICAgIHZhciBfc3RhdGUkbmF2aWdhdGlvbiRmb3I7CgogICAgLy8gRGVkdWNlIGlmIHdlJ3JlIGluIGEgbG9hZGluZy9hY3Rpb25SZWxvYWQgc3RhdGU6CiAgICAvLyAtIFdlIGhhdmUgY29tbWl0dGVkIGFjdGlvbkRhdGEgaW4gdGhlIHN0b3JlCiAgICAvLyAtIFRoZSBjdXJyZW50IG5hdmlnYXRpb24gd2FzIGEgc3VibWlzc2lvbgogICAgLy8gLSBXZSdyZSBwYXN0IHRoZSBzdWJtaXR0aW5nIHN0YXRlIGFuZCBpbnRvIHRoZSBsb2FkaW5nIHN0YXRlCiAgICAvLyAtIFRoZSBsb2NhdGlvbiB3ZSd2ZSBmaW5pc2hlZCBsb2FkaW5nIGlzIGRpZmZlcmVudCBmcm9tIHRoZSBzdWJtaXNzaW9uCiAgICAvLyAgIGxvY2F0aW9uLCBpbmRpY2F0aW5nIHdlIHJlZGlyZWN0ZWQgZnJvbSB0aGUgYWN0aW9uIChhdm9pZHMgZmFsc2UKICAgIC8vICAgcG9zaXRpdmVzIGZvciBsb2FkaW5nL3N1Ym1pc3Npb25SZWRpcmVjdCB3aGVuIGFjdGlvbkRhdGEgcmV0dXJuZWQKICAgIC8vICAgb24gYSBwcmlvciBzdWJtaXNzaW9uKQogICAgbGV0IGlzQWN0aW9uUmVsb2FkID0gc3RhdGUuYWN0aW9uRGF0YSAhPSBudWxsICYmIHN0YXRlLm5hdmlnYXRpb24uZm9ybU1ldGhvZCAhPSBudWxsICYmIHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgPT09ICJsb2FkaW5nIiAmJiAoKF9zdGF0ZSRuYXZpZ2F0aW9uJGZvciA9IHN0YXRlLm5hdmlnYXRpb24uZm9ybUFjdGlvbikgPT0gbnVsbCA/IHZvaWQgMCA6IF9zdGF0ZSRuYXZpZ2F0aW9uJGZvci5zcGxpdCgiPyIpWzBdKSA9PT0gbG9jYXRpb24ucGF0aG5hbWU7IC8vIEFsd2F5cyBwcmVzZXJ2ZSBhbnkgZXhpc3RpbmcgbG9hZGVyRGF0YSBmcm9tIHJlLXVzZWQgcm91dGVzCgogICAgbGV0IG5ld0xvYWRlckRhdGEgPSBuZXdTdGF0ZS5sb2FkZXJEYXRhID8gewogICAgICBsb2FkZXJEYXRhOiBtZXJnZUxvYWRlckRhdGEoc3RhdGUubG9hZGVyRGF0YSwgbmV3U3RhdGUubG9hZGVyRGF0YSwgbmV3U3RhdGUubWF0Y2hlcyB8fCBbXSkKICAgIH0gOiB7fTsKICAgIHVwZGF0ZVN0YXRlKF9leHRlbmRzKHt9LCBpc0FjdGlvblJlbG9hZCA/IHt9IDogewogICAgICBhY3Rpb25EYXRhOiBudWxsCiAgICB9LCBuZXdTdGF0ZSwgbmV3TG9hZGVyRGF0YSwgewogICAgICBoaXN0b3J5QWN0aW9uOiBwZW5kaW5nQWN0aW9uLAogICAgICBsb2NhdGlvbiwKICAgICAgaW5pdGlhbGl6ZWQ6IHRydWUsCiAgICAgIG5hdmlnYXRpb246IElETEVfTkFWSUdBVElPTiwKICAgICAgcmV2YWxpZGF0aW9uOiAiaWRsZSIsCiAgICAgIC8vIERvbid0IHJlc3RvcmUgb24gc3VibWlzc2lvbiBuYXZpZ2F0aW9ucwogICAgICByZXN0b3JlU2Nyb2xsUG9zaXRpb246IHN0YXRlLm5hdmlnYXRpb24uZm9ybURhdGEgPyBmYWxzZSA6IGdldFNhdmVkU2Nyb2xsUG9zaXRpb24obG9jYXRpb24sIG5ld1N0YXRlLm1hdGNoZXMgfHwgc3RhdGUubWF0Y2hlcyksCiAgICAgIHByZXZlbnRTY3JvbGxSZXNldDogcGVuZGluZ1ByZXZlbnRTY3JvbGxSZXNldAogICAgfSkpOwoKICAgIGlmIChpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24pIDsgZWxzZSBpZiAocGVuZGluZ0FjdGlvbiA9PT0gQWN0aW9uLlBvcCkgOyBlbHNlIGlmIChwZW5kaW5nQWN0aW9uID09PSBBY3Rpb24uUHVzaCkgewogICAgICBpbml0Lmhpc3RvcnkucHVzaChsb2NhdGlvbiwgbG9jYXRpb24uc3RhdGUpOwogICAgfSBlbHNlIGlmIChwZW5kaW5nQWN0aW9uID09PSBBY3Rpb24uUmVwbGFjZSkgewogICAgICBpbml0Lmhpc3RvcnkucmVwbGFjZShsb2NhdGlvbiwgbG9jYXRpb24uc3RhdGUpOwogICAgfSAvLyBSZXNldCBzdGF0ZWZ1bCBuYXZpZ2F0aW9uIHZhcnMKCgogICAgcGVuZGluZ0FjdGlvbiA9IEFjdGlvbi5Qb3A7CiAgICBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gZmFsc2U7CiAgICBpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24gPSBmYWxzZTsKICAgIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQgPSBmYWxzZTsKICAgIGNhbmNlbGxlZERlZmVycmVkUm91dGVzID0gW107CiAgICBjYW5jZWxsZWRGZXRjaGVyTG9hZHMgPSBbXTsKICB9IC8vIFRyaWdnZXIgYSBuYXZpZ2F0aW9uIGV2ZW50LCB3aGljaCBjYW4gZWl0aGVyIGJlIGEgbnVtZXJpY2FsIFBPUCBvciBhIFBVU0gKICAvLyByZXBsYWNlIHdpdGggYW4gb3B0aW9uYWwgc3VibWlzc2lvbgoKCiAgYXN5bmMgZnVuY3Rpb24gbmF2aWdhdGUodG8sIG9wdHMpIHsKICAgIGlmICh0eXBlb2YgdG8gPT09ICJudW1iZXIiKSB7CiAgICAgIGluaXQuaGlzdG9yeS5nbyh0byk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBsZXQgewogICAgICBwYXRoLAogICAgICBzdWJtaXNzaW9uLAogICAgICBlcnJvcgogICAgfSA9IG5vcm1hbGl6ZU5hdmlnYXRlT3B0aW9ucyh0bywgb3B0cyk7CiAgICBsZXQgbG9jYXRpb24gPSBjcmVhdGVMb2NhdGlvbihzdGF0ZS5sb2NhdGlvbiwgcGF0aCwgb3B0cyAmJiBvcHRzLnN0YXRlKTsgLy8gV2hlbiB1c2luZyBuYXZpZ2F0ZSBhcyBhIFBVU0gvUkVQTEFDRSB3ZSBhcmVuJ3QgcmVhZGluZyBhbiBhbHJlYWR5LWVuY29kZWQKICAgIC8vIFVSTCBmcm9tIHdpbmRvdy5sb2NhdGlvbiwgc28gd2UgbmVlZCB0byBlbmNvZGUgaXQgaGVyZSBzbyB0aGUgYmVoYXZpb3IKICAgIC8vIHJlbWFpbnMgdGhlIHNhbWUgYXMgUE9QIGFuZCBub24tZGF0YS1yb3V0ZXIgdXNhZ2VzLiAgbmV3IFVSTCgpIGRvZXMgYWxsCiAgICAvLyB0aGUgc2FtZSBlbmNvZGluZyB3ZSdkIGdldCBmcm9tIGEgaGlzdG9yeS5wdXNoU3RhdGUvd2luZG93LmxvY2F0aW9uIHJlYWQKICAgIC8vIHdpdGhvdXQgaGF2aW5nIHRvIHRvdWNoIGhpc3RvcnkKCiAgICBsb2NhdGlvbiA9IGluaXQuaGlzdG9yeS5lbmNvZGVMb2NhdGlvbihsb2NhdGlvbik7CiAgICBsZXQgaGlzdG9yeUFjdGlvbiA9IChvcHRzICYmIG9wdHMucmVwbGFjZSkgPT09IHRydWUgfHwgc3VibWlzc2lvbiAhPSBudWxsID8gQWN0aW9uLlJlcGxhY2UgOiBBY3Rpb24uUHVzaDsKICAgIGxldCBwcmV2ZW50U2Nyb2xsUmVzZXQgPSBvcHRzICYmICJwcmV2ZW50U2Nyb2xsUmVzZXQiIGluIG9wdHMgPyBvcHRzLnByZXZlbnRTY3JvbGxSZXNldCA9PT0gdHJ1ZSA6IHVuZGVmaW5lZDsKICAgIHJldHVybiBhd2FpdCBzdGFydE5hdmlnYXRpb24oaGlzdG9yeUFjdGlvbiwgbG9jYXRpb24sIHsKICAgICAgc3VibWlzc2lvbiwKICAgICAgLy8gU2VuZCB0aHJvdWdoIHRoZSBmb3JtRGF0YSBzZXJpYWxpemF0aW9uIGVycm9yIGlmIHdlIGhhdmUgb25lIHNvIHdlIGNhbgogICAgICAvLyByZW5kZXIgYXQgdGhlIHJpZ2h0IGVycm9yIGJvdW5kYXJ5IGFmdGVyIHdlIG1hdGNoIHJvdXRlcwogICAgICBwZW5kaW5nRXJyb3I6IGVycm9yLAogICAgICBwcmV2ZW50U2Nyb2xsUmVzZXQsCiAgICAgIHJlcGxhY2U6IG9wdHMgJiYgb3B0cy5yZXBsYWNlCiAgICB9KTsKICB9IC8vIFJldmFsaWRhdGUgYWxsIGN1cnJlbnQgbG9hZGVycy4gIElmIGEgbmF2aWdhdGlvbiBpcyBpbiBwcm9ncmVzcyBvciBpZiB0aGlzCiAgLy8gaXMgaW50ZXJydXB0ZWQgYnkgYSBuYXZpZ2F0aW9uLCBhbGxvdyB0aGlzIHRvICJzdWNjZWVkIiBieSBjYWxsaW5nIGFsbAogIC8vIGxvYWRlcnMgZHVyaW5nIHRoZSBuZXh0IGxvYWRlciByb3VuZAoKCiAgZnVuY3Rpb24gcmV2YWxpZGF0ZSgpIHsKICAgIGludGVycnVwdEFjdGl2ZUxvYWRzKCk7CiAgICB1cGRhdGVTdGF0ZSh7CiAgICAgIHJldmFsaWRhdGlvbjogImxvYWRpbmciCiAgICB9KTsgLy8gSWYgd2UncmUgY3VycmVudGx5IHN1Ym1pdHRpbmcgYW4gYWN0aW9uLCB3ZSBkb24ndCBuZWVkIHRvIHN0YXJ0IGEgbmV3CiAgICAvLyBuYXZpZ2F0aW9uLCB3ZSdsbCBqdXN0IGxldCB0aGUgZm9sbG93IHVwIGxvYWRlciBleGVjdXRpb24gY2FsbCBhbGwgbG9hZGVycwoKICAgIGlmIChzdGF0ZS5uYXZpZ2F0aW9uLnN0YXRlID09PSAic3VibWl0dGluZyIpIHsKICAgICAgcmV0dXJuOwogICAgfSAvLyBJZiB3ZSdyZSBjdXJyZW50bHkgaW4gYW4gaWRsZSBzdGF0ZSwgc3RhcnQgYSBuZXcgbmF2aWdhdGlvbiBmb3IgdGhlIGN1cnJlbnQKICAgIC8vIGFjdGlvbi9sb2NhdGlvbiBhbmQgbWFyayBpdCBhcyB1bmludGVycnVwdGVkLCB3aGljaCB3aWxsIHNraXAgdGhlIGhpc3RvcnkKICAgIC8vIHVwZGF0ZSBpbiBjb21wbGV0ZU5hdmlnYXRpb24KCgogICAgaWYgKHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgPT09ICJpZGxlIikgewogICAgICBzdGFydE5hdmlnYXRpb24oc3RhdGUuaGlzdG9yeUFjdGlvbiwgc3RhdGUubG9jYXRpb24sIHsKICAgICAgICBzdGFydFVuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb246IHRydWUKICAgICAgfSk7CiAgICAgIHJldHVybjsKICAgIH0gLy8gT3RoZXJ3aXNlLCBpZiB3ZSdyZSBjdXJyZW50bHkgaW4gYSBsb2FkaW5nIHN0YXRlLCBqdXN0IHN0YXJ0IGEgbmV3CiAgICAvLyBuYXZpZ2F0aW9uIHRvIHRoZSBuYXZpZ2F0aW9uLmxvY2F0aW9uIGJ1dCBkbyBub3QgdHJpZ2dlciBhbiB1bmludGVycnVwdGVkCiAgICAvLyByZXZhbGlkYXRpb24gc28gdGhhdCBoaXN0b3J5IGNvcnJlY3RseSB1cGRhdGVzIG9uY2UgdGhlIG5hdmlnYXRpb24gY29tcGxldGVzCgoKICAgIHN0YXJ0TmF2aWdhdGlvbihwZW5kaW5nQWN0aW9uIHx8IHN0YXRlLmhpc3RvcnlBY3Rpb24sIHN0YXRlLm5hdmlnYXRpb24ubG9jYXRpb24sIHsKICAgICAgb3ZlcnJpZGVOYXZpZ2F0aW9uOiBzdGF0ZS5uYXZpZ2F0aW9uCiAgICB9KTsKICB9IC8vIFN0YXJ0IGEgbmF2aWdhdGlvbiB0byB0aGUgZ2l2ZW4gYWN0aW9uL2xvY2F0aW9uLiAgQ2FuIG9wdGlvbmFsbHkgcHJvdmlkZSBhCiAgLy8gb3ZlcnJpZGVOYXZpZ2F0aW9uIHdoaWNoIHdpbGwgb3ZlcnJpZGUgdGhlIG5vcm1hbExvYWQgaW4gdGhlIGNhc2Ugb2YgYSByZWRpcmVjdAogIC8vIG5hdmlnYXRpb24KCgogIGFzeW5jIGZ1bmN0aW9uIHN0YXJ0TmF2aWdhdGlvbihoaXN0b3J5QWN0aW9uLCBsb2NhdGlvbiwgb3B0cykgewogICAgLy8gQWJvcnQgYW55IGluLXByb2dyZXNzIG5hdmlnYXRpb25zIGFuZCBzdGFydCBhIG5ldyBvbmUuIFVuc2V0IGFueSBvbmdvaW5nCiAgICAvLyB1bmludGVycnVwdGVkIHJldmFsaWRhdGlvbnMgdW5sZXNzIHRvbGQgb3RoZXJ3aXNlLCBzaW5jZSB3ZSB3YW50IHRoaXMKICAgIC8vIG5ldyBuYXZpZ2F0aW9uIHRvIHVwZGF0ZSBoaXN0b3J5IG5vcm1hbGx5CiAgICBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIgJiYgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyLmFib3J0KCk7CiAgICBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIgPSBudWxsOwogICAgcGVuZGluZ0FjdGlvbiA9IGhpc3RvcnlBY3Rpb247CiAgICBpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24gPSAob3B0cyAmJiBvcHRzLnN0YXJ0VW5pbnRlcnJ1cHRlZFJldmFsaWRhdGlvbikgPT09IHRydWU7IC8vIFNhdmUgdGhlIGN1cnJlbnQgc2Nyb2xsIHBvc2l0aW9uIGV2ZXJ5IHRpbWUgd2Ugc3RhcnQgYSBuZXcgbmF2aWdhdGlvbiwKICAgIC8vIGFuZCB0cmFjayB3aGV0aGVyIHdlIHNob3VsZCByZXNldCBzY3JvbGwgb24gY29tcGxldGlvbgoKICAgIHNhdmVTY3JvbGxQb3NpdGlvbihzdGF0ZS5sb2NhdGlvbiwgc3RhdGUubWF0Y2hlcyk7CiAgICBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gKG9wdHMgJiYgb3B0cy5wcmV2ZW50U2Nyb2xsUmVzZXQpID09PSB0cnVlOwogICAgbGV0IGxvYWRpbmdOYXZpZ2F0aW9uID0gb3B0cyAmJiBvcHRzLm92ZXJyaWRlTmF2aWdhdGlvbjsKICAgIGxldCBtYXRjaGVzID0gbWF0Y2hSb3V0ZXMoZGF0YVJvdXRlcywgbG9jYXRpb24sIGluaXQuYmFzZW5hbWUpOyAvLyBTaG9ydCBjaXJjdWl0IHdpdGggYSA0MDQgb24gdGhlIHJvb3QgZXJyb3IgYm91bmRhcnkgaWYgd2UgbWF0Y2ggbm90aGluZwoKICAgIGlmICghbWF0Y2hlcykgewogICAgICBsZXQgewogICAgICAgIG1hdGNoZXM6IG5vdEZvdW5kTWF0Y2hlcywKICAgICAgICByb3V0ZSwKICAgICAgICBlcnJvcgogICAgICB9ID0gZ2V0Tm90Rm91bmRNYXRjaGVzKGRhdGFSb3V0ZXMpOyAvLyBDYW5jZWwgYWxsIHBlbmRpbmcgZGVmZXJyZWQgb24gNDA0cyBzaW5jZSB3ZSBkb24ndCBrZWVwIGFueSByb3V0ZXMKCiAgICAgIGNhbmNlbEFjdGl2ZURlZmVycmVkcygpOwogICAgICBjb21wbGV0ZU5hdmlnYXRpb24obG9jYXRpb24sIHsKICAgICAgICBtYXRjaGVzOiBub3RGb3VuZE1hdGNoZXMsCiAgICAgICAgbG9hZGVyRGF0YToge30sCiAgICAgICAgZXJyb3JzOiB7CiAgICAgICAgICBbcm91dGUuaWRdOiBlcnJvcgogICAgICAgIH0KICAgICAgfSk7CiAgICAgIHJldHVybjsKICAgIH0gLy8gU2hvcnQgY2lyY3VpdCBpZiBpdCdzIG9ubHkgYSBoYXNoIGNoYW5nZQoKCiAgICBpZiAoaXNIYXNoQ2hhbmdlT25seShzdGF0ZS5sb2NhdGlvbiwgbG9jYXRpb24pKSB7CiAgICAgIGNvbXBsZXRlTmF2aWdhdGlvbihsb2NhdGlvbiwgewogICAgICAgIG1hdGNoZXMKICAgICAgfSk7CiAgICAgIHJldHVybjsKICAgIH0gLy8gQ3JlYXRlIGEgY29udHJvbGxlci9SZXF1ZXN0IGZvciB0aGlzIG5hdmlnYXRpb24KCgogICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpOwogICAgbGV0IHJlcXVlc3QgPSBjcmVhdGVSZXF1ZXN0KGxvY2F0aW9uLCBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIuc2lnbmFsLCBvcHRzICYmIG9wdHMuc3VibWlzc2lvbik7CiAgICBsZXQgcGVuZGluZ0FjdGlvbkRhdGE7CiAgICBsZXQgcGVuZGluZ0Vycm9yOwoKICAgIGlmIChvcHRzICYmIG9wdHMucGVuZGluZ0Vycm9yKSB7CiAgICAgIC8vIElmIHdlIGhhdmUgYSBwZW5kaW5nRXJyb3IsIGl0IG1lYW5zIHRoZSB1c2VyIGF0dGVtcHRlZCBhIEdFVCBzdWJtaXNzaW9uCiAgICAgIC8vIHdpdGggYmluYXJ5IEZvcm1EYXRhIHNvIGFzc2lnbiBoZXJlIGFuZCBza2lwIHRvIGhhbmRsZUxvYWRlcnMuICBUaGF0CiAgICAgIC8vIHdheSB3ZSBoYW5kbGUgY2FsbGluZyBsb2FkZXJzIGFib3ZlIHRoZSBib3VuZGFyeSBldGMuICBJdCdzIG5vdCByZWFsbHkKICAgICAgLy8gZGlmZmVyZW50IGZyb20gYW4gYWN0aW9uRXJyb3IgaW4gdGhhdCBzZW5zZS4KICAgICAgcGVuZGluZ0Vycm9yID0gewogICAgICAgIFtmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMpLnJvdXRlLmlkXTogb3B0cy5wZW5kaW5nRXJyb3IKICAgICAgfTsKICAgIH0gZWxzZSBpZiAob3B0cyAmJiBvcHRzLnN1Ym1pc3Npb24pIHsKICAgICAgLy8gQ2FsbCBhY3Rpb24gaWYgd2UgcmVjZWl2ZWQgYW4gYWN0aW9uIHN1Ym1pc3Npb24KICAgICAgbGV0IGFjdGlvbk91dHB1dCA9IGF3YWl0IGhhbmRsZUFjdGlvbihyZXF1ZXN0LCBsb2NhdGlvbiwgb3B0cy5zdWJtaXNzaW9uLCBtYXRjaGVzLCB7CiAgICAgICAgcmVwbGFjZTogb3B0cy5yZXBsYWNlCiAgICAgIH0pOwoKICAgICAgaWYgKGFjdGlvbk91dHB1dC5zaG9ydENpcmN1aXRlZCkgewogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgcGVuZGluZ0FjdGlvbkRhdGEgPSBhY3Rpb25PdXRwdXQucGVuZGluZ0FjdGlvbkRhdGE7CiAgICAgIHBlbmRpbmdFcnJvciA9IGFjdGlvbk91dHB1dC5wZW5kaW5nQWN0aW9uRXJyb3I7CgogICAgICBsZXQgbmF2aWdhdGlvbiA9IF9leHRlbmRzKHsKICAgICAgICBzdGF0ZTogImxvYWRpbmciLAogICAgICAgIGxvY2F0aW9uCiAgICAgIH0sIG9wdHMuc3VibWlzc2lvbik7CgogICAgICBsb2FkaW5nTmF2aWdhdGlvbiA9IG5hdmlnYXRpb247CiAgICB9IC8vIENhbGwgbG9hZGVycwoKCiAgICBsZXQgewogICAgICBzaG9ydENpcmN1aXRlZCwKICAgICAgbG9hZGVyRGF0YSwKICAgICAgZXJyb3JzCiAgICB9ID0gYXdhaXQgaGFuZGxlTG9hZGVycyhyZXF1ZXN0LCBsb2NhdGlvbiwgbWF0Y2hlcywgbG9hZGluZ05hdmlnYXRpb24sIG9wdHMgJiYgb3B0cy5zdWJtaXNzaW9uLCBvcHRzICYmIG9wdHMucmVwbGFjZSwgcGVuZGluZ0FjdGlvbkRhdGEsIHBlbmRpbmdFcnJvcik7CgogICAgaWYgKHNob3J0Q2lyY3VpdGVkKSB7CiAgICAgIHJldHVybjsKICAgIH0gLy8gQ2xlYW4gdXAgbm93IHRoYXQgdGhlIGFjdGlvbi9sb2FkZXJzIGhhdmUgY29tcGxldGVkLiAgRG9uJ3QgY2xlYW4gdXAgaWYKICAgIC8vIHdlIHNob3J0IGNpcmN1aXRlZCBiZWNhdXNlIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciB3aWxsIGhhdmUgYWxyZWFkeQogICAgLy8gYmVlbiBhc3NpZ25lZCB0byBhIG5ldyBjb250cm9sbGVyIGZvciB0aGUgbmV4dCBuYXZpZ2F0aW9uCgoKICAgIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciA9IG51bGw7CiAgICBjb21wbGV0ZU5hdmlnYXRpb24obG9jYXRpb24sIHsKICAgICAgbWF0Y2hlcywKICAgICAgbG9hZGVyRGF0YSwKICAgICAgZXJyb3JzCiAgICB9KTsKICB9IC8vIENhbGwgdGhlIGFjdGlvbiBtYXRjaGVkIGJ5IHRoZSBsZWFmIHJvdXRlIGZvciB0aGlzIG5hdmlnYXRpb24gYW5kIGhhbmRsZQogIC8vIHJlZGlyZWN0cy9lcnJvcnMKCgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUFjdGlvbihyZXF1ZXN0LCBsb2NhdGlvbiwgc3VibWlzc2lvbiwgbWF0Y2hlcywgb3B0cykgewogICAgaW50ZXJydXB0QWN0aXZlTG9hZHMoKTsgLy8gUHV0IHVzIGluIGEgc3VibWl0dGluZyBzdGF0ZQoKICAgIGxldCBuYXZpZ2F0aW9uID0gX2V4dGVuZHMoewogICAgICBzdGF0ZTogInN1Ym1pdHRpbmciLAogICAgICBsb2NhdGlvbgogICAgfSwgc3VibWlzc2lvbik7CgogICAgdXBkYXRlU3RhdGUoewogICAgICBuYXZpZ2F0aW9uCiAgICB9KTsgLy8gQ2FsbCBvdXIgYWN0aW9uIGFuZCBnZXQgdGhlIHJlc3VsdAoKICAgIGxldCByZXN1bHQ7CiAgICBsZXQgYWN0aW9uTWF0Y2ggPSBnZXRUYXJnZXRNYXRjaChtYXRjaGVzLCBsb2NhdGlvbik7CgogICAgaWYgKCFhY3Rpb25NYXRjaC5yb3V0ZS5hY3Rpb24pIHsKICAgICAgcmVzdWx0ID0gZ2V0TWV0aG9kTm90QWxsb3dlZFJlc3VsdChsb2NhdGlvbik7CiAgICB9IGVsc2UgewogICAgICByZXN1bHQgPSBhd2FpdCBjYWxsTG9hZGVyT3JBY3Rpb24oImFjdGlvbiIsIHJlcXVlc3QsIGFjdGlvbk1hdGNoLCBtYXRjaGVzLCByb3V0ZXIuYmFzZW5hbWUpOwoKICAgICAgaWYgKHJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHsKICAgICAgICByZXR1cm4gewogICAgICAgICAgc2hvcnRDaXJjdWl0ZWQ6IHRydWUKICAgICAgICB9OwogICAgICB9CiAgICB9CgogICAgaWYgKGlzUmVkaXJlY3RSZXN1bHQocmVzdWx0KSkgewogICAgICBsZXQgcmVkaXJlY3ROYXZpZ2F0aW9uID0gX2V4dGVuZHMoewogICAgICAgIHN0YXRlOiAibG9hZGluZyIsCiAgICAgICAgbG9jYXRpb246IGNyZWF0ZUxvY2F0aW9uKHN0YXRlLmxvY2F0aW9uLCByZXN1bHQubG9jYXRpb24pCiAgICAgIH0sIHN1Ym1pc3Npb24pOwoKICAgICAgYXdhaXQgc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24ocmVzdWx0LCByZWRpcmVjdE5hdmlnYXRpb24sIG9wdHMgJiYgb3B0cy5yZXBsYWNlKTsKICAgICAgcmV0dXJuIHsKICAgICAgICBzaG9ydENpcmN1aXRlZDogdHJ1ZQogICAgICB9OwogICAgfQoKICAgIGlmIChpc0Vycm9yUmVzdWx0KHJlc3VsdCkpIHsKICAgICAgLy8gU3RvcmUgb2ZmIHRoZSBwZW5kaW5nIGVycm9yIC0gd2UgdXNlIGl0IHRvIGRldGVybWluZSB3aGljaCBsb2FkZXJzCiAgICAgIC8vIHRvIGNhbGwgYW5kIHdpbGwgY29tbWl0IGl0IHdoZW4gd2UgY29tcGxldGUgdGhlIG5hdmlnYXRpb24KICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIGFjdGlvbk1hdGNoLnJvdXRlLmlkKTsgLy8gQnkgZGVmYXVsdCwgYWxsIHN1Ym1pc3Npb25zIGFyZSBSRVBMQUNFIG5hdmlnYXRpb25zLCBidXQgaWYgdGhlCiAgICAgIC8vIGFjdGlvbiB0aHJldyBhbiBlcnJvciB0aGF0J2xsIGJlIHJlbmRlcmVkIGluIGFuIGVycm9yRWxlbWVudCwgd2UgZmFsbAogICAgICAvLyBiYWNrIHRvIFBVU0ggc28gdGhhdCB0aGUgdXNlciBjYW4gdXNlIHRoZSBiYWNrIGJ1dHRvbiB0byBnZXQgYmFjayB0bwogICAgICAvLyB0aGUgcHJlLXN1Ym1pc3Npb24gZm9ybSBsb2NhdGlvbiB0byB0cnkgYWdhaW4KCiAgICAgIGlmICgob3B0cyAmJiBvcHRzLnJlcGxhY2UpICE9PSB0cnVlKSB7CiAgICAgICAgcGVuZGluZ0FjdGlvbiA9IEFjdGlvbi5QdXNoOwogICAgICB9CgogICAgICByZXR1cm4gewogICAgICAgIHBlbmRpbmdBY3Rpb25FcnJvcjogewogICAgICAgICAgW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZXJyb3IKICAgICAgICB9CiAgICAgIH07CiAgICB9CgogICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoImRlZmVyKCkgaXMgbm90IHN1cHBvcnRlZCBpbiBhY3Rpb25zIik7CiAgICB9CgogICAgcmV0dXJuIHsKICAgICAgcGVuZGluZ0FjdGlvbkRhdGE6IHsKICAgICAgICBbYWN0aW9uTWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZGF0YQogICAgICB9CiAgICB9OwogIH0gLy8gQ2FsbCBhbGwgYXBwbGljYWJsZSBsb2FkZXJzIGZvciB0aGUgZ2l2ZW4gbWF0Y2hlcywgaGFuZGxpbmcgcmVkaXJlY3RzLAogIC8vIGVycm9ycywgZXRjLgoKCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlTG9hZGVycyhyZXF1ZXN0LCBsb2NhdGlvbiwgbWF0Y2hlcywgb3ZlcnJpZGVOYXZpZ2F0aW9uLCBzdWJtaXNzaW9uLCByZXBsYWNlLCBwZW5kaW5nQWN0aW9uRGF0YSwgcGVuZGluZ0Vycm9yKSB7CiAgICAvLyBGaWd1cmUgb3V0IHRoZSByaWdodCBuYXZpZ2F0aW9uIHdlIHdhbnQgdG8gdXNlIGZvciBkYXRhIGxvYWRpbmcKICAgIGxldCBsb2FkaW5nTmF2aWdhdGlvbiA9IG92ZXJyaWRlTmF2aWdhdGlvbjsKCiAgICBpZiAoIWxvYWRpbmdOYXZpZ2F0aW9uKSB7CiAgICAgIGxldCBuYXZpZ2F0aW9uID0gewogICAgICAgIHN0YXRlOiAibG9hZGluZyIsCiAgICAgICAgbG9jYXRpb24sCiAgICAgICAgZm9ybU1ldGhvZDogdW5kZWZpbmVkLAogICAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICAgICAgICBmb3JtRW5jVHlwZTogdW5kZWZpbmVkLAogICAgICAgIGZvcm1EYXRhOiB1bmRlZmluZWQKICAgICAgfTsKICAgICAgbG9hZGluZ05hdmlnYXRpb24gPSBuYXZpZ2F0aW9uOwogICAgfQoKICAgIGxldCBbbWF0Y2hlc1RvTG9hZCwgcmV2YWxpZGF0aW5nRmV0Y2hlcnNdID0gZ2V0TWF0Y2hlc1RvTG9hZChzdGF0ZSwgbWF0Y2hlcywgc3VibWlzc2lvbiwgbG9jYXRpb24sIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQsIGNhbmNlbGxlZERlZmVycmVkUm91dGVzLCBjYW5jZWxsZWRGZXRjaGVyTG9hZHMsIHBlbmRpbmdBY3Rpb25EYXRhLCBwZW5kaW5nRXJyb3IsIGZldGNoTG9hZE1hdGNoZXMpOyAvLyBDYW5jZWwgcGVuZGluZyBkZWZlcnJlZHMgZm9yIG5vLWxvbmdlci1tYXRjaGVkIHJvdXRlcyBvciByb3V0ZXMgd2UncmUKICAgIC8vIGFib3V0IHRvIHJlbG9hZC4gIE5vdGUgdGhhdCBpZiB0aGlzIGlzIGFuIGFjdGlvbiByZWxvYWQgd2Ugd291bGQgaGF2ZQogICAgLy8gYWxyZWFkeSBjYW5jZWxsZWQgYWxsIHBlbmRpbmcgZGVmZXJyZWRzIHNvIHRoaXMgd291bGQgYmUgYSBuby1vcAoKICAgIGNhbmNlbEFjdGl2ZURlZmVycmVkcyhyb3V0ZUlkID0+ICEobWF0Y2hlcyAmJiBtYXRjaGVzLnNvbWUobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSkgfHwgbWF0Y2hlc1RvTG9hZCAmJiBtYXRjaGVzVG9Mb2FkLnNvbWUobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSk7IC8vIFNob3J0IGNpcmN1aXQgaWYgd2UgaGF2ZSBubyBsb2FkZXJzIHRvIHJ1bgoKICAgIGlmIChtYXRjaGVzVG9Mb2FkLmxlbmd0aCA9PT0gMCAmJiByZXZhbGlkYXRpbmdGZXRjaGVycy5sZW5ndGggPT09IDApIHsKICAgICAgY29tcGxldGVOYXZpZ2F0aW9uKGxvY2F0aW9uLCB7CiAgICAgICAgbWF0Y2hlcywKICAgICAgICBsb2FkZXJEYXRhOiBtZXJnZUxvYWRlckRhdGEoc3RhdGUubG9hZGVyRGF0YSwge30sIG1hdGNoZXMpLAogICAgICAgIC8vIENvbW1pdCBwZW5kaW5nIGVycm9yIGlmIHdlJ3JlIHNob3J0IGNpcmN1aXRpbmcKICAgICAgICBlcnJvcnM6IHBlbmRpbmdFcnJvciB8fCBudWxsLAogICAgICAgIGFjdGlvbkRhdGE6IHBlbmRpbmdBY3Rpb25EYXRhIHx8IG51bGwKICAgICAgfSk7CiAgICAgIHJldHVybiB7CiAgICAgICAgc2hvcnRDaXJjdWl0ZWQ6IHRydWUKICAgICAgfTsKICAgIH0gLy8gSWYgdGhpcyBpcyBhbiB1bmludGVycnVwdGVkIHJldmFsaWRhdGlvbiwgd2UgcmVtYWluIGluIG91ciBjdXJyZW50IGlkbGUKICAgIC8vIHN0YXRlLiAgSWYgbm90LCB3ZSBuZWVkIHRvIHN3aXRjaCB0byBvdXIgbG9hZGluZyBzdGF0ZSBhbmQgbG9hZCBkYXRhLAogICAgLy8gcHJlc2VydmluZyBhbnkgbmV3IGFjdGlvbiBkYXRhIG9yIGV4aXN0aW5nIGFjdGlvbiBkYXRhIChpbiB0aGUgY2FzZSBvZgogICAgLy8gYSByZXZhbGlkYXRpb24gaW50ZXJydXB0aW5nIGFuIGFjdGlvblJlbG9hZCkKCgogICAgaWYgKCFpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24pIHsKICAgICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZm9yRWFjaChfcmVmMiA9PiB7CiAgICAgICAgbGV0IFtrZXldID0gX3JlZjI7CiAgICAgICAgbGV0IGZldGNoZXIgPSBzdGF0ZS5mZXRjaGVycy5nZXQoa2V5KTsKICAgICAgICBsZXQgcmV2YWxpZGF0aW5nRmV0Y2hlciA9IHsKICAgICAgICAgIHN0YXRlOiAibG9hZGluZyIsCiAgICAgICAgICBkYXRhOiBmZXRjaGVyICYmIGZldGNoZXIuZGF0YSwKICAgICAgICAgIGZvcm1NZXRob2Q6IHVuZGVmaW5lZCwKICAgICAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICAgICAgICAgIGZvcm1FbmNUeXBlOiB1bmRlZmluZWQsCiAgICAgICAgICBmb3JtRGF0YTogdW5kZWZpbmVkCiAgICAgICAgfTsKICAgICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCByZXZhbGlkYXRpbmdGZXRjaGVyKTsKICAgICAgfSk7CiAgICAgIHVwZGF0ZVN0YXRlKF9leHRlbmRzKHsKICAgICAgICBuYXZpZ2F0aW9uOiBsb2FkaW5nTmF2aWdhdGlvbiwKICAgICAgICBhY3Rpb25EYXRhOiBwZW5kaW5nQWN0aW9uRGF0YSB8fCBzdGF0ZS5hY3Rpb25EYXRhIHx8IG51bGwKICAgICAgfSwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMubGVuZ3RoID4gMCA/IHsKICAgICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycykKICAgICAgfSA6IHt9KSk7CiAgICB9CgogICAgcGVuZGluZ05hdmlnYXRpb25Mb2FkSWQgPSArK2luY3JlbWVudGluZ0xvYWRJZDsKICAgIHJldmFsaWRhdGluZ0ZldGNoZXJzLmZvckVhY2goX3JlZjMgPT4gewogICAgICBsZXQgW2tleV0gPSBfcmVmMzsKICAgICAgcmV0dXJuIGZldGNoQ29udHJvbGxlcnMuc2V0KGtleSwgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyKTsKICAgIH0pOwogICAgbGV0IHsKICAgICAgcmVzdWx0cywKICAgICAgbG9hZGVyUmVzdWx0cywKICAgICAgZmV0Y2hlclJlc3VsdHMKICAgIH0gPSBhd2FpdCBjYWxsTG9hZGVyc0FuZE1heWJlUmVzb2x2ZURhdGEoc3RhdGUubWF0Y2hlcywgbWF0Y2hlcywgbWF0Y2hlc1RvTG9hZCwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMsIHJlcXVlc3QpOwoKICAgIGlmIChyZXF1ZXN0LnNpZ25hbC5hYm9ydGVkKSB7CiAgICAgIHJldHVybiB7CiAgICAgICAgc2hvcnRDaXJjdWl0ZWQ6IHRydWUKICAgICAgfTsKICAgIH0gLy8gQ2xlYW4gdXAgX2FmdGVyXyBsb2FkZXJzIGhhdmUgY29tcGxldGVkLiAgRG9uJ3QgY2xlYW4gdXAgaWYgd2Ugc2hvcnQKICAgIC8vIGNpcmN1aXRlZCBiZWNhdXNlIGZldGNoQ29udHJvbGxlcnMgd291bGQgaGF2ZSBiZWVuIGFib3J0ZWQgYW5kCiAgICAvLyByZWFzc2lnbmVkIHRvIG5ldyBjb250cm9sbGVycyBmb3IgdGhlIG5leHQgbmF2aWdhdGlvbgoKCiAgICByZXZhbGlkYXRpbmdGZXRjaGVycy5mb3JFYWNoKF9yZWY0ID0+IHsKICAgICAgbGV0IFtrZXldID0gX3JlZjQ7CiAgICAgIHJldHVybiBmZXRjaENvbnRyb2xsZXJzLmRlbGV0ZShrZXkpOwogICAgfSk7IC8vIElmIGFueSBsb2FkZXJzIHJldHVybmVkIGEgcmVkaXJlY3QgUmVzcG9uc2UsIHN0YXJ0IGEgbmV3IFJFUExBQ0UgbmF2aWdhdGlvbgoKICAgIGxldCByZWRpcmVjdCA9IGZpbmRSZWRpcmVjdChyZXN1bHRzKTsKCiAgICBpZiAocmVkaXJlY3QpIHsKICAgICAgbGV0IHJlZGlyZWN0TmF2aWdhdGlvbiA9IGdldExvYWRlclJlZGlyZWN0KHN0YXRlLCByZWRpcmVjdCk7CiAgICAgIGF3YWl0IHN0YXJ0UmVkaXJlY3ROYXZpZ2F0aW9uKHJlZGlyZWN0LCByZWRpcmVjdE5hdmlnYXRpb24sIHJlcGxhY2UpOwogICAgICByZXR1cm4gewogICAgICAgIHNob3J0Q2lyY3VpdGVkOiB0cnVlCiAgICAgIH07CiAgICB9IC8vIFByb2Nlc3MgYW5kIGNvbW1pdCBvdXRwdXQgZnJvbSBsb2FkZXJzCgoKICAgIGxldCB7CiAgICAgIGxvYWRlckRhdGEsCiAgICAgIGVycm9ycwogICAgfSA9IHByb2Nlc3NMb2FkZXJEYXRhKHN0YXRlLCBtYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCBsb2FkZXJSZXN1bHRzLCBwZW5kaW5nRXJyb3IsIHJldmFsaWRhdGluZ0ZldGNoZXJzLCBmZXRjaGVyUmVzdWx0cywgYWN0aXZlRGVmZXJyZWRzKTsgLy8gV2lyZSB1cCBzdWJzY3JpYmVycyB0byB1cGRhdGUgbG9hZGVyRGF0YSBhcyBwcm9taXNlcyBzZXR0bGUKCiAgICBhY3RpdmVEZWZlcnJlZHMuZm9yRWFjaCgoZGVmZXJyZWREYXRhLCByb3V0ZUlkKSA9PiB7CiAgICAgIGRlZmVycmVkRGF0YS5zdWJzY3JpYmUoYWJvcnRlZCA9PiB7CiAgICAgICAgLy8gTm90ZTogTm8gbmVlZCB0byB1cGRhdGVTdGF0ZSBoZXJlIHNpbmNlIHRoZSBUcmFja2VkUHJvbWlzZSBvbgogICAgICAgIC8vIGxvYWRlckRhdGEgaXMgc3RhYmxlIGFjcm9zcyByZXNvbHZlL3JlamVjdAogICAgICAgIC8vIFJlbW92ZSB0aGlzIGluc3RhbmNlIGlmIHdlIHdlcmUgYWJvcnRlZCBvciBpZiBwcm9taXNlcyBoYXZlIHNldHRsZWQKICAgICAgICBpZiAoYWJvcnRlZCB8fCBkZWZlcnJlZERhdGEuZG9uZSkgewogICAgICAgICAgYWN0aXZlRGVmZXJyZWRzLmRlbGV0ZShyb3V0ZUlkKTsKICAgICAgICB9CiAgICAgIH0pOwogICAgfSk7CiAgICBtYXJrRmV0Y2hSZWRpcmVjdHNEb25lKCk7CiAgICBsZXQgZGlkQWJvcnRGZXRjaExvYWRzID0gYWJvcnRTdGFsZUZldGNoTG9hZHMocGVuZGluZ05hdmlnYXRpb25Mb2FkSWQpOwogICAgcmV0dXJuIF9leHRlbmRzKHsKICAgICAgbG9hZGVyRGF0YSwKICAgICAgZXJyb3JzCiAgICB9LCBkaWRBYm9ydEZldGNoTG9hZHMgfHwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMubGVuZ3RoID4gMCA/IHsKICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpCiAgICB9IDoge30pOwogIH0KCiAgZnVuY3Rpb24gZ2V0RmV0Y2hlcihrZXkpIHsKICAgIHJldHVybiBzdGF0ZS5mZXRjaGVycy5nZXQoa2V5KSB8fCBJRExFX0ZFVENIRVI7CiAgfSAvLyBUcmlnZ2VyIGEgZmV0Y2hlciBsb2FkL3N1Ym1pdCBmb3IgdGhlIGdpdmVuIGZldGNoZXIga2V5CgoKICBmdW5jdGlvbiBmZXRjaChrZXksIHJvdXRlSWQsIGhyZWYsIG9wdHMpIHsKICAgIGlmIChpc1NlcnZlcikgewogICAgICB0aHJvdyBuZXcgRXJyb3IoInJvdXRlci5mZXRjaCgpIHdhcyBjYWxsZWQgZHVyaW5nIHRoZSBzZXJ2ZXIgcmVuZGVyLCBidXQgaXQgc2hvdWxkbid0IGJlLiAiICsgIllvdSBhcmUgbGlrZWx5IGNhbGxpbmcgYSB1c2VGZXRjaGVyKCkgbWV0aG9kIGluIHRoZSBib2R5IG9mIHlvdXIgY29tcG9uZW50LiAiICsgIlRyeSBtb3ZpbmcgaXQgdG8gYSB1c2VFZmZlY3Qgb3IgYSBjYWxsYmFjay4iKTsKICAgIH0KCiAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMoa2V5KSkgYWJvcnRGZXRjaGVyKGtleSk7CiAgICBsZXQgbWF0Y2hlcyA9IG1hdGNoUm91dGVzKGRhdGFSb3V0ZXMsIGhyZWYsIGluaXQuYmFzZW5hbWUpOwoKICAgIGlmICghbWF0Y2hlcykgewogICAgICBzZXRGZXRjaGVyRXJyb3Ioa2V5LCByb3V0ZUlkLCBuZXcgRXJyb3JSZXNwb25zZSg0MDQsICJOb3QgRm91bmQiLCBudWxsKSk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBsZXQgewogICAgICBwYXRoLAogICAgICBzdWJtaXNzaW9uCiAgICB9ID0gbm9ybWFsaXplTmF2aWdhdGVPcHRpb25zKGhyZWYsIG9wdHMsIHRydWUpOwogICAgbGV0IG1hdGNoID0gZ2V0VGFyZ2V0TWF0Y2gobWF0Y2hlcywgcGF0aCk7CgogICAgaWYgKHN1Ym1pc3Npb24pIHsKICAgICAgaGFuZGxlRmV0Y2hlckFjdGlvbihrZXksIHJvdXRlSWQsIHBhdGgsIG1hdGNoLCBtYXRjaGVzLCBzdWJtaXNzaW9uKTsKICAgICAgcmV0dXJuOwogICAgfSAvLyBTdG9yZSBvZmYgdGhlIG1hdGNoIHNvIHdlIGNhbiBjYWxsIGl0J3Mgc2hvdWxkUmV2YWxpZGF0ZSBvbiBzdWJzZXF1ZW50CiAgICAvLyByZXZhbGlkYXRpb25zCgoKICAgIGZldGNoTG9hZE1hdGNoZXMuc2V0KGtleSwgW3BhdGgsIG1hdGNoLCBtYXRjaGVzXSk7CiAgICBoYW5kbGVGZXRjaGVyTG9hZGVyKGtleSwgcm91dGVJZCwgcGF0aCwgbWF0Y2gsIG1hdGNoZXMpOwogIH0gLy8gQ2FsbCB0aGUgYWN0aW9uIGZvciB0aGUgbWF0Y2hlZCBmZXRjaGVyLnN1Ym1pdCgpLCBhbmQgdGhlbiBoYW5kbGUgcmVkaXJlY3RzLAogIC8vIGVycm9ycywgYW5kIHJldmFsaWRhdGlvbgoKCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlRmV0Y2hlckFjdGlvbihrZXksIHJvdXRlSWQsIHBhdGgsIG1hdGNoLCByZXF1ZXN0TWF0Y2hlcywgc3VibWlzc2lvbikgewogICAgaW50ZXJydXB0QWN0aXZlTG9hZHMoKTsKICAgIGZldGNoTG9hZE1hdGNoZXMuZGVsZXRlKGtleSk7CgogICAgaWYgKCFtYXRjaC5yb3V0ZS5hY3Rpb24pIHsKICAgICAgbGV0IHsKICAgICAgICBlcnJvcgogICAgICB9ID0gZ2V0TWV0aG9kTm90QWxsb3dlZFJlc3VsdChwYXRoKTsKICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgZXJyb3IpOwogICAgICByZXR1cm47CiAgICB9IC8vIFB1dCB0aGlzIGZldGNoZXIgaW50byBpdCdzIHN1Ym1pdHRpbmcgc3RhdGUKCgogICAgbGV0IGV4aXN0aW5nRmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpOwoKICAgIGxldCBmZXRjaGVyID0gX2V4dGVuZHMoewogICAgICBzdGF0ZTogInN1Ym1pdHRpbmciCiAgICB9LCBzdWJtaXNzaW9uLCB7CiAgICAgIGRhdGE6IGV4aXN0aW5nRmV0Y2hlciAmJiBleGlzdGluZ0ZldGNoZXIuZGF0YQogICAgfSk7CgogICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgZmV0Y2hlcik7CiAgICB1cGRhdGVTdGF0ZSh7CiAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKQogICAgfSk7IC8vIENhbGwgdGhlIGFjdGlvbiBmb3IgdGhlIGZldGNoZXIKCiAgICBsZXQgYWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpOwogICAgbGV0IGZldGNoUmVxdWVzdCA9IGNyZWF0ZVJlcXVlc3QocGF0aCwgYWJvcnRDb250cm9sbGVyLnNpZ25hbCwgc3VibWlzc2lvbik7CiAgICBmZXRjaENvbnRyb2xsZXJzLnNldChrZXksIGFib3J0Q29udHJvbGxlcik7CiAgICBsZXQgYWN0aW9uUmVzdWx0ID0gYXdhaXQgY2FsbExvYWRlck9yQWN0aW9uKCJhY3Rpb24iLCBmZXRjaFJlcXVlc3QsIG1hdGNoLCByZXF1ZXN0TWF0Y2hlcywgcm91dGVyLmJhc2VuYW1lKTsKCiAgICBpZiAoZmV0Y2hSZXF1ZXN0LnNpZ25hbC5hYm9ydGVkKSB7CiAgICAgIC8vIFdlIGNhbiBkZWxldGUgdGhpcyBzbyBsb25nIGFzIHdlIHdlcmVuJ3QgYWJvcnRlZCBieSBvdSBvdXIgb3duIGZldGNoZXIKICAgICAgLy8gcmUtc3VibWl0IHdoaWNoIHdvdWxkIGhhdmUgcHV0IF9uZXdfIGNvbnRyb2xsZXIgaXMgaW4gZmV0Y2hDb250cm9sbGVycwogICAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5nZXQoa2V5KSA9PT0gYWJvcnRDb250cm9sbGVyKSB7CiAgICAgICAgZmV0Y2hDb250cm9sbGVycy5kZWxldGUoa2V5KTsKICAgICAgfQoKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KGFjdGlvblJlc3VsdCkpIHsKICAgICAgZmV0Y2hDb250cm9sbGVycy5kZWxldGUoa2V5KTsKICAgICAgZmV0Y2hSZWRpcmVjdElkcy5hZGQoa2V5KTsKCiAgICAgIGxldCBsb2FkaW5nRmV0Y2hlciA9IF9leHRlbmRzKHsKICAgICAgICBzdGF0ZTogImxvYWRpbmciCiAgICAgIH0sIHN1Ym1pc3Npb24sIHsKICAgICAgICBkYXRhOiB1bmRlZmluZWQKICAgICAgfSk7CgogICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBsb2FkaW5nRmV0Y2hlcik7CiAgICAgIHVwZGF0ZVN0YXRlKHsKICAgICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycykKICAgICAgfSk7CgogICAgICBsZXQgcmVkaXJlY3ROYXZpZ2F0aW9uID0gX2V4dGVuZHMoewogICAgICAgIHN0YXRlOiAibG9hZGluZyIsCiAgICAgICAgbG9jYXRpb246IGNyZWF0ZUxvY2F0aW9uKHN0YXRlLmxvY2F0aW9uLCBhY3Rpb25SZXN1bHQubG9jYXRpb24pCiAgICAgIH0sIHN1Ym1pc3Npb24pOwoKICAgICAgYXdhaXQgc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24oYWN0aW9uUmVzdWx0LCByZWRpcmVjdE5hdmlnYXRpb24pOwogICAgICByZXR1cm47CiAgICB9IC8vIFByb2Nlc3MgYW55IG5vbi1yZWRpcmVjdCBlcnJvcnMgdGhyb3duCgoKICAgIGlmIChpc0Vycm9yUmVzdWx0KGFjdGlvblJlc3VsdCkpIHsKICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgYWN0aW9uUmVzdWx0LmVycm9yKTsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmIChpc0RlZmVycmVkUmVzdWx0KGFjdGlvblJlc3VsdCkpIHsKICAgICAgcm91dGVyX2ludmFyaWFudChmYWxzZSwgImRlZmVyKCkgaXMgbm90IHN1cHBvcnRlZCBpbiBhY3Rpb25zIik7CiAgICB9IC8vIFN0YXJ0IHRoZSBkYXRhIGxvYWQgZm9yIGN1cnJlbnQgbWF0Y2hlcywgb3IgdGhlIG5leHQgbG9jYXRpb24gaWYgd2UncmUKICAgIC8vIGluIHRoZSBtaWRkbGUgb2YgYSBuYXZpZ2F0aW9uCgoKICAgIGxldCBuZXh0TG9jYXRpb24gPSBzdGF0ZS5uYXZpZ2F0aW9uLmxvY2F0aW9uIHx8IHN0YXRlLmxvY2F0aW9uOwogICAgbGV0IHJldmFsaWRhdGlvblJlcXVlc3QgPSBjcmVhdGVSZXF1ZXN0KG5leHRMb2NhdGlvbiwgYWJvcnRDb250cm9sbGVyLnNpZ25hbCk7CiAgICBsZXQgbWF0Y2hlcyA9IHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgIT09ICJpZGxlIiA/IG1hdGNoUm91dGVzKGRhdGFSb3V0ZXMsIHN0YXRlLm5hdmlnYXRpb24ubG9jYXRpb24sIGluaXQuYmFzZW5hbWUpIDogc3RhdGUubWF0Y2hlczsKICAgIHJvdXRlcl9pbnZhcmlhbnQobWF0Y2hlcywgIkRpZG4ndCBmaW5kIGFueSBtYXRjaGVzIGFmdGVyIGZldGNoZXIgYWN0aW9uIik7CiAgICBsZXQgbG9hZElkID0gKytpbmNyZW1lbnRpbmdMb2FkSWQ7CiAgICBmZXRjaFJlbG9hZElkcy5zZXQoa2V5LCBsb2FkSWQpOwoKICAgIGxldCBsb2FkRmV0Y2hlciA9IF9leHRlbmRzKHsKICAgICAgc3RhdGU6ICJsb2FkaW5nIiwKICAgICAgZGF0YTogYWN0aW9uUmVzdWx0LmRhdGEKICAgIH0sIHN1Ym1pc3Npb24pOwoKICAgIHN0YXRlLmZldGNoZXJzLnNldChrZXksIGxvYWRGZXRjaGVyKTsKICAgIGxldCBbbWF0Y2hlc1RvTG9hZCwgcmV2YWxpZGF0aW5nRmV0Y2hlcnNdID0gZ2V0TWF0Y2hlc1RvTG9hZChzdGF0ZSwgbWF0Y2hlcywgc3VibWlzc2lvbiwgbmV4dExvY2F0aW9uLCBpc1JldmFsaWRhdGlvblJlcXVpcmVkLCBjYW5jZWxsZWREZWZlcnJlZFJvdXRlcywgY2FuY2VsbGVkRmV0Y2hlckxvYWRzLCB7CiAgICAgIFttYXRjaC5yb3V0ZS5pZF06IGFjdGlvblJlc3VsdC5kYXRhCiAgICB9LCB1bmRlZmluZWQsIC8vIE5vIG5lZWQgdG8gc2VuZCB0aHJvdWdoIGVycm9ycyBzaW5jZSB3ZSBzaG9ydCBjaXJjdWl0IGFib3ZlCiAgICBmZXRjaExvYWRNYXRjaGVzKTsgLy8gUHV0IGFsbCByZXZhbGlkYXRpbmcgZmV0Y2hlcnMgaW50byB0aGUgbG9hZGluZyBzdGF0ZSwgZXhjZXB0IGZvciB0aGUKICAgIC8vIGN1cnJlbnQgZmV0Y2hlciB3aGljaCB3ZSB3YW50IHRvIGtlZXAgaW4gaXQncyBjdXJyZW50IGxvYWRpbmcgc3RhdGUgd2hpY2gKICAgIC8vIGNvbnRhaW5zIGl0J3MgYWN0aW9uIHN1Ym1pc3Npb24gaW5mbyArIGFjdGlvbiBkYXRhCgogICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZmlsdGVyKF9yZWY1ID0+IHsKICAgICAgbGV0IFtzdGFsZUtleV0gPSBfcmVmNTsKICAgICAgcmV0dXJuIHN0YWxlS2V5ICE9PSBrZXk7CiAgICB9KS5mb3JFYWNoKF9yZWY2ID0+IHsKICAgICAgbGV0IFtzdGFsZUtleV0gPSBfcmVmNjsKICAgICAgbGV0IGV4aXN0aW5nRmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChzdGFsZUtleSk7CiAgICAgIGxldCByZXZhbGlkYXRpbmdGZXRjaGVyID0gewogICAgICAgIHN0YXRlOiAibG9hZGluZyIsCiAgICAgICAgZGF0YTogZXhpc3RpbmdGZXRjaGVyICYmIGV4aXN0aW5nRmV0Y2hlci5kYXRhLAogICAgICAgIGZvcm1NZXRob2Q6IHVuZGVmaW5lZCwKICAgICAgICBmb3JtQWN0aW9uOiB1bmRlZmluZWQsCiAgICAgICAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCwKICAgICAgICBmb3JtRGF0YTogdW5kZWZpbmVkCiAgICAgIH07CiAgICAgIHN0YXRlLmZldGNoZXJzLnNldChzdGFsZUtleSwgcmV2YWxpZGF0aW5nRmV0Y2hlcik7CiAgICAgIGZldGNoQ29udHJvbGxlcnMuc2V0KHN0YWxlS2V5LCBhYm9ydENvbnRyb2xsZXIpOwogICAgfSk7CiAgICB1cGRhdGVTdGF0ZSh7CiAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKQogICAgfSk7CiAgICBsZXQgewogICAgICByZXN1bHRzLAogICAgICBsb2FkZXJSZXN1bHRzLAogICAgICBmZXRjaGVyUmVzdWx0cwogICAgfSA9IGF3YWl0IGNhbGxMb2FkZXJzQW5kTWF5YmVSZXNvbHZlRGF0YShzdGF0ZS5tYXRjaGVzLCBtYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCByZXZhbGlkYXRpbmdGZXRjaGVycywgcmV2YWxpZGF0aW9uUmVxdWVzdCk7CgogICAgaWYgKGFib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkgewogICAgICByZXR1cm47CiAgICB9CgogICAgZmV0Y2hSZWxvYWRJZHMuZGVsZXRlKGtleSk7CiAgICBmZXRjaENvbnRyb2xsZXJzLmRlbGV0ZShrZXkpOwogICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZm9yRWFjaChfcmVmNyA9PiB7CiAgICAgIGxldCBbc3RhbGVLZXldID0gX3JlZjc7CiAgICAgIHJldHVybiBmZXRjaENvbnRyb2xsZXJzLmRlbGV0ZShzdGFsZUtleSk7CiAgICB9KTsKICAgIGxldCByZWRpcmVjdCA9IGZpbmRSZWRpcmVjdChyZXN1bHRzKTsKCiAgICBpZiAocmVkaXJlY3QpIHsKICAgICAgbGV0IHJlZGlyZWN0TmF2aWdhdGlvbiA9IGdldExvYWRlclJlZGlyZWN0KHN0YXRlLCByZWRpcmVjdCk7CiAgICAgIGF3YWl0IHN0YXJ0UmVkaXJlY3ROYXZpZ2F0aW9uKHJlZGlyZWN0LCByZWRpcmVjdE5hdmlnYXRpb24pOwogICAgICByZXR1cm47CiAgICB9IC8vIFByb2Nlc3MgYW5kIGNvbW1pdCBvdXRwdXQgZnJvbSBsb2FkZXJzCgoKICAgIGxldCB7CiAgICAgIGxvYWRlckRhdGEsCiAgICAgIGVycm9ycwogICAgfSA9IHByb2Nlc3NMb2FkZXJEYXRhKHN0YXRlLCBzdGF0ZS5tYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCBsb2FkZXJSZXN1bHRzLCB1bmRlZmluZWQsIHJldmFsaWRhdGluZ0ZldGNoZXJzLCBmZXRjaGVyUmVzdWx0cywgYWN0aXZlRGVmZXJyZWRzKTsKICAgIGxldCBkb25lRmV0Y2hlciA9IHsKICAgICAgc3RhdGU6ICJpZGxlIiwKICAgICAgZGF0YTogYWN0aW9uUmVzdWx0LmRhdGEsCiAgICAgIGZvcm1NZXRob2Q6IHVuZGVmaW5lZCwKICAgICAgZm9ybUFjdGlvbjogdW5kZWZpbmVkLAogICAgICBmb3JtRW5jVHlwZTogdW5kZWZpbmVkLAogICAgICBmb3JtRGF0YTogdW5kZWZpbmVkCiAgICB9OwogICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgZG9uZUZldGNoZXIpOwogICAgbGV0IGRpZEFib3J0RmV0Y2hMb2FkcyA9IGFib3J0U3RhbGVGZXRjaExvYWRzKGxvYWRJZCk7IC8vIElmIHdlIGFyZSBjdXJyZW50bHkgaW4gYSBuYXZpZ2F0aW9uIGxvYWRpbmcgc3RhdGUgYW5kIHRoaXMgZmV0Y2hlciBpcwogICAgLy8gbW9yZSByZWNlbnQgdGhhbiB0aGUgbmF2aWdhdGlvbiwgd2Ugd2FudCB0aGUgbmV3ZXIgZGF0YSBzbyBhYm9ydCB0aGUKICAgIC8vIG5hdmlnYXRpb24gYW5kIGNvbXBsZXRlIGl0IHdpdGggdGhlIGZldGNoZXIgZGF0YQoKICAgIGlmIChzdGF0ZS5uYXZpZ2F0aW9uLnN0YXRlID09PSAibG9hZGluZyIgJiYgbG9hZElkID4gcGVuZGluZ05hdmlnYXRpb25Mb2FkSWQpIHsKICAgICAgcm91dGVyX2ludmFyaWFudChwZW5kaW5nQWN0aW9uLCAiRXhwZWN0ZWQgcGVuZGluZyBhY3Rpb24iKTsKICAgICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyICYmIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlci5hYm9ydCgpOwogICAgICBjb21wbGV0ZU5hdmlnYXRpb24oc3RhdGUubmF2aWdhdGlvbi5sb2NhdGlvbiwgewogICAgICAgIG1hdGNoZXMsCiAgICAgICAgbG9hZGVyRGF0YSwKICAgICAgICBlcnJvcnMsCiAgICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpCiAgICAgIH0pOwogICAgfSBlbHNlIHsKICAgICAgLy8gb3RoZXJ3aXNlIGp1c3QgdXBkYXRlIHdpdGggdGhlIGZldGNoZXIgZGF0YSwgcHJlc2VydmluZyBhbnkgZXhpc3RpbmcKICAgICAgLy8gbG9hZGVyRGF0YSBmb3IgbG9hZGVycyB0aGF0IGRpZCBub3QgbmVlZCB0byByZWxvYWQuICBXZSBoYXZlIHRvCiAgICAgIC8vIG1hbnVhbGx5IG1lcmdlIGhlcmUgc2luY2Ugd2UgYXJlbid0IGdvaW5nIHRocm91Z2ggY29tcGxldGVOYXZpZ2F0aW9uCiAgICAgIHVwZGF0ZVN0YXRlKF9leHRlbmRzKHsKICAgICAgICBlcnJvcnMsCiAgICAgICAgbG9hZGVyRGF0YTogbWVyZ2VMb2FkZXJEYXRhKHN0YXRlLmxvYWRlckRhdGEsIGxvYWRlckRhdGEsIG1hdGNoZXMpCiAgICAgIH0sIGRpZEFib3J0RmV0Y2hMb2FkcyA/IHsKICAgICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycykKICAgICAgfSA6IHt9KSk7CiAgICAgIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQgPSBmYWxzZTsKICAgIH0KICB9IC8vIENhbGwgdGhlIG1hdGNoZWQgbG9hZGVyIGZvciBmZXRjaGVyLmxvYWQoKSwgaGFuZGxpbmcgcmVkaXJlY3RzLCBlcnJvcnMsIGV0Yy4KCgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUZldGNoZXJMb2FkZXIoa2V5LCByb3V0ZUlkLCBwYXRoLCBtYXRjaCwgbWF0Y2hlcykgewogICAgbGV0IGV4aXN0aW5nRmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpOyAvLyBQdXQgdGhpcyBmZXRjaGVyIGludG8gaXQncyBsb2FkaW5nIHN0YXRlCgogICAgbGV0IGxvYWRpbmdGZXRjaGVyID0gewogICAgICBzdGF0ZTogImxvYWRpbmciLAogICAgICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsCiAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICAgICAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCwKICAgICAgZm9ybURhdGE6IHVuZGVmaW5lZCwKICAgICAgZGF0YTogZXhpc3RpbmdGZXRjaGVyICYmIGV4aXN0aW5nRmV0Y2hlci5kYXRhCiAgICB9OwogICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgbG9hZGluZ0ZldGNoZXIpOwogICAgdXBkYXRlU3RhdGUoewogICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycykKICAgIH0pOyAvLyBDYWxsIHRoZSBsb2FkZXIgZm9yIHRoaXMgZmV0Y2hlciByb3V0ZSBtYXRjaAoKICAgIGxldCBhYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7CiAgICBsZXQgZmV0Y2hSZXF1ZXN0ID0gY3JlYXRlUmVxdWVzdChwYXRoLCBhYm9ydENvbnRyb2xsZXIuc2lnbmFsKTsKICAgIGZldGNoQ29udHJvbGxlcnMuc2V0KGtleSwgYWJvcnRDb250cm9sbGVyKTsKICAgIGxldCByZXN1bHQgPSBhd2FpdCBjYWxsTG9hZGVyT3JBY3Rpb24oImxvYWRlciIsIGZldGNoUmVxdWVzdCwgbWF0Y2gsIG1hdGNoZXMsIHJvdXRlci5iYXNlbmFtZSk7IC8vIERlZmVycmVkIGlzbid0IHN1cHBvcnRlZCBvciBmZXRjaGVyIGxvYWRzLCBhd2FpdCBldmVyeXRoaW5nIGFuZCB0cmVhdCBpdAogICAgLy8gYXMgYSBub3JtYWwgbG9hZC4gIHJlc29sdmVEZWZlcnJlZERhdGEgd2lsbCByZXR1cm4gdW5kZWZpbmVkIGlmIHRoaXMKICAgIC8vIGZldGNoZXIgZ2V0cyBhYm9ydGVkLCBzbyB3ZSBqdXN0IGxlYXZlIHJlc3VsdCB1bnRvdWNoZWQgYW5kIHNob3J0IGNpcmN1aXQKICAgIC8vIGJlbG93IGlmIHRoYXQgaGFwcGVucwoKICAgIGlmIChpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkpIHsKICAgICAgcmVzdWx0ID0gKGF3YWl0IHJlc29sdmVEZWZlcnJlZERhdGEocmVzdWx0LCBmZXRjaFJlcXVlc3Quc2lnbmFsLCB0cnVlKSkgfHwgcmVzdWx0OwogICAgfSAvLyBXZSBjYW4gZGVsZXRlIHRoaXMgc28gbG9uZyBhcyB3ZSB3ZXJlbid0IGFib3J0ZWQgYnkgb3Ugb3VyIG93biBmZXRjaGVyCiAgICAvLyByZS1sb2FkIHdoaWNoIHdvdWxkIGhhdmUgcHV0IF9uZXdfIGNvbnRyb2xsZXIgaXMgaW4gZmV0Y2hDb250cm9sbGVycwoKCiAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5nZXQoa2V5KSA9PT0gYWJvcnRDb250cm9sbGVyKSB7CiAgICAgIGZldGNoQ29udHJvbGxlcnMuZGVsZXRlKGtleSk7CiAgICB9CgogICAgaWYgKGZldGNoUmVxdWVzdC5zaWduYWwuYWJvcnRlZCkgewogICAgICByZXR1cm47CiAgICB9IC8vIElmIHRoZSBsb2FkZXIgdGhyZXcgYSByZWRpcmVjdCBSZXNwb25zZSwgc3RhcnQgYSBuZXcgUkVQTEFDRSBuYXZpZ2F0aW9uCgoKICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkpIHsKICAgICAgbGV0IHJlZGlyZWN0TmF2aWdhdGlvbiA9IGdldExvYWRlclJlZGlyZWN0KHN0YXRlLCByZXN1bHQpOwogICAgICBhd2FpdCBzdGFydFJlZGlyZWN0TmF2aWdhdGlvbihyZXN1bHQsIHJlZGlyZWN0TmF2aWdhdGlvbik7CiAgICAgIHJldHVybjsKICAgIH0gLy8gUHJvY2VzcyBhbnkgbm9uLXJlZGlyZWN0IGVycm9ycyB0aHJvd24KCgogICAgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkgewogICAgICBsZXQgYm91bmRhcnlNYXRjaCA9IGZpbmROZWFyZXN0Qm91bmRhcnkoc3RhdGUubWF0Y2hlcywgcm91dGVJZCk7CiAgICAgIHN0YXRlLmZldGNoZXJzLmRlbGV0ZShrZXkpOyAvLyBUT0RPOiBJbiByZW1peCwgdGhpcyB3b3VsZCByZXNldCB0byBJRExFX05BVklHQVRJT04gaWYgaXQgd2FzIGEgY2F0Y2ggLQogICAgICAvLyBkbyB3ZSBuZWVkIHRvIGJlaGF2ZSBhbnkgZGlmZmVyZW50bHkgd2l0aCBvdXIgbm9uLXJlZGlyZWN0IGVycm9ycz8KICAgICAgLy8gV2hhdCBpZiBpdCB3YXMgYSBub24tcmVkaXJlY3QgUmVzcG9uc2U/CgogICAgICB1cGRhdGVTdGF0ZSh7CiAgICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpLAogICAgICAgIGVycm9yczogewogICAgICAgICAgW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZXJyb3IKICAgICAgICB9CiAgICAgIH0pOwogICAgICByZXR1cm47CiAgICB9CgogICAgcm91dGVyX2ludmFyaWFudCghaXNEZWZlcnJlZFJlc3VsdChyZXN1bHQpLCAiVW5oYW5kbGVkIGZldGNoZXIgZGVmZXJyZWQgZGF0YSIpOyAvLyBQdXQgdGhlIGZldGNoZXIgYmFjayBpbnRvIGFuIGlkbGUgc3RhdGUKCiAgICBsZXQgZG9uZUZldGNoZXIgPSB7CiAgICAgIHN0YXRlOiAiaWRsZSIsCiAgICAgIGRhdGE6IHJlc3VsdC5kYXRhLAogICAgICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsCiAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICAgICAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCwKICAgICAgZm9ybURhdGE6IHVuZGVmaW5lZAogICAgfTsKICAgIHN0YXRlLmZldGNoZXJzLnNldChrZXksIGRvbmVGZXRjaGVyKTsKICAgIHVwZGF0ZVN0YXRlKHsKICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpCiAgICB9KTsKICB9CiAgLyoqCiAgICogVXRpbGl0eSBmdW5jdGlvbiB0byBoYW5kbGUgcmVkaXJlY3RzIHJldHVybmVkIGZyb20gYW4gYWN0aW9uIG9yIGxvYWRlci4KICAgKiBOb3JtYWxseSwgYSByZWRpcmVjdCAicmVwbGFjZXMiIHRoZSBuYXZpZ2F0aW9uIHRoYXQgdHJpZ2dlcmVkIGl0LiAgU28sIGZvcgogICAqIGV4YW1wbGU6CiAgICoKICAgKiAgLSB1c2VyIGlzIG9uIC9hCiAgICogIC0gdXNlciBjbGlja3MgYSBsaW5rIHRvIC9iCiAgICogIC0gbG9hZGVyIGZvciAvYiByZWRpcmVjdHMgdG8gL2MKICAgKgogICAqIEluIGEgbm9uLUpTIGFwcCB0aGUgYnJvd3NlciB3b3VsZCB0cmFjayB0aGUgaW4tZmxpZ2h0IG5hdmlnYXRpb24gdG8gL2IgYW5kCiAgICogdGhlbiByZXBsYWNlIGl0IHdpdGggL2Mgd2hlbiBpdCBlbmNvdW50ZXJlZCB0aGUgcmVkaXJlY3QgcmVzcG9uc2UuICBJbgogICAqIHRoZSBlbmQgaXQgd291bGQgb25seSBldmVyIHVwZGF0ZSB0aGUgVVJMIGJhciB3aXRoIC9jLgogICAqCiAgICogSW4gY2xpZW50LXNpZGUgcm91dGluZyB1c2luZyBwdXNoU3RhdGUvcmVwbGFjZVN0YXRlLCB3ZSBhaW0gdG8gZW11bGF0ZQogICAqIHRoaXMgYmVoYXZpb3IgYW5kIHdlIGFsc28gZG8gbm90IHVwZGF0ZSBoaXN0b3J5IHVudGlsIHRoZSBlbmQgb2YgdGhlCiAgICogbmF2aWdhdGlvbiAoaW5jbHVkaW5nIHByb2Nlc3NlZCByZWRpcmVjdHMpLiAgVGhpcyBtZWFucyB0aGF0IHdlIG5ldmVyCiAgICogYWN0dWFsbHkgdG91Y2ggaGlzdG9yeSB1bnRpbCB3ZSd2ZSBwcm9jZXNzZWQgcmVkaXJlY3RzLCBzbyB3ZSBqdXN0IHVzZQogICAqIHRoZSBoaXN0b3J5IGFjdGlvbiBmcm9tIHRoZSBvcmlnaW5hbCBuYXZpZ2F0aW9uIChQVVNIIG9yIFJFUExBQ0UpLgogICAqLwoKCiAgYXN5bmMgZnVuY3Rpb24gc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24ocmVkaXJlY3QsIG5hdmlnYXRpb24sIHJlcGxhY2UpIHsKICAgIGlmIChyZWRpcmVjdC5yZXZhbGlkYXRlKSB7CiAgICAgIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQgPSB0cnVlOwogICAgfQoKICAgIHJvdXRlcl9pbnZhcmlhbnQobmF2aWdhdGlvbi5sb2NhdGlvbiwgIkV4cGVjdGVkIGEgbG9jYXRpb24gb24gdGhlIHJlZGlyZWN0IG5hdmlnYXRpb24iKTsgLy8gVGhlcmUncyBubyBuZWVkIHRvIGFib3J0IG9uIHJlZGlyZWN0cywgc2luY2Ugd2UgZG9uJ3QgZGV0ZWN0IHRoZQogICAgLy8gcmVkaXJlY3QgdW50aWwgdGhlIGFjdGlvbi9sb2FkZXJzIGhhdmUgc2V0dGxlZAoKICAgIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciA9IG51bGw7CiAgICBsZXQgcmVkaXJlY3RIaXN0b3J5QWN0aW9uID0gcmVwbGFjZSA9PT0gdHJ1ZSA/IEFjdGlvbi5SZXBsYWNlIDogQWN0aW9uLlB1c2g7CiAgICBhd2FpdCBzdGFydE5hdmlnYXRpb24ocmVkaXJlY3RIaXN0b3J5QWN0aW9uLCBuYXZpZ2F0aW9uLmxvY2F0aW9uLCB7CiAgICAgIG92ZXJyaWRlTmF2aWdhdGlvbjogbmF2aWdhdGlvbgogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBjYWxsTG9hZGVyc0FuZE1heWJlUmVzb2x2ZURhdGEoY3VycmVudE1hdGNoZXMsIG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIGZldGNoZXJzVG9Mb2FkLCByZXF1ZXN0KSB7CiAgICAvLyBDYWxsIGFsbCBuYXZpZ2F0aW9uIGxvYWRlcnMgYW5kIHJldmFsaWRhdGluZyBmZXRjaGVyIGxvYWRlcnMgaW4gcGFyYWxsZWwsCiAgICAvLyB0aGVuIHNsaWNlIG9mZiB0aGUgcmVzdWx0cyBpbnRvIHNlcGFyYXRlIGFycmF5cyBzbyB3ZSBjYW4gaGFuZGxlIHRoZW0KICAgIC8vIGFjY29yZGluZ2x5CiAgICBsZXQgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKFsuLi5tYXRjaGVzVG9Mb2FkLm1hcChtYXRjaCA9PiBjYWxsTG9hZGVyT3JBY3Rpb24oImxvYWRlciIsIHJlcXVlc3QsIG1hdGNoLCBtYXRjaGVzLCByb3V0ZXIuYmFzZW5hbWUpKSwgLi4uZmV0Y2hlcnNUb0xvYWQubWFwKF9yZWY4ID0+IHsKICAgICAgbGV0IFssIGhyZWYsIG1hdGNoLCBmZXRjaE1hdGNoZXNdID0gX3JlZjg7CiAgICAgIHJldHVybiBjYWxsTG9hZGVyT3JBY3Rpb24oImxvYWRlciIsIGNyZWF0ZVJlcXVlc3QoaHJlZiwgcmVxdWVzdC5zaWduYWwpLCBtYXRjaCwgZmV0Y2hNYXRjaGVzLCByb3V0ZXIuYmFzZW5hbWUpOwogICAgfSldKTsKICAgIGxldCBsb2FkZXJSZXN1bHRzID0gcmVzdWx0cy5zbGljZSgwLCBtYXRjaGVzVG9Mb2FkLmxlbmd0aCk7CiAgICBsZXQgZmV0Y2hlclJlc3VsdHMgPSByZXN1bHRzLnNsaWNlKG1hdGNoZXNUb0xvYWQubGVuZ3RoKTsKICAgIGF3YWl0IFByb21pc2UuYWxsKFtyZXNvbHZlRGVmZXJyZWRSZXN1bHRzKGN1cnJlbnRNYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCBsb2FkZXJSZXN1bHRzLCByZXF1ZXN0LnNpZ25hbCwgZmFsc2UsIHN0YXRlLmxvYWRlckRhdGEpLCByZXNvbHZlRGVmZXJyZWRSZXN1bHRzKGN1cnJlbnRNYXRjaGVzLCBmZXRjaGVyc1RvTG9hZC5tYXAoX3JlZjkgPT4gewogICAgICBsZXQgWywsIG1hdGNoXSA9IF9yZWY5OwogICAgICByZXR1cm4gbWF0Y2g7CiAgICB9KSwgZmV0Y2hlclJlc3VsdHMsIHJlcXVlc3Quc2lnbmFsLCB0cnVlKV0pOwogICAgcmV0dXJuIHsKICAgICAgcmVzdWx0cywKICAgICAgbG9hZGVyUmVzdWx0cywKICAgICAgZmV0Y2hlclJlc3VsdHMKICAgIH07CiAgfQoKICBmdW5jdGlvbiBpbnRlcnJ1cHRBY3RpdmVMb2FkcygpIHsKICAgIC8vIEV2ZXJ5IGludGVycnVwdGlvbiB0cmlnZ2VycyBhIHJldmFsaWRhdGlvbgogICAgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCA9IHRydWU7IC8vIENhbmNlbCBwZW5kaW5nIHJvdXRlLWxldmVsIGRlZmVycmVkcyBhbmQgbWFyayBjYW5jZWxsZWQgcm91dGVzIGZvcgogICAgLy8gcmV2YWxpZGF0aW9uCgogICAgY2FuY2VsbGVkRGVmZXJyZWRSb3V0ZXMucHVzaCguLi5jYW5jZWxBY3RpdmVEZWZlcnJlZHMoKSk7IC8vIEFib3J0IGluLWZsaWdodCBmZXRjaGVyIGxvYWRzCgogICAgZmV0Y2hMb2FkTWF0Y2hlcy5mb3JFYWNoKChfLCBrZXkpID0+IHsKICAgICAgaWYgKGZldGNoQ29udHJvbGxlcnMuaGFzKGtleSkpIHsKICAgICAgICBjYW5jZWxsZWRGZXRjaGVyTG9hZHMucHVzaChrZXkpOwogICAgICAgIGFib3J0RmV0Y2hlcihrZXkpOwogICAgICB9CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHNldEZldGNoZXJFcnJvcihrZXksIHJvdXRlSWQsIGVycm9yKSB7CiAgICBsZXQgYm91bmRhcnlNYXRjaCA9IGZpbmROZWFyZXN0Qm91bmRhcnkoc3RhdGUubWF0Y2hlcywgcm91dGVJZCk7CiAgICBkZWxldGVGZXRjaGVyKGtleSk7CiAgICB1cGRhdGVTdGF0ZSh7CiAgICAgIGVycm9yczogewogICAgICAgIFtib3VuZGFyeU1hdGNoLnJvdXRlLmlkXTogZXJyb3IKICAgICAgfSwKICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpCiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIGRlbGV0ZUZldGNoZXIoa2V5KSB7CiAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMoa2V5KSkgYWJvcnRGZXRjaGVyKGtleSk7CiAgICBmZXRjaExvYWRNYXRjaGVzLmRlbGV0ZShrZXkpOwogICAgZmV0Y2hSZWxvYWRJZHMuZGVsZXRlKGtleSk7CiAgICBmZXRjaFJlZGlyZWN0SWRzLmRlbGV0ZShrZXkpOwogICAgc3RhdGUuZmV0Y2hlcnMuZGVsZXRlKGtleSk7CiAgfQoKICBmdW5jdGlvbiBhYm9ydEZldGNoZXIoa2V5KSB7CiAgICBsZXQgY29udHJvbGxlciA9IGZldGNoQ29udHJvbGxlcnMuZ2V0KGtleSk7CiAgICByb3V0ZXJfaW52YXJpYW50KGNvbnRyb2xsZXIsICJFeHBlY3RlZCBmZXRjaCBjb250cm9sbGVyOiAiICsga2V5KTsKICAgIGNvbnRyb2xsZXIuYWJvcnQoKTsKICAgIGZldGNoQ29udHJvbGxlcnMuZGVsZXRlKGtleSk7CiAgfQoKICBmdW5jdGlvbiBtYXJrRmV0Y2hlcnNEb25lKGtleXMpIHsKICAgIGZvciAobGV0IGtleSBvZiBrZXlzKSB7CiAgICAgIGxldCBmZXRjaGVyID0gZ2V0RmV0Y2hlcihrZXkpOwogICAgICBsZXQgZG9uZUZldGNoZXIgPSB7CiAgICAgICAgc3RhdGU6ICJpZGxlIiwKICAgICAgICBkYXRhOiBmZXRjaGVyLmRhdGEsCiAgICAgICAgZm9ybU1ldGhvZDogdW5kZWZpbmVkLAogICAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCwKICAgICAgICBmb3JtRW5jVHlwZTogdW5kZWZpbmVkLAogICAgICAgIGZvcm1EYXRhOiB1bmRlZmluZWQKICAgICAgfTsKICAgICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgZG9uZUZldGNoZXIpOwogICAgfQogIH0KCiAgZnVuY3Rpb24gbWFya0ZldGNoUmVkaXJlY3RzRG9uZSgpIHsKICAgIGxldCBkb25lS2V5cyA9IFtdOwoKICAgIGZvciAobGV0IGtleSBvZiBmZXRjaFJlZGlyZWN0SWRzKSB7CiAgICAgIGxldCBmZXRjaGVyID0gc3RhdGUuZmV0Y2hlcnMuZ2V0KGtleSk7CiAgICAgIHJvdXRlcl9pbnZhcmlhbnQoZmV0Y2hlciwgIkV4cGVjdGVkIGZldGNoZXI6ICIgKyBrZXkpOwoKICAgICAgaWYgKGZldGNoZXIuc3RhdGUgPT09ICJsb2FkaW5nIikgewogICAgICAgIGZldGNoUmVkaXJlY3RJZHMuZGVsZXRlKGtleSk7CiAgICAgICAgZG9uZUtleXMucHVzaChrZXkpOwogICAgICB9CiAgICB9CgogICAgbWFya0ZldGNoZXJzRG9uZShkb25lS2V5cyk7CiAgfQoKICBmdW5jdGlvbiBhYm9ydFN0YWxlRmV0Y2hMb2FkcyhsYW5kZWRJZCkgewogICAgbGV0IHllZXRlZEtleXMgPSBbXTsKCiAgICBmb3IgKGxldCBba2V5LCBpZF0gb2YgZmV0Y2hSZWxvYWRJZHMpIHsKICAgICAgaWYgKGlkIDwgbGFuZGVkSWQpIHsKICAgICAgICBsZXQgZmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpOwogICAgICAgIHJvdXRlcl9pbnZhcmlhbnQoZmV0Y2hlciwgIkV4cGVjdGVkIGZldGNoZXI6ICIgKyBrZXkpOwoKICAgICAgICBpZiAoZmV0Y2hlci5zdGF0ZSA9PT0gImxvYWRpbmciKSB7CiAgICAgICAgICBhYm9ydEZldGNoZXIoa2V5KTsKICAgICAgICAgIGZldGNoUmVsb2FkSWRzLmRlbGV0ZShrZXkpOwogICAgICAgICAgeWVldGVkS2V5cy5wdXNoKGtleSk7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgbWFya0ZldGNoZXJzRG9uZSh5ZWV0ZWRLZXlzKTsKICAgIHJldHVybiB5ZWV0ZWRLZXlzLmxlbmd0aCA+IDA7CiAgfQoKICBmdW5jdGlvbiBjYW5jZWxBY3RpdmVEZWZlcnJlZHMocHJlZGljYXRlKSB7CiAgICBsZXQgY2FuY2VsbGVkUm91dGVJZHMgPSBbXTsKICAgIGFjdGl2ZURlZmVycmVkcy5mb3JFYWNoKChkZmQsIHJvdXRlSWQpID0+IHsKICAgICAgaWYgKCFwcmVkaWNhdGUgfHwgcHJlZGljYXRlKHJvdXRlSWQpKSB7CiAgICAgICAgLy8gQ2FuY2VsIHRoZSBkZWZlcnJlZCAtIGJ1dCBkbyBub3QgcmVtb3ZlIGZyb20gYWN0aXZlRGVmZXJyZWRzIGhlcmUgLQogICAgICAgIC8vIHdlIHJlbHkgb24gdGhlIHN1YnNjcmliZXJzIHRvIGRvIHRoYXQgc28gb3VyIHRlc3RzIGNhbiBhc3NlcnQgcHJvcGVyCiAgICAgICAgLy8gY2xlYW51cCB2aWEgX2ludGVybmFsQWN0aXZlRGVmZXJyZWRzCiAgICAgICAgZGZkLmNhbmNlbCgpOwogICAgICAgIGNhbmNlbGxlZFJvdXRlSWRzLnB1c2gocm91dGVJZCk7CiAgICAgICAgYWN0aXZlRGVmZXJyZWRzLmRlbGV0ZShyb3V0ZUlkKTsKICAgICAgfQogICAgfSk7CiAgICByZXR1cm4gY2FuY2VsbGVkUm91dGVJZHM7CiAgfSAvLyBPcHQgaW4gdG8gY2FwdHVyaW5nIGFuZCByZXBvcnRpbmcgc2Nyb2xsIHBvc2l0aW9ucyBkdXJpbmcgbmF2aWdhdGlvbnMsCiAgLy8gdXNlZCBieSB0aGUgPFNjcm9sbFJlc3RvcmF0aW9uPiBjb21wb25lbnQKCgogIGZ1bmN0aW9uIGVuYWJsZVNjcm9sbFJlc3RvcmF0aW9uKHBvc2l0aW9ucywgZ2V0UG9zaXRpb24sIGdldEtleSkgewogICAgc2F2ZWRTY3JvbGxQb3NpdGlvbnMgPSBwb3NpdGlvbnM7CiAgICBnZXRTY3JvbGxQb3NpdGlvbiA9IGdldFBvc2l0aW9uOwoKICAgIGdldFNjcm9sbFJlc3RvcmF0aW9uS2V5ID0gZ2V0S2V5IHx8IChsb2NhdGlvbiA9PiBsb2NhdGlvbi5rZXkpOyAvLyBQZXJmb3JtIGluaXRpYWwgaHlkcmF0aW9uIHNjcm9sbCByZXN0b3JhdGlvbiwgc2luY2Ugd2UgbWlzcyB0aGUgYm9hdCBvbgogICAgLy8gdGhlIGluaXRpYWwgdXBkYXRlU3RhdGUoKSBiZWNhdXNlIHdlJ3ZlIG5vdCB5ZXQgcmVuZGVyZWQgPFNjcm9sbFJlc3RvcmF0aW9uLz4KICAgIC8vIGFuZCB0aGVyZWZvcmUgaGF2ZSBubyBzYXZlZFNjcm9sbFBvc2l0aW9ucyBhdmFpbGFibGUKCgogICAgaWYgKCFpbml0aWFsU2Nyb2xsUmVzdG9yZWQgJiYgc3RhdGUubmF2aWdhdGlvbiA9PT0gSURMRV9OQVZJR0FUSU9OKSB7CiAgICAgIGluaXRpYWxTY3JvbGxSZXN0b3JlZCA9IHRydWU7CiAgICAgIGxldCB5ID0gZ2V0U2F2ZWRTY3JvbGxQb3NpdGlvbihzdGF0ZS5sb2NhdGlvbiwgc3RhdGUubWF0Y2hlcyk7CgogICAgICBpZiAoeSAhPSBudWxsKSB7CiAgICAgICAgdXBkYXRlU3RhdGUoewogICAgICAgICAgcmVzdG9yZVNjcm9sbFBvc2l0aW9uOiB5CiAgICAgICAgfSk7CiAgICAgIH0KICAgIH0KCiAgICByZXR1cm4gKCkgPT4gewogICAgICBzYXZlZFNjcm9sbFBvc2l0aW9ucyA9IG51bGw7CiAgICAgIGdldFNjcm9sbFBvc2l0aW9uID0gbnVsbDsKICAgICAgZ2V0U2Nyb2xsUmVzdG9yYXRpb25LZXkgPSBudWxsOwogICAgfTsKICB9CgogIGZ1bmN0aW9uIHNhdmVTY3JvbGxQb3NpdGlvbihsb2NhdGlvbiwgbWF0Y2hlcykgewogICAgaWYgKHNhdmVkU2Nyb2xsUG9zaXRpb25zICYmIGdldFNjcm9sbFJlc3RvcmF0aW9uS2V5ICYmIGdldFNjcm9sbFBvc2l0aW9uKSB7CiAgICAgIGxldCB1c2VyTWF0Y2hlcyA9IG1hdGNoZXMubWFwKG0gPT4gY3JlYXRlVXNlTWF0Y2hlc01hdGNoKG0sIHN0YXRlLmxvYWRlckRhdGEpKTsKICAgICAgbGV0IGtleSA9IGdldFNjcm9sbFJlc3RvcmF0aW9uS2V5KGxvY2F0aW9uLCB1c2VyTWF0Y2hlcykgfHwgbG9jYXRpb24ua2V5OwogICAgICBzYXZlZFNjcm9sbFBvc2l0aW9uc1trZXldID0gZ2V0U2Nyb2xsUG9zaXRpb24oKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGdldFNhdmVkU2Nyb2xsUG9zaXRpb24obG9jYXRpb24sIG1hdGNoZXMpIHsKICAgIGlmIChzYXZlZFNjcm9sbFBvc2l0aW9ucyAmJiBnZXRTY3JvbGxSZXN0b3JhdGlvbktleSAmJiBnZXRTY3JvbGxQb3NpdGlvbikgewogICAgICBsZXQgdXNlck1hdGNoZXMgPSBtYXRjaGVzLm1hcChtID0+IGNyZWF0ZVVzZU1hdGNoZXNNYXRjaChtLCBzdGF0ZS5sb2FkZXJEYXRhKSk7CiAgICAgIGxldCBrZXkgPSBnZXRTY3JvbGxSZXN0b3JhdGlvbktleShsb2NhdGlvbiwgdXNlck1hdGNoZXMpIHx8IGxvY2F0aW9uLmtleTsKICAgICAgbGV0IHkgPSBzYXZlZFNjcm9sbFBvc2l0aW9uc1trZXldOwoKICAgICAgaWYgKHR5cGVvZiB5ID09PSAibnVtYmVyIikgewogICAgICAgIHJldHVybiB5OwogICAgICB9CiAgICB9CgogICAgcmV0dXJuIG51bGw7CiAgfQoKICByb3V0ZXIgPSB7CiAgICBnZXQgYmFzZW5hbWUoKSB7CiAgICAgIHJldHVybiBpbml0LmJhc2VuYW1lOwogICAgfSwKCiAgICBnZXQgc3RhdGUoKSB7CiAgICAgIHJldHVybiBzdGF0ZTsKICAgIH0sCgogICAgZ2V0IHJvdXRlcygpIHsKICAgICAgcmV0dXJuIGRhdGFSb3V0ZXM7CiAgICB9LAoKICAgIGluaXRpYWxpemUsCiAgICBzdWJzY3JpYmUsCiAgICBlbmFibGVTY3JvbGxSZXN0b3JhdGlvbiwKICAgIG5hdmlnYXRlLAogICAgZmV0Y2gsCiAgICByZXZhbGlkYXRlLAogICAgLy8gUGFzc3Rocm91Z2ggdG8gaGlzdG9yeS1hd2FyZSBjcmVhdGVIcmVmIHVzZWQgYnkgdXNlSHJlZiBzbyB3ZSBnZXQgcHJvcGVyCiAgICAvLyBoYXNoLWF3YXJlIFVSTHMgaW4gRE9NIHBhdGhzCiAgICBjcmVhdGVIcmVmOiB0byA9PiBpbml0Lmhpc3RvcnkuY3JlYXRlSHJlZih0byksCiAgICBnZXRGZXRjaGVyLAogICAgZGVsZXRlRmV0Y2hlciwKICAgIGRpc3Bvc2UsCiAgICBfaW50ZXJuYWxGZXRjaENvbnRyb2xsZXJzOiBmZXRjaENvbnRyb2xsZXJzLAogICAgX2ludGVybmFsQWN0aXZlRGVmZXJyZWRzOiBhY3RpdmVEZWZlcnJlZHMKICB9OwogIHJldHVybiByb3V0ZXI7Cn0gLy8jZW5kcmVnaW9uCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCi8vI3JlZ2lvbiBjcmVhdGVTdGF0aWNIYW5kbGVyCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgpjb25zdCB2YWxpZEFjdGlvbk1ldGhvZHMgPSBuZXcgU2V0KFsiUE9TVCIsICJQVVQiLCAiUEFUQ0giLCAiREVMRVRFIl0pOwpjb25zdCB2YWxpZFJlcXVlc3RNZXRob2RzID0gbmV3IFNldChbIkdFVCIsICJIRUFEIiwgLi4udmFsaWRBY3Rpb25NZXRob2RzXSk7CmZ1bmN0aW9uIHVuc3RhYmxlX2NyZWF0ZVN0YXRpY0hhbmRsZXIocm91dGVzKSB7CiAgcm91dGVyX2ludmFyaWFudChyb3V0ZXMubGVuZ3RoID4gMCwgIllvdSBtdXN0IHByb3ZpZGUgYSBub24tZW1wdHkgcm91dGVzIGFycmF5IHRvIHVuc3RhYmxlX2NyZWF0ZVN0YXRpY0hhbmRsZXIiKTsKICBsZXQgZGF0YVJvdXRlcyA9IGNvbnZlcnRSb3V0ZXNUb0RhdGFSb3V0ZXMocm91dGVzKTsKICAvKioKICAgKiBUaGUgcXVlcnkoKSBtZXRob2QgaXMgaW50ZW5kZWQgZm9yIGRvY3VtZW50IHJlcXVlc3RzLCBpbiB3aGljaCB3ZSB3YW50IHRvCiAgICogY2FsbCBhbiBvcHRpb25hbCBhY3Rpb24gYW5kIHBvdGVudGlhbGx5IG11bHRpcGxlIGxvYWRlcnMgZm9yIGFsbCBuZXN0ZWQKICAgKiByb3V0ZXMuICBJdCByZXR1cm5zIGEgU3RhdGljSGFuZGxlckNvbnRleHQgb2JqZWN0LCB3aGljaCBpcyB2ZXJ5IHNpbWlsYXIKICAgKiB0byB0aGUgcm91dGVyIHN0YXRlIChsb2NhdGlvbiwgbG9hZGVyRGF0YSwgYWN0aW9uRGF0YSwgZXJyb3JzLCBldGMuKSBhbmQKICAgKiBhbHNvIGFkZHMgU1NSLXNwZWNpZmljIGluZm9ybWF0aW9uIHN1Y2ggYXMgdGhlIHN0YXR1c0NvZGUgYW5kIGhlYWRlcnMKICAgKiBmcm9tIGFjdGlvbi9sb2FkZXJzIFJlc3BvbnNlcy4KICAgKgogICAqIEl0IF9zaG91bGRfIG5ldmVyIHRocm93IGFuZCBzaG91bGQgcmVwb3J0IGFsbCBlcnJvcnMgdGhyb3VnaCB0aGUKICAgKiByZXR1cm5lZCBjb250ZXh0LmVycm9ycyBvYmplY3QsIHByb3Blcmx5IGFzc29jaWF0aW5nIGVycm9ycyB0byB0aGVpciBlcnJvcgogICAqIGJvdW5kYXJ5LiAgQWRkaXRpb25hbGx5LCBpdCB0cmFja3MgX2RlZXBlc3RSZW5kZXJlZEJvdW5kYXJ5SWQgd2hpY2ggY2FuIGJlCiAgICogdXNlZCB0byBlbXVsYXRlIFJlYWN0IGVycm9yIGJvdW5kYXJpZXMgZHVyaW5nIFNTciBieSBwZXJmb3JtaW5nIGEgc2Vjb25kCiAgICogcGFzcyBvbmx5IGRvd24gdG8gdGhlIGJvdW5kYXJ5SWQuCiAgICoKICAgKiBUaGUgb25lIGV4Y2VwdGlvbiB3aGVyZSB3ZSBkbyBub3QgcmV0dXJuIGEgU3RhdGljSGFuZGxlckNvbnRleHQgaXMgd2hlbiBhCiAgICogcmVkaXJlY3QgcmVzcG9uc2UgaXMgcmV0dXJuZWQgb3IgdGhyb3duIGZyb20gYW55IGFjdGlvbi9sb2FkZXIuICBXZQogICAqIHByb3BhZ2F0ZSB0aGF0IG91dCBhbmQgcmV0dXJuIHRoZSByYXcgUmVzcG9uc2Ugc28gdGhlIEhUVFAgc2VydmVyIGNhbgogICAqIHJldHVybiBpdCBkaXJlY3RseS4KICAgKi8KCiAgYXN5bmMgZnVuY3Rpb24gcXVlcnkocmVxdWVzdCkgewogICAgbGV0IHVybCA9IG5ldyBVUkwocmVxdWVzdC51cmwpOwogICAgbGV0IGxvY2F0aW9uID0gY3JlYXRlTG9jYXRpb24oIiIsIHJvdXRlcl9jcmVhdGVQYXRoKHVybCksIG51bGwsICJkZWZhdWx0Iik7CiAgICBsZXQgbWF0Y2hlcyA9IG1hdGNoUm91dGVzKGRhdGFSb3V0ZXMsIGxvY2F0aW9uKTsKCiAgICBpZiAoIXZhbGlkUmVxdWVzdE1ldGhvZHMuaGFzKHJlcXVlc3QubWV0aG9kKSkgewogICAgICBsZXQgewogICAgICAgIG1hdGNoZXM6IG1ldGhvZE5vdEFsbG93ZWRNYXRjaGVzLAogICAgICAgIHJvdXRlLAogICAgICAgIGVycm9yCiAgICAgIH0gPSBnZXRNZXRob2ROb3RBbGxvd2VkTWF0Y2hlcyhkYXRhUm91dGVzKTsKICAgICAgcmV0dXJuIHsKICAgICAgICBsb2NhdGlvbiwKICAgICAgICBtYXRjaGVzOiBtZXRob2ROb3RBbGxvd2VkTWF0Y2hlcywKICAgICAgICBsb2FkZXJEYXRhOiB7fSwKICAgICAgICBhY3Rpb25EYXRhOiBudWxsLAogICAgICAgIGVycm9yczogewogICAgICAgICAgW3JvdXRlLmlkXTogZXJyb3IKICAgICAgICB9LAogICAgICAgIHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1cywKICAgICAgICBsb2FkZXJIZWFkZXJzOiB7fSwKICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fQogICAgICB9OwogICAgfSBlbHNlIGlmICghbWF0Y2hlcykgewogICAgICBsZXQgewogICAgICAgIG1hdGNoZXM6IG5vdEZvdW5kTWF0Y2hlcywKICAgICAgICByb3V0ZSwKICAgICAgICBlcnJvcgogICAgICB9ID0gZ2V0Tm90Rm91bmRNYXRjaGVzKGRhdGFSb3V0ZXMpOwogICAgICByZXR1cm4gewogICAgICAgIGxvY2F0aW9uLAogICAgICAgIG1hdGNoZXM6IG5vdEZvdW5kTWF0Y2hlcywKICAgICAgICBsb2FkZXJEYXRhOiB7fSwKICAgICAgICBhY3Rpb25EYXRhOiBudWxsLAogICAgICAgIGVycm9yczogewogICAgICAgICAgW3JvdXRlLmlkXTogZXJyb3IKICAgICAgICB9LAogICAgICAgIHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1cywKICAgICAgICBsb2FkZXJIZWFkZXJzOiB7fSwKICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fQogICAgICB9OwogICAgfQoKICAgIGxldCByZXN1bHQgPSBhd2FpdCBxdWVyeUltcGwocmVxdWVzdCwgbG9jYXRpb24sIG1hdGNoZXMpOwoKICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBSZXNwb25zZSkgewogICAgICByZXR1cm4gcmVzdWx0OwogICAgfSAvLyBXaGVuIHJldHVybmluZyBTdGF0aWNIYW5kbGVyQ29udGV4dCwgd2UgcGF0Y2ggYmFjayBpbiB0aGUgbG9jYXRpb24gaGVyZQogICAgLy8gc2luY2Ugd2UgbmVlZCBpdCBmb3IgUmVhY3QgQ29udGV4dC4gIEJ1dCB0aGlzIGhlbHBzIGtlZXAgb3VyIHN1Ym1pdCBhbmQKICAgIC8vIGxvYWRSb3V0ZURhdGEgb3BlcmF0aW5nIG9uIGEgUmVxdWVzdCBpbnN0ZWFkIG9mIGEgTG9jYXRpb24KCgogICAgcmV0dXJuIF9leHRlbmRzKHsKICAgICAgbG9jYXRpb24KICAgIH0sIHJlc3VsdCk7CiAgfQogIC8qKgogICAqIFRoZSBxdWVyeVJvdXRlKCkgbWV0aG9kIGlzIGludGVuZGVkIGZvciB0YXJnZXRlZCByb3V0ZSByZXF1ZXN0cywgZWl0aGVyCiAgICogZm9yIGZldGNoID9fZGF0YSByZXF1ZXN0cyBvciByZXNvdXJjZSByb3V0ZSByZXF1ZXN0cy4gIEluIHRoaXMgY2FzZSwgd2UKICAgKiBhcmUgb25seSBldmVyIGNhbGxpbmcgYSBzaW5nbGUgYWN0aW9uIG9yIGxvYWRlciwgYW5kIHdlIGFyZSByZXR1cm5pbmcgdGhlCiAgICogcmV0dXJuZWQgdmFsdWUgZGlyZWN0bHkuICBJbiBtb3N0IGNhc2VzLCB0aGlzIHdpbGwgYmUgYSBSZXNwb25zZSByZXR1cm5lZAogICAqIGZyb20gdGhlIGFjdGlvbi9sb2FkZXIsIGJ1dCBpdCBtYXkgYmUgYSBwcmltaXRpdmUgb3Igb3RoZXIgdmFsdWUgYXMgd2VsbCAtCiAgICogYW5kIGluIHN1Y2ggY2FzZXMgdGhlIGNhbGxpbmcgY29udGV4dCBzaG91bGQgaGFuZGxlIHRoYXQgYWNjb3JkaW5nbHkuCiAgICoKICAgKiBXZSBkbyByZXNwZWN0IHRoZSB0aHJvdy9yZXR1cm4gZGlmZmVyZW50aWF0aW9uLCBzbyBpZiBhbiBhY3Rpb24vbG9hZGVyCiAgICogdGhyb3dzLCB0aGVuIHRoaXMgbWV0aG9kIHdpbGwgdGhyb3cgdGhlIHZhbHVlLiAgVGhpcyBpcyBpbXBvcnRhbnQgc28gd2UKICAgKiBjYW4gZG8gcHJvcGVyIGJvdW5kYXJ5IGlkZW50aWZpY2F0aW9uIGluIFJlbWl4IHdoZXJlIGEgdGhyb3duIFJlc3BvbnNlCiAgICogbXVzdCBnbyB0byB0aGUgQ2F0Y2ggQm91bmRhcnkgYnV0IGEgcmV0dXJuZWQgUmVzcG9uc2UgaXMgaGFwcHktcGF0aC4KICAgKgogICAqIE9uZSB0aGluZyB0byBub3RlIGlzIHRoYXQgYW55IFJvdXRlci1pbml0aWF0ZWQgdGhyb3duIFJlc3BvbnNlIChzdWNoIGFzIGEKICAgKiA0MDQgb3IgNDA1KSB3aWxsIGhhdmUgYSBjdXN0b20gWC1SZW1peC1Sb3V0ZXItRXJyb3I6ICJ5ZXMiIGhlYWRlciBvbiBpdAogICAqIGluIG9yZGVyIHRvIGRpZmZlcmVudGlhdGUgZnJvbSByZXNwb25zZXMgdGhyb3duIGZyb20gdXNlciBhY3Rpb25zL2xvYWRlcnMuCiAgICovCgoKICBhc3luYyBmdW5jdGlvbiBxdWVyeVJvdXRlKHJlcXVlc3QsIHJvdXRlSWQpIHsKICAgIGxldCB1cmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsKTsKICAgIGxldCBsb2NhdGlvbiA9IGNyZWF0ZUxvY2F0aW9uKCIiLCByb3V0ZXJfY3JlYXRlUGF0aCh1cmwpLCBudWxsLCAiZGVmYXVsdCIpOwogICAgbGV0IG1hdGNoZXMgPSBtYXRjaFJvdXRlcyhkYXRhUm91dGVzLCBsb2NhdGlvbik7CgogICAgaWYgKCF2YWxpZFJlcXVlc3RNZXRob2RzLmhhcyhyZXF1ZXN0Lm1ldGhvZCkpIHsKICAgICAgdGhyb3cgY3JlYXRlUm91dGVyRXJyb3JSZXNwb25zZShudWxsLCB7CiAgICAgICAgc3RhdHVzOiA0MDUsCiAgICAgICAgc3RhdHVzVGV4dDogIk1ldGhvZCBOb3QgQWxsb3dlZCIKICAgICAgfSk7CiAgICB9IGVsc2UgaWYgKCFtYXRjaGVzKSB7CiAgICAgIHRocm93IGNyZWF0ZVJvdXRlckVycm9yUmVzcG9uc2UobnVsbCwgewogICAgICAgIHN0YXR1czogNDA0LAogICAgICAgIHN0YXR1c1RleHQ6ICJOb3QgRm91bmQiCiAgICAgIH0pOwogICAgfQoKICAgIGxldCBtYXRjaCA9IHJvdXRlSWQgPyBtYXRjaGVzLmZpbmQobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSA6IGdldFRhcmdldE1hdGNoKG1hdGNoZXMsIGxvY2F0aW9uKTsKCiAgICBpZiAoIW1hdGNoKSB7CiAgICAgIHRocm93IGNyZWF0ZVJvdXRlckVycm9yUmVzcG9uc2UobnVsbCwgewogICAgICAgIHN0YXR1czogNDA0LAogICAgICAgIHN0YXR1c1RleHQ6ICJOb3QgRm91bmQiCiAgICAgIH0pOwogICAgfQoKICAgIGxldCByZXN1bHQgPSBhd2FpdCBxdWVyeUltcGwocmVxdWVzdCwgbG9jYXRpb24sIG1hdGNoZXMsIG1hdGNoKTsKCiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgUmVzcG9uc2UpIHsKICAgICAgcmV0dXJuIHJlc3VsdDsKICAgIH0KCiAgICBsZXQgZXJyb3IgPSByZXN1bHQuZXJyb3JzID8gT2JqZWN0LnZhbHVlcyhyZXN1bHQuZXJyb3JzKVswXSA6IHVuZGVmaW5lZDsKCiAgICBpZiAoZXJyb3IgIT09IHVuZGVmaW5lZCkgewogICAgICAvLyBJZiB3ZSBnb3QgYmFjayByZXN1bHQuZXJyb3JzLCB0aGF0IG1lYW5zIHRoZSBsb2FkZXIvYWN0aW9uIHRocmV3CiAgICAgIC8vIF9zb21ldGhpbmdfIHRoYXQgd2Fzbid0IGEgUmVzcG9uc2UsIGJ1dCBpdCdzIG5vdCBndWFyYW50ZWVkL3JlcXVpcmVkCiAgICAgIC8vIHRvIGJlIGFuIGBpbnN0YW5jZW9mIEVycm9yYCBlaXRoZXIsIHNvIHdlIGhhdmUgdG8gdXNlIHRocm93IGhlcmUgdG8KICAgICAgLy8gcHJlc2VydmUgdGhlICJlcnJvciIgc3RhdGUgb3V0c2lkZSBvZiBxdWVyeUltcGwuCiAgICAgIHRocm93IGVycm9yOwogICAgfSAvLyBQaWNrIG9mZiB0aGUgcmlnaHQgc3RhdGUgdmFsdWUgdG8gcmV0dXJuCgoKICAgIGxldCByb3V0ZURhdGEgPSBbcmVzdWx0LmFjdGlvbkRhdGEsIHJlc3VsdC5sb2FkZXJEYXRhXS5maW5kKHYgPT4gdik7CiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhyb3V0ZURhdGEgfHwge30pWzBdOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gcXVlcnlJbXBsKHJlcXVlc3QsIGxvY2F0aW9uLCBtYXRjaGVzLCByb3V0ZU1hdGNoKSB7CiAgICByb3V0ZXJfaW52YXJpYW50KHJlcXVlc3Quc2lnbmFsLCAicXVlcnkoKS9xdWVyeVJvdXRlKCkgcmVxdWVzdHMgbXVzdCBjb250YWluIGFuIEFib3J0Q29udHJvbGxlciBzaWduYWwiKTsKCiAgICB0cnkgewogICAgICBpZiAodmFsaWRBY3Rpb25NZXRob2RzLmhhcyhyZXF1ZXN0Lm1ldGhvZCkpIHsKICAgICAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3VibWl0KHJlcXVlc3QsIG1hdGNoZXMsIHJvdXRlTWF0Y2ggfHwgZ2V0VGFyZ2V0TWF0Y2gobWF0Y2hlcywgbG9jYXRpb24pLCByb3V0ZU1hdGNoICE9IG51bGwpOwogICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KCiAgICAgIGxldCByZXN1bHQgPSBhd2FpdCBsb2FkUm91dGVEYXRhKHJlcXVlc3QsIG1hdGNoZXMsIHJvdXRlTWF0Y2gpOwogICAgICByZXR1cm4gcmVzdWx0IGluc3RhbmNlb2YgUmVzcG9uc2UgPyByZXN1bHQgOiBfZXh0ZW5kcyh7fSwgcmVzdWx0LCB7CiAgICAgICAgYWN0aW9uRGF0YTogbnVsbCwKICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fQogICAgICB9KTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgLy8gSWYgdGhlIHVzZXIgdGhyZXcvcmV0dXJuZWQgYSBSZXNwb25zZSBpbiBjYWxsTG9hZGVyT3JBY3Rpb24sIHdlIHRocm93CiAgICAgIC8vIGl0IHRvIGJhaWwgb3V0IGFuZCB0aGVuIHJldHVybiBvciB0aHJvdyBoZXJlIGJhc2VkIG9uIHdoZXRoZXIgdGhlIHVzZXIKICAgICAgLy8gcmV0dXJuZWQgb3IgdGhyZXcKICAgICAgaWYgKGlzUXVlcnlSb3V0ZVJlc3BvbnNlKGUpKSB7CiAgICAgICAgaWYgKGUudHlwZSA9PT0gUmVzdWx0VHlwZS5lcnJvciAmJiAhaXNSZWRpcmVjdFJlc3BvbnNlKGUucmVzcG9uc2UpKSB7CiAgICAgICAgICB0aHJvdyBlLnJlc3BvbnNlOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGUucmVzcG9uc2U7CiAgICAgIH0gLy8gUmVkaXJlY3RzIGFyZSBhbHdheXMgcmV0dXJuZWQgc2luY2UgdGhleSBkb24ndCBwcm9wYWdhdGUgdG8gY2F0Y2gKICAgICAgLy8gYm91bmRhcmllcwoKCiAgICAgIGlmIChpc1JlZGlyZWN0UmVzcG9uc2UoZSkpIHsKICAgICAgICByZXR1cm4gZTsKICAgICAgfQoKICAgICAgdGhyb3cgZTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIHN1Ym1pdChyZXF1ZXN0LCBtYXRjaGVzLCBhY3Rpb25NYXRjaCwgaXNSb3V0ZVJlcXVlc3QpIHsKICAgIGxldCByZXN1bHQ7CgogICAgaWYgKCFhY3Rpb25NYXRjaC5yb3V0ZS5hY3Rpb24pIHsKICAgICAgaWYgKGlzUm91dGVSZXF1ZXN0KSB7CiAgICAgICAgdGhyb3cgY3JlYXRlUm91dGVyRXJyb3JSZXNwb25zZShudWxsLCB7CiAgICAgICAgICBzdGF0dXM6IDQwNSwKICAgICAgICAgIHN0YXR1c1RleHQ6ICJNZXRob2QgTm90IEFsbG93ZWQiCiAgICAgICAgfSk7CiAgICAgIH0KCiAgICAgIHJlc3VsdCA9IGdldE1ldGhvZE5vdEFsbG93ZWRSZXN1bHQocmVxdWVzdC51cmwpOwogICAgfSBlbHNlIHsKICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbExvYWRlck9yQWN0aW9uKCJhY3Rpb24iLCByZXF1ZXN0LCBhY3Rpb25NYXRjaCwgbWF0Y2hlcywgdW5kZWZpbmVkLCAvLyBCYXNlbmFtZSBub3QgY3VycmVudGx5IHN1cHBvcnRlZCBpbiBzdGF0aWMgaGFuZGxlcnMKICAgICAgdHJ1ZSwgaXNSb3V0ZVJlcXVlc3QpOwoKICAgICAgaWYgKHJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHsKICAgICAgICBsZXQgbWV0aG9kID0gaXNSb3V0ZVJlcXVlc3QgPyAicXVlcnlSb3V0ZSIgOiAicXVlcnkiOwogICAgICAgIHRocm93IG5ldyBFcnJvcihtZXRob2QgKyAiKCkgY2FsbCBhYm9ydGVkIik7CiAgICAgIH0KICAgIH0KCiAgICBpZiAoaXNSZWRpcmVjdFJlc3VsdChyZXN1bHQpKSB7CiAgICAgIC8vIFVoaGhoIC0gdGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuLCB3ZSBzaG91bGQgYWx3YXlzIHRocm93IHRoZXNlIGZyb20KICAgICAgLy8gY2FsbExvYWRlck9yQWN0aW9uLCBidXQgdGhlIHR5cGUgbmFycm93aW5nIGhlcmUga2VlcHMgVFMgaGFwcHkgYW5kIHdlCiAgICAgIC8vIGNhbiBnZXQgYmFjayBvbiB0aGUgInRocm93IGFsbCByZWRpcmVjdCByZXNwb25zZXMiIHRyYWluIGhlcmUgc2hvdWxkCiAgICAgIC8vIHRoaXMgZXZlciBoYXBwZW4gOi8KICAgICAgdGhyb3cgbmV3IFJlc3BvbnNlKG51bGwsIHsKICAgICAgICBzdGF0dXM6IHJlc3VsdC5zdGF0dXMsCiAgICAgICAgaGVhZGVyczogewogICAgICAgICAgTG9jYXRpb246IHJlc3VsdC5sb2NhdGlvbgogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoImRlZmVyKCkgaXMgbm90IHN1cHBvcnRlZCBpbiBhY3Rpb25zIik7CiAgICB9CgogICAgaWYgKGlzUm91dGVSZXF1ZXN0KSB7CiAgICAgIC8vIE5vdGU6IFRoaXMgc2hvdWxkIG9ubHkgYmUgbm9uLVJlc3BvbnNlIHZhbHVlcyBpZiB3ZSBnZXQgaGVyZSwgc2luY2UKICAgICAgLy8gaXNSb3V0ZVJlcXVlc3Qgc2hvdWxkIHRocm93IGFueSBSZXNwb25zZSByZWNlaXZlZCBpbiBjYWxsTG9hZGVyT3JBY3Rpb24KICAgICAgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkgewogICAgICAgIGxldCBib3VuZGFyeU1hdGNoID0gZmluZE5lYXJlc3RCb3VuZGFyeShtYXRjaGVzLCBhY3Rpb25NYXRjaC5yb3V0ZS5pZCk7CiAgICAgICAgcmV0dXJuIHsKICAgICAgICAgIG1hdGNoZXM6IFthY3Rpb25NYXRjaF0sCiAgICAgICAgICBsb2FkZXJEYXRhOiB7fSwKICAgICAgICAgIGFjdGlvbkRhdGE6IG51bGwsCiAgICAgICAgICBlcnJvcnM6IHsKICAgICAgICAgICAgW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZXJyb3IKICAgICAgICAgIH0sCiAgICAgICAgICAvLyBOb3RlOiBzdGF0dXNDb2RlICsgaGVhZGVycyBhcmUgdW51c2VkIGhlcmUgc2luY2UgcXVlcnlSb3V0ZSB3aWxsCiAgICAgICAgICAvLyByZXR1cm4gdGhlIHJhdyBSZXNwb25zZSBvciB2YWx1ZQogICAgICAgICAgc3RhdHVzQ29kZTogNTAwLAogICAgICAgICAgbG9hZGVySGVhZGVyczoge30sCiAgICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fQogICAgICAgIH07CiAgICAgIH0KCiAgICAgIHJldHVybiB7CiAgICAgICAgbWF0Y2hlczogW2FjdGlvbk1hdGNoXSwKICAgICAgICBsb2FkZXJEYXRhOiB7fSwKICAgICAgICBhY3Rpb25EYXRhOiB7CiAgICAgICAgICBbYWN0aW9uTWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZGF0YQogICAgICAgIH0sCiAgICAgICAgZXJyb3JzOiBudWxsLAogICAgICAgIC8vIE5vdGU6IHN0YXR1c0NvZGUgKyBoZWFkZXJzIGFyZSB1bnVzZWQgaGVyZSBzaW5jZSBxdWVyeVJvdXRlIHdpbGwKICAgICAgICAvLyByZXR1cm4gdGhlIHJhdyBSZXNwb25zZSBvciB2YWx1ZQogICAgICAgIHN0YXR1c0NvZGU6IDIwMCwKICAgICAgICBsb2FkZXJIZWFkZXJzOiB7fSwKICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fQogICAgICB9OwogICAgfQoKICAgIGlmIChpc0Vycm9yUmVzdWx0KHJlc3VsdCkpIHsKICAgICAgLy8gU3RvcmUgb2ZmIHRoZSBwZW5kaW5nIGVycm9yIC0gd2UgdXNlIGl0IHRvIGRldGVybWluZSB3aGljaCBsb2FkZXJzCiAgICAgIC8vIHRvIGNhbGwgYW5kIHdpbGwgY29tbWl0IGl0IHdoZW4gd2UgY29tcGxldGUgdGhlIG5hdmlnYXRpb24KICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIGFjdGlvbk1hdGNoLnJvdXRlLmlkKTsKICAgICAgbGV0IGNvbnRleHQgPSBhd2FpdCBsb2FkUm91dGVEYXRhKHJlcXVlc3QsIG1hdGNoZXMsIHVuZGVmaW5lZCwgewogICAgICAgIFtib3VuZGFyeU1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmVycm9yCiAgICAgIH0pOyAvLyBhY3Rpb24gc3RhdHVzIGNvZGVzIHRha2UgcHJlY2VkZW5jZSBvdmVyIGxvYWRlciBzdGF0dXMgY29kZXMKCiAgICAgIHJldHVybiBfZXh0ZW5kcyh7fSwgY29udGV4dCwgewogICAgICAgIHN0YXR1c0NvZGU6IGlzUm91dGVFcnJvclJlc3BvbnNlKHJlc3VsdC5lcnJvcikgPyByZXN1bHQuZXJyb3Iuc3RhdHVzIDogNTAwLAogICAgICAgIGFjdGlvbkRhdGE6IG51bGwsCiAgICAgICAgYWN0aW9uSGVhZGVyczogX2V4dGVuZHMoe30sIHJlc3VsdC5oZWFkZXJzID8gewogICAgICAgICAgW2FjdGlvbk1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmhlYWRlcnMKICAgICAgICB9IDoge30pCiAgICAgIH0pOwogICAgfQoKICAgIGxldCBjb250ZXh0ID0gYXdhaXQgbG9hZFJvdXRlRGF0YShyZXF1ZXN0LCBtYXRjaGVzKTsKICAgIHJldHVybiBfZXh0ZW5kcyh7fSwgY29udGV4dCwgcmVzdWx0LnN0YXR1c0NvZGUgPyB7CiAgICAgIHN0YXR1c0NvZGU6IHJlc3VsdC5zdGF0dXNDb2RlCiAgICB9IDoge30sIHsKICAgICAgYWN0aW9uRGF0YTogewogICAgICAgIFthY3Rpb25NYXRjaC5yb3V0ZS5pZF06IHJlc3VsdC5kYXRhCiAgICAgIH0sCiAgICAgIGFjdGlvbkhlYWRlcnM6IF9leHRlbmRzKHt9LCByZXN1bHQuaGVhZGVycyA/IHsKICAgICAgICBbYWN0aW9uTWF0Y2gucm91dGUuaWRdOiByZXN1bHQuaGVhZGVycwogICAgICB9IDoge30pCiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGxvYWRSb3V0ZURhdGEocmVxdWVzdCwgbWF0Y2hlcywgcm91dGVNYXRjaCwgcGVuZGluZ0FjdGlvbkVycm9yKSB7CiAgICBsZXQgaXNSb3V0ZVJlcXVlc3QgPSByb3V0ZU1hdGNoICE9IG51bGw7CiAgICBsZXQgcmVxdWVzdE1hdGNoZXMgPSByb3V0ZU1hdGNoID8gW3JvdXRlTWF0Y2hdIDogZ2V0TG9hZGVyTWF0Y2hlc1VudGlsQm91bmRhcnkobWF0Y2hlcywgT2JqZWN0LmtleXMocGVuZGluZ0FjdGlvbkVycm9yIHx8IHt9KVswXSk7CiAgICBsZXQgbWF0Y2hlc1RvTG9hZCA9IHJlcXVlc3RNYXRjaGVzLmZpbHRlcihtID0+IG0ucm91dGUubG9hZGVyKTsgLy8gU2hvcnQgY2lyY3VpdCBpZiB3ZSBoYXZlIG5vIGxvYWRlcnMgdG8gcnVuCgogICAgaWYgKG1hdGNoZXNUb0xvYWQubGVuZ3RoID09PSAwKSB7CiAgICAgIHJldHVybiB7CiAgICAgICAgbWF0Y2hlcywKICAgICAgICBsb2FkZXJEYXRhOiB7fSwKICAgICAgICBlcnJvcnM6IHBlbmRpbmdBY3Rpb25FcnJvciB8fCBudWxsLAogICAgICAgIHN0YXR1c0NvZGU6IDIwMCwKICAgICAgICBsb2FkZXJIZWFkZXJzOiB7fQogICAgICB9OwogICAgfQoKICAgIGxldCByZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoWy4uLm1hdGNoZXNUb0xvYWQubWFwKG1hdGNoID0+IGNhbGxMb2FkZXJPckFjdGlvbigibG9hZGVyIiwgcmVxdWVzdCwgbWF0Y2gsIG1hdGNoZXMsIHVuZGVmaW5lZCwgLy8gQmFzZW5hbWUgbm90IGN1cnJlbnRseSBzdXBwb3J0ZWQgaW4gc3RhdGljIGhhbmRsZXJzCiAgICB0cnVlLCBpc1JvdXRlUmVxdWVzdCkpXSk7CgogICAgaWYgKHJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHsKICAgICAgbGV0IG1ldGhvZCA9IGlzUm91dGVSZXF1ZXN0ID8gInF1ZXJ5Um91dGUiIDogInF1ZXJ5IjsKICAgICAgdGhyb3cgbmV3IEVycm9yKG1ldGhvZCArICIoKSBjYWxsIGFib3J0ZWQiKTsKICAgIH0gLy8gQ2FuJ3QgZG8gYW55dGhpbmcgd2l0aCB0aGVzZSB3aXRob3V0IHRoZSBSZW1peCBzaWRlIG9mIHRoaW5ncywgc28ganVzdAogICAgLy8gY2FuY2VsIHRoZW0gZm9yIG5vdwoKCiAgICByZXN1bHRzLmZvckVhY2gocmVzdWx0ID0+IHsKICAgICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSkgewogICAgICAgIHJlc3VsdC5kZWZlcnJlZERhdGEuY2FuY2VsKCk7CiAgICAgIH0KICAgIH0pOyAvLyBQcm9jZXNzIGFuZCBjb21taXQgb3V0cHV0IGZyb20gbG9hZGVycwoKICAgIGxldCBjb250ZXh0ID0gcHJvY2Vzc1JvdXRlTG9hZGVyRGF0YShtYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCByZXN1bHRzLCBwZW5kaW5nQWN0aW9uRXJyb3IpOwogICAgcmV0dXJuIF9leHRlbmRzKHt9LCBjb250ZXh0LCB7CiAgICAgIG1hdGNoZXMKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gY3JlYXRlUm91dGVyRXJyb3JSZXNwb25zZShib2R5LCBpbml0KSB7CiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKGJvZHksIF9leHRlbmRzKHt9LCBpbml0LCB7CiAgICAgIGhlYWRlcnM6IF9leHRlbmRzKHt9LCBpbml0LmhlYWRlcnMsIHsKICAgICAgICAiWC1SZW1peC1Sb3V0ZXItRXJyb3IiOiAieWVzIgogICAgICB9KQogICAgfSkpOwogIH0KCiAgcmV0dXJuIHsKICAgIGRhdGFSb3V0ZXMsCiAgICBxdWVyeSwKICAgIHF1ZXJ5Um91dGUKICB9Owp9IC8vI2VuZHJlZ2lvbgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyNyZWdpb24gSGVscGVycwovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKLyoqCiAqIEdpdmVuIGFuIGV4aXN0aW5nIFN0YXRpY0hhbmRsZXJDb250ZXh0IGFuZCBhbiBlcnJvciB0aHJvd24gYXQgcmVuZGVyIHRpbWUsCiAqIHByb3ZpZGUgYW4gdXBkYXRlZCBTdGF0aWNIYW5kbGVyQ29udGV4dCBzdWl0YWJsZSBmb3IgYSBzZWNvbmQgU1NSIHJlbmRlcgogKi8KCmZ1bmN0aW9uIGdldFN0YXRpY0NvbnRleHRGcm9tRXJyb3Iocm91dGVzLCBjb250ZXh0LCBlcnJvcikgewogIGxldCBuZXdDb250ZXh0ID0gX2V4dGVuZHMoe30sIGNvbnRleHQsIHsKICAgIHN0YXR1c0NvZGU6IDUwMCwKICAgIGVycm9yczogewogICAgICBbY29udGV4dC5fZGVlcGVzdFJlbmRlcmVkQm91bmRhcnlJZCB8fCByb3V0ZXNbMF0uaWRdOiBlcnJvcgogICAgfQogIH0pOwoKICByZXR1cm4gbmV3Q29udGV4dDsKfSAvLyBOb3JtYWxpemUgbmF2aWdhdGlvbiBvcHRpb25zIGJ5IGNvbnZlcnRpbmcgZm9ybU1ldGhvZD1HRVQgZm9ybURhdGEgb2JqZWN0cyB0bwovLyBVUkxTZWFyY2hQYXJhbXMgc28gdGhleSBiZWhhdmUgaWRlbnRpY2FsbHkgdG8gbGlua3Mgd2l0aCBxdWVyeSBwYXJhbXMKCmZ1bmN0aW9uIG5vcm1hbGl6ZU5hdmlnYXRlT3B0aW9ucyh0bywgb3B0cywgaXNGZXRjaGVyKSB7CiAgaWYgKGlzRmV0Y2hlciA9PT0gdm9pZCAwKSB7CiAgICBpc0ZldGNoZXIgPSBmYWxzZTsKICB9CgogIGxldCBwYXRoID0gdHlwZW9mIHRvID09PSAic3RyaW5nIiA/IHRvIDogcm91dGVyX2NyZWF0ZVBhdGgodG8pOyAvLyBSZXR1cm4gbG9jYXRpb24gdmVyYmF0aW0gb24gbm9uLXN1Ym1pc3Npb24gbmF2aWdhdGlvbnMKCiAgaWYgKCFvcHRzIHx8ICEoImZvcm1NZXRob2QiIGluIG9wdHMpICYmICEoImZvcm1EYXRhIiBpbiBvcHRzKSkgewogICAgcmV0dXJuIHsKICAgICAgcGF0aAogICAgfTsKICB9IC8vIENyZWF0ZSBhIFN1Ym1pc3Npb24gb24gbm9uLUdFVCBuYXZpZ2F0aW9ucwoKCiAgaWYgKG9wdHMuZm9ybU1ldGhvZCAhPSBudWxsICYmIG9wdHMuZm9ybU1ldGhvZCAhPT0gImdldCIpIHsKICAgIHJldHVybiB7CiAgICAgIHBhdGgsCiAgICAgIHN1Ym1pc3Npb246IHsKICAgICAgICBmb3JtTWV0aG9kOiBvcHRzLmZvcm1NZXRob2QsCiAgICAgICAgZm9ybUFjdGlvbjogc3RyaXBIYXNoRnJvbVBhdGgocGF0aCksCiAgICAgICAgZm9ybUVuY1R5cGU6IG9wdHMgJiYgb3B0cy5mb3JtRW5jVHlwZSB8fCAiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIiwKICAgICAgICBmb3JtRGF0YTogb3B0cy5mb3JtRGF0YQogICAgICB9CiAgICB9OwogIH0gLy8gTm8gZm9ybURhdGEgdG8gZmxhdHRlbiBmb3IgR0VUIHN1Ym1pc3Npb24KCgogIGlmICghb3B0cy5mb3JtRGF0YSkgewogICAgcmV0dXJuIHsKICAgICAgcGF0aAogICAgfTsKICB9IC8vIEZsYXR0ZW4gc3VibWlzc2lvbiBvbnRvIFVSTFNlYXJjaFBhcmFtcyBmb3IgR0VUIHN1Ym1pc3Npb25zCgoKICBsZXQgcGFyc2VkUGF0aCA9IHBhcnNlUGF0aChwYXRoKTsKCiAgdHJ5IHsKICAgIGxldCBzZWFyY2hQYXJhbXMgPSBjb252ZXJ0Rm9ybURhdGFUb1NlYXJjaFBhcmFtcyhvcHRzLmZvcm1EYXRhKTsgLy8gU2luY2UgZmV0Y2hlciBHRVQgc3VibWlzc2lvbnMgb25seSBydW4gYSBzaW5nbGUgbG9hZGVyIChhcyBvcHBvc2VkIHRvCiAgICAvLyBuYXZpZ2F0aW9uIEdFVCBzdWJtaXNzaW9ucyB3aGljaCBydW4gYWxsIGxvYWRlcnMpLCB3ZSBuZWVkIHRvIHByZXNlcnZlCiAgICAvLyBhbnkgaW5jb21pbmcgP2luZGV4IHBhcmFtcwoKICAgIGlmIChpc0ZldGNoZXIgJiYgcGFyc2VkUGF0aC5zZWFyY2ggJiYgaGFzTmFrZWRJbmRleFF1ZXJ5KHBhcnNlZFBhdGguc2VhcmNoKSkgewogICAgICBzZWFyY2hQYXJhbXMuYXBwZW5kKCJpbmRleCIsICIiKTsKICAgIH0KCiAgICBwYXJzZWRQYXRoLnNlYXJjaCA9ICI/IiArIHNlYXJjaFBhcmFtczsKICB9IGNhdGNoIChlKSB7CiAgICByZXR1cm4gewogICAgICBwYXRoLAogICAgICBlcnJvcjogbmV3IEVycm9yUmVzcG9uc2UoNDAwLCAiQmFkIFJlcXVlc3QiLCAiQ2Fubm90IHN1Ym1pdCBiaW5hcnkgZm9ybSBkYXRhIHVzaW5nIEdFVCIpCiAgICB9OwogIH0KCiAgcmV0dXJuIHsKICAgIHBhdGg6IHJvdXRlcl9jcmVhdGVQYXRoKHBhcnNlZFBhdGgpCiAgfTsKfQoKZnVuY3Rpb24gZ2V0TG9hZGVyUmVkaXJlY3Qoc3RhdGUsIHJlZGlyZWN0KSB7CiAgbGV0IHsKICAgIGZvcm1NZXRob2QsCiAgICBmb3JtQWN0aW9uLAogICAgZm9ybUVuY1R5cGUsCiAgICBmb3JtRGF0YQogIH0gPSBzdGF0ZS5uYXZpZ2F0aW9uOwogIGxldCBuYXZpZ2F0aW9uID0gewogICAgc3RhdGU6ICJsb2FkaW5nIiwKICAgIGxvY2F0aW9uOiBjcmVhdGVMb2NhdGlvbihzdGF0ZS5sb2NhdGlvbiwgcmVkaXJlY3QubG9jYXRpb24pLAogICAgZm9ybU1ldGhvZDogZm9ybU1ldGhvZCB8fCB1bmRlZmluZWQsCiAgICBmb3JtQWN0aW9uOiBmb3JtQWN0aW9uIHx8IHVuZGVmaW5lZCwKICAgIGZvcm1FbmNUeXBlOiBmb3JtRW5jVHlwZSB8fCB1bmRlZmluZWQsCiAgICBmb3JtRGF0YTogZm9ybURhdGEgfHwgdW5kZWZpbmVkCiAgfTsKICByZXR1cm4gbmF2aWdhdGlvbjsKfSAvLyBGaWx0ZXIgb3V0IGFsbCByb3V0ZXMgYmVsb3cgYW55IGNhdWdodCBlcnJvciBhcyB0aGV5IGFyZW4ndCBnb2luZyB0bwovLyByZW5kZXIgc28gd2UgZG9uJ3QgbmVlZCB0byBsb2FkIHRoZW0KCgpmdW5jdGlvbiBnZXRMb2FkZXJNYXRjaGVzVW50aWxCb3VuZGFyeShtYXRjaGVzLCBib3VuZGFyeUlkKSB7CiAgbGV0IGJvdW5kYXJ5TWF0Y2hlcyA9IG1hdGNoZXM7CgogIGlmIChib3VuZGFyeUlkKSB7CiAgICBsZXQgaW5kZXggPSBtYXRjaGVzLmZpbmRJbmRleChtID0+IG0ucm91dGUuaWQgPT09IGJvdW5kYXJ5SWQpOwoKICAgIGlmIChpbmRleCA+PSAwKSB7CiAgICAgIGJvdW5kYXJ5TWF0Y2hlcyA9IG1hdGNoZXMuc2xpY2UoMCwgaW5kZXgpOwogICAgfQogIH0KCiAgcmV0dXJuIGJvdW5kYXJ5TWF0Y2hlczsKfQoKZnVuY3Rpb24gZ2V0TWF0Y2hlc1RvTG9hZChzdGF0ZSwgbWF0Y2hlcywgc3VibWlzc2lvbiwgbG9jYXRpb24sIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQsIGNhbmNlbGxlZERlZmVycmVkUm91dGVzLCBjYW5jZWxsZWRGZXRjaGVyTG9hZHMsIHBlbmRpbmdBY3Rpb25EYXRhLCBwZW5kaW5nRXJyb3IsIGZldGNoTG9hZE1hdGNoZXMpIHsKICBsZXQgYWN0aW9uUmVzdWx0ID0gcGVuZGluZ0Vycm9yID8gT2JqZWN0LnZhbHVlcyhwZW5kaW5nRXJyb3IpWzBdIDogcGVuZGluZ0FjdGlvbkRhdGEgPyBPYmplY3QudmFsdWVzKHBlbmRpbmdBY3Rpb25EYXRhKVswXSA6IG51bGw7IC8vIFBpY2sgbmF2aWdhdGlvbiBtYXRjaGVzIHRoYXQgYXJlIG5ldC1uZXcgb3IgcXVhbGlmeSBmb3IgcmV2YWxpZGF0aW9uCgogIGxldCBib3VuZGFyeUlkID0gcGVuZGluZ0Vycm9yID8gT2JqZWN0LmtleXMocGVuZGluZ0Vycm9yKVswXSA6IHVuZGVmaW5lZDsKICBsZXQgYm91bmRhcnlNYXRjaGVzID0gZ2V0TG9hZGVyTWF0Y2hlc1VudGlsQm91bmRhcnkobWF0Y2hlcywgYm91bmRhcnlJZCk7CiAgbGV0IG5hdmlnYXRpb25NYXRjaGVzID0gYm91bmRhcnlNYXRjaGVzLmZpbHRlcigobWF0Y2gsIGluZGV4KSA9PiBtYXRjaC5yb3V0ZS5sb2FkZXIgIT0gbnVsbCAmJiAoaXNOZXdMb2FkZXIoc3RhdGUubG9hZGVyRGF0YSwgc3RhdGUubWF0Y2hlc1tpbmRleF0sIG1hdGNoKSB8fCAvLyBJZiB0aGlzIHJvdXRlIGhhZCBhIHBlbmRpbmcgZGVmZXJyZWQgY2FuY2VsbGVkIGl0IG11c3QgYmUgcmV2YWxpZGF0ZWQKICBjYW5jZWxsZWREZWZlcnJlZFJvdXRlcy5zb21lKGlkID0+IGlkID09PSBtYXRjaC5yb3V0ZS5pZCkgfHwgc2hvdWxkUmV2YWxpZGF0ZUxvYWRlcihzdGF0ZS5sb2NhdGlvbiwgc3RhdGUubWF0Y2hlc1tpbmRleF0sIHN1Ym1pc3Npb24sIGxvY2F0aW9uLCBtYXRjaCwgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCwgYWN0aW9uUmVzdWx0KSkpOyAvLyBQaWNrIGZldGNoZXIubG9hZHMgdGhhdCBuZWVkIHRvIGJlIHJldmFsaWRhdGVkCgogIGxldCByZXZhbGlkYXRpbmdGZXRjaGVycyA9IFtdOwogIGZldGNoTG9hZE1hdGNoZXMgJiYgZmV0Y2hMb2FkTWF0Y2hlcy5mb3JFYWNoKChfcmVmMTAsIGtleSkgPT4gewogICAgbGV0IFtocmVmLCBtYXRjaCwgZmV0Y2hNYXRjaGVzXSA9IF9yZWYxMDsKCiAgICAvLyBUaGlzIGZldGNoZXIgd2FzIGNhbmNlbGxlZCBmcm9tIGEgcHJpb3IgYWN0aW9uIHN1Ym1pc3Npb24gLSBmb3JjZSByZWxvYWQKICAgIGlmIChjYW5jZWxsZWRGZXRjaGVyTG9hZHMuaW5jbHVkZXMoa2V5KSkgewogICAgICByZXZhbGlkYXRpbmdGZXRjaGVycy5wdXNoKFtrZXksIGhyZWYsIG1hdGNoLCBmZXRjaE1hdGNoZXNdKTsKICAgIH0gZWxzZSBpZiAoaXNSZXZhbGlkYXRpb25SZXF1aXJlZCkgewogICAgICBsZXQgc2hvdWxkUmV2YWxpZGF0ZSA9IHNob3VsZFJldmFsaWRhdGVMb2FkZXIoaHJlZiwgbWF0Y2gsIHN1Ym1pc3Npb24sIGhyZWYsIG1hdGNoLCBpc1JldmFsaWRhdGlvblJlcXVpcmVkLCBhY3Rpb25SZXN1bHQpOwoKICAgICAgaWYgKHNob3VsZFJldmFsaWRhdGUpIHsKICAgICAgICByZXZhbGlkYXRpbmdGZXRjaGVycy5wdXNoKFtrZXksIGhyZWYsIG1hdGNoLCBmZXRjaE1hdGNoZXNdKTsKICAgICAgfQogICAgfQogIH0pOwogIHJldHVybiBbbmF2aWdhdGlvbk1hdGNoZXMsIHJldmFsaWRhdGluZ0ZldGNoZXJzXTsKfQoKZnVuY3Rpb24gaXNOZXdMb2FkZXIoY3VycmVudExvYWRlckRhdGEsIGN1cnJlbnRNYXRjaCwgbWF0Y2gpIHsKICBsZXQgaXNOZXcgPSAvLyBbYV0gLT4gW2EsIGJdCiAgIWN1cnJlbnRNYXRjaCB8fCAvLyBbYSwgYl0gLT4gW2EsIGNdCiAgbWF0Y2gucm91dGUuaWQgIT09IGN1cnJlbnRNYXRjaC5yb3V0ZS5pZDsgLy8gSGFuZGxlIHRoZSBjYXNlIHRoYXQgd2UgZG9uJ3QgaGF2ZSBkYXRhIGZvciBhIHJlLXVzZWQgcm91dGUsIHBvdGVudGlhbGx5CiAgLy8gZnJvbSBhIHByaW9yIGVycm9yIG9yIGZyb20gYSBjYW5jZWxsZWQgcGVuZGluZyBkZWZlcnJlZAoKICBsZXQgaXNNaXNzaW5nRGF0YSA9IGN1cnJlbnRMb2FkZXJEYXRhW21hdGNoLnJvdXRlLmlkXSA9PT0gdW5kZWZpbmVkOyAvLyBBbHdheXMgbG9hZCBpZiB0aGlzIGlzIGEgbmV0LW5ldyByb3V0ZSBvciB3ZSBkb24ndCB5ZXQgaGF2ZSBkYXRhCgogIHJldHVybiBpc05ldyB8fCBpc01pc3NpbmdEYXRhOwp9CgpmdW5jdGlvbiBpc05ld1JvdXRlSW5zdGFuY2UoY3VycmVudE1hdGNoLCBtYXRjaCkgewogIGxldCBjdXJyZW50UGF0aCA9IGN1cnJlbnRNYXRjaC5yb3V0ZS5wYXRoOwogIHJldHVybiAoLy8gcGFyYW0gY2hhbmdlIGZvciB0aGlzIG1hdGNoLCAvdXNlcnMvMTIzIC0+IC91c2Vycy80NTYKICAgIGN1cnJlbnRNYXRjaC5wYXRobmFtZSAhPT0gbWF0Y2gucGF0aG5hbWUgfHwgLy8gc3BsYXQgcGFyYW0gY2hhbmdlZCwgd2hpY2ggaXMgbm90IHByZXNlbnQgaW4gbWF0Y2gucGF0aAogICAgLy8gZS5nLiAvZmlsZXMvaW1hZ2VzL2F2YXRhci5qcGcgLT4gZmlsZXMvZmluYW5jZXMueGxzCiAgICBjdXJyZW50UGF0aCAmJiBjdXJyZW50UGF0aC5lbmRzV2l0aCgiKiIpICYmIGN1cnJlbnRNYXRjaC5wYXJhbXNbIioiXSAhPT0gbWF0Y2gucGFyYW1zWyIqIl0KICApOwp9CgpmdW5jdGlvbiBzaG91bGRSZXZhbGlkYXRlTG9hZGVyKGN1cnJlbnRMb2NhdGlvbiwgY3VycmVudE1hdGNoLCBzdWJtaXNzaW9uLCBsb2NhdGlvbiwgbWF0Y2gsIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQsIGFjdGlvblJlc3VsdCkgewogIGxldCBjdXJyZW50VXJsID0gY3JlYXRlVVJMKGN1cnJlbnRMb2NhdGlvbik7CiAgbGV0IGN1cnJlbnRQYXJhbXMgPSBjdXJyZW50TWF0Y2gucGFyYW1zOwogIGxldCBuZXh0VXJsID0gY3JlYXRlVVJMKGxvY2F0aW9uKTsKICBsZXQgbmV4dFBhcmFtcyA9IG1hdGNoLnBhcmFtczsgLy8gVGhpcyBpcyB0aGUgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbiBhcyB0byB3aGVuIHdlIHJldmFsaWRhdGUuICBJZiB0aGUgcm91dGUKICAvLyBwcm92aWRlcyBpdCdzIG93biBpbXBsZW1lbnRhdGlvbiwgdGhlbiB3ZSBnaXZlIHRoZW0gZnVsbCBjb250cm9sIGJ1dAogIC8vIHByb3ZpZGUgdGhpcyB2YWx1ZSBzbyB0aGV5IGNhbiBsZXZlcmFnZSBpdCBpZiBuZWVkZWQgYWZ0ZXIgdGhleSBjaGVjawogIC8vIHRoZWlyIG93biBzcGVjaWZpYyB1c2UgY2FzZXMKICAvLyBOb3RlIHRoYXQgZmV0Y2hlcnMgYWx3YXlzIHByb3ZpZGUgdGhlIHNhbWUgY3VycmVudC9uZXh0IGxvY2F0aW9ucyBzbyB0aGUKICAvLyBVUkwtYmFzZWQgY2hlY2tzIGhlcmUgZG9uJ3QgYXBwbHkgdG8gZmV0Y2hlciBzaG91bGRSZXZhbGlkYXRlIGNhbGxzCgogIGxldCBkZWZhdWx0U2hvdWxkUmV2YWxpZGF0ZSA9IGlzTmV3Um91dGVJbnN0YW5jZShjdXJyZW50TWF0Y2gsIG1hdGNoKSB8fCAvLyBDbGlja2VkIHRoZSBzYW1lIGxpbmssIHJlc3VibWl0dGVkIGEgR0VUIGZvcm0KICBjdXJyZW50VXJsLnRvU3RyaW5nKCkgPT09IG5leHRVcmwudG9TdHJpbmcoKSB8fCAvLyBTZWFyY2ggcGFyYW1zIGFmZmVjdCBhbGwgbG9hZGVycwogIGN1cnJlbnRVcmwuc2VhcmNoICE9PSBuZXh0VXJsLnNlYXJjaCB8fCAvLyBGb3JjZWQgcmV2YWxpZGF0aW9uIGR1ZSB0byBzdWJtaXNzaW9uLCB1c2VSZXZhbGlkYXRlLCBvciBYLVJlbWl4LVJldmFsaWRhdGUKICBpc1JldmFsaWRhdGlvblJlcXVpcmVkOwoKICBpZiAobWF0Y2gucm91dGUuc2hvdWxkUmV2YWxpZGF0ZSkgewogICAgbGV0IHJvdXRlQ2hvaWNlID0gbWF0Y2gucm91dGUuc2hvdWxkUmV2YWxpZGF0ZShfZXh0ZW5kcyh7CiAgICAgIGN1cnJlbnRVcmwsCiAgICAgIGN1cnJlbnRQYXJhbXMsCiAgICAgIG5leHRVcmwsCiAgICAgIG5leHRQYXJhbXMKICAgIH0sIHN1Ym1pc3Npb24sIHsKICAgICAgYWN0aW9uUmVzdWx0LAogICAgICBkZWZhdWx0U2hvdWxkUmV2YWxpZGF0ZQogICAgfSkpOwoKICAgIGlmICh0eXBlb2Ygcm91dGVDaG9pY2UgPT09ICJib29sZWFuIikgewogICAgICByZXR1cm4gcm91dGVDaG9pY2U7CiAgICB9CiAgfQoKICByZXR1cm4gZGVmYXVsdFNob3VsZFJldmFsaWRhdGU7Cn0KCmFzeW5jIGZ1bmN0aW9uIGNhbGxMb2FkZXJPckFjdGlvbih0eXBlLCByZXF1ZXN0LCBtYXRjaCwgbWF0Y2hlcywgYmFzZW5hbWUsIGlzU3RhdGljUmVxdWVzdCwgaXNSb3V0ZVJlcXVlc3QpIHsKICBpZiAoaXNTdGF0aWNSZXF1ZXN0ID09PSB2b2lkIDApIHsKICAgIGlzU3RhdGljUmVxdWVzdCA9IGZhbHNlOwogIH0KCiAgaWYgKGlzUm91dGVSZXF1ZXN0ID09PSB2b2lkIDApIHsKICAgIGlzUm91dGVSZXF1ZXN0ID0gZmFsc2U7CiAgfQoKICBsZXQgcmVzdWx0VHlwZTsKICBsZXQgcmVzdWx0OyAvLyBTZXR1cCBhIHByb21pc2Ugd2UgY2FuIHJhY2UgYWdhaW5zdCBzbyB0aGF0IGFib3J0IHNpZ25hbHMgc2hvcnQgY2lyY3VpdAoKICBsZXQgcmVqZWN0OwogIGxldCBhYm9ydFByb21pc2UgPSBuZXcgUHJvbWlzZSgoXywgcikgPT4gcmVqZWN0ID0gcik7CgogIGxldCBvblJlamVjdCA9ICgpID0+IHJlamVjdCgpOwoKICByZXF1ZXN0LnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCJhYm9ydCIsIG9uUmVqZWN0KTsKCiAgdHJ5IHsKICAgIGxldCBoYW5kbGVyID0gbWF0Y2gucm91dGVbdHlwZV07CiAgICByb3V0ZXJfaW52YXJpYW50KGhhbmRsZXIsICJDb3VsZCBub3QgZmluZCB0aGUgIiArIHR5cGUgKyAiIHRvIHJ1biBvbiB0aGUgXCIiICsgbWF0Y2gucm91dGUuaWQgKyAiXCIgcm91dGUiKTsKICAgIHJlc3VsdCA9IGF3YWl0IFByb21pc2UucmFjZShbaGFuZGxlcih7CiAgICAgIHJlcXVlc3QsCiAgICAgIHBhcmFtczogbWF0Y2gucGFyYW1zCiAgICB9KSwgYWJvcnRQcm9taXNlXSk7CiAgfSBjYXRjaCAoZSkgewogICAgcmVzdWx0VHlwZSA9IFJlc3VsdFR5cGUuZXJyb3I7CiAgICByZXN1bHQgPSBlOwogIH0gZmluYWxseSB7CiAgICByZXF1ZXN0LnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKCJhYm9ydCIsIG9uUmVqZWN0KTsKICB9CgogIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBSZXNwb25zZSkgewogICAgbGV0IHN0YXR1cyA9IHJlc3VsdC5zdGF0dXM7IC8vIFByb2Nlc3MgcmVkaXJlY3RzCgogICAgaWYgKHN0YXR1cyA+PSAzMDAgJiYgc3RhdHVzIDw9IDM5OSkgewogICAgICBsZXQgbG9jYXRpb24gPSByZXN1bHQuaGVhZGVycy5nZXQoIkxvY2F0aW9uIik7CiAgICAgIHJvdXRlcl9pbnZhcmlhbnQobG9jYXRpb24sICJSZWRpcmVjdHMgcmV0dXJuZWQvdGhyb3duIGZyb20gbG9hZGVycy9hY3Rpb25zIG11c3QgaGF2ZSBhIExvY2F0aW9uIGhlYWRlciIpOyAvLyBTdXBwb3J0IHJlbGF0aXZlIHJvdXRpbmcgaW4gcmVkaXJlY3RzCgogICAgICBsZXQgYWN0aXZlTWF0Y2hlcyA9IG1hdGNoZXMuc2xpY2UoMCwgbWF0Y2hlcy5pbmRleE9mKG1hdGNoKSArIDEpOwogICAgICBsZXQgcm91dGVQYXRobmFtZXMgPSBnZXRQYXRoQ29udHJpYnV0aW5nTWF0Y2hlcyhhY3RpdmVNYXRjaGVzKS5tYXAobWF0Y2ggPT4gbWF0Y2gucGF0aG5hbWVCYXNlKTsKICAgICAgbGV0IHJlcXVlc3RQYXRoID0gY3JlYXRlVVJMKHJlcXVlc3QudXJsKS5wYXRobmFtZTsKICAgICAgbGV0IHJlc29sdmVkTG9jYXRpb24gPSByZXNvbHZlVG8obG9jYXRpb24sIHJvdXRlUGF0aG5hbWVzLCByZXF1ZXN0UGF0aCk7CiAgICAgIHJvdXRlcl9pbnZhcmlhbnQocm91dGVyX2NyZWF0ZVBhdGgocmVzb2x2ZWRMb2NhdGlvbiksICJVbmFibGUgdG8gcmVzb2x2ZSByZWRpcmVjdCBsb2NhdGlvbjogIiArIHJlc3VsdC5oZWFkZXJzLmdldCgiTG9jYXRpb24iKSk7IC8vIFByZXBlbmQgdGhlIGJhc2VuYW1lIHRvIHRoZSByZWRpcmVjdCBsb2NhdGlvbiBpZiB3ZSBoYXZlIG9uZQoKICAgICAgaWYgKGJhc2VuYW1lKSB7CiAgICAgICAgbGV0IHBhdGggPSByZXNvbHZlZExvY2F0aW9uLnBhdGhuYW1lOwogICAgICAgIHJlc29sdmVkTG9jYXRpb24ucGF0aG5hbWUgPSBwYXRoID09PSAiLyIgPyBiYXNlbmFtZSA6IHJvdXRlcl9qb2luUGF0aHMoW2Jhc2VuYW1lLCBwYXRoXSk7CiAgICAgIH0KCiAgICAgIGxvY2F0aW9uID0gcm91dGVyX2NyZWF0ZVBhdGgocmVzb2x2ZWRMb2NhdGlvbik7IC8vIERvbid0IHByb2Nlc3MgcmVkaXJlY3RzIGluIHRoZSByb3V0ZXIgZHVyaW5nIHN0YXRpYyByZXF1ZXN0cyByZXF1ZXN0cy4KICAgICAgLy8gSW5zdGVhZCwgdGhyb3cgdGhlIFJlc3BvbnNlIGFuZCBsZXQgdGhlIHNlcnZlciBoYW5kbGUgaXQgd2l0aCBhbiBIVFRQCiAgICAgIC8vIHJlZGlyZWN0LiAgV2UgYWxzbyB1cGRhdGUgdGhlIExvY2F0aW9uIGhlYWRlciBpbiBwbGFjZSBpbiB0aGlzIGZsb3cgc28KICAgICAgLy8gYmFzZW5hbWUgYW5kIHJlbGF0aXZlIHJvdXRpbmcgaXMgdGFrZW4gaW50byBhY2NvdW50CgogICAgICBpZiAoaXNTdGF0aWNSZXF1ZXN0KSB7CiAgICAgICAgcmVzdWx0LmhlYWRlcnMuc2V0KCJMb2NhdGlvbiIsIGxvY2F0aW9uKTsKICAgICAgICB0aHJvdyByZXN1bHQ7CiAgICAgIH0KCiAgICAgIHJldHVybiB7CiAgICAgICAgdHlwZTogUmVzdWx0VHlwZS5yZWRpcmVjdCwKICAgICAgICBzdGF0dXMsCiAgICAgICAgbG9jYXRpb24sCiAgICAgICAgcmV2YWxpZGF0ZTogcmVzdWx0LmhlYWRlcnMuZ2V0KCJYLVJlbWl4LVJldmFsaWRhdGUiKSAhPT0gbnVsbAogICAgICB9OwogICAgfSAvLyBGb3IgU1NSIHNpbmdsZS1yb3V0ZSByZXF1ZXN0cywgd2Ugd2FudCB0byBoYW5kIFJlc3BvbnNlcyBiYWNrIGRpcmVjdGx5CiAgICAvLyB3aXRob3V0IHVud3JhcHBpbmcuICBXZSBkbyB0aGlzIHdpdGggdGhlIFF1ZXJ5Um91dGVSZXNwb25zZSB3cmFwcGVyCiAgICAvLyBpbnRlcmZhY2Ugc28gd2UgY2FuIGtub3cgd2hldGhlciBpdCB3YXMgcmV0dXJuZWQgb3IgdGhyb3duCgoKICAgIGlmIChpc1JvdXRlUmVxdWVzdCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdGhyb3ctbGl0ZXJhbAogICAgICB0aHJvdyB7CiAgICAgICAgdHlwZTogcmVzdWx0VHlwZSB8fCBSZXN1bHRUeXBlLmRhdGEsCiAgICAgICAgcmVzcG9uc2U6IHJlc3VsdAogICAgICB9OwogICAgfQoKICAgIGxldCBkYXRhOwogICAgbGV0IGNvbnRlbnRUeXBlID0gcmVzdWx0LmhlYWRlcnMuZ2V0KCJDb250ZW50LVR5cGUiKTsKCiAgICBpZiAoY29udGVudFR5cGUgJiYgY29udGVudFR5cGUuc3RhcnRzV2l0aCgiYXBwbGljYXRpb24vanNvbiIpKSB7CiAgICAgIGRhdGEgPSBhd2FpdCByZXN1bHQuanNvbigpOwogICAgfSBlbHNlIHsKICAgICAgZGF0YSA9IGF3YWl0IHJlc3VsdC50ZXh0KCk7CiAgICB9CgogICAgaWYgKHJlc3VsdFR5cGUgPT09IFJlc3VsdFR5cGUuZXJyb3IpIHsKICAgICAgcmV0dXJuIHsKICAgICAgICB0eXBlOiByZXN1bHRUeXBlLAogICAgICAgIGVycm9yOiBuZXcgRXJyb3JSZXNwb25zZShzdGF0dXMsIHJlc3VsdC5zdGF0dXNUZXh0LCBkYXRhKSwKICAgICAgICBoZWFkZXJzOiByZXN1bHQuaGVhZGVycwogICAgICB9OwogICAgfQoKICAgIHJldHVybiB7CiAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZGF0YSwKICAgICAgZGF0YSwKICAgICAgc3RhdHVzQ29kZTogcmVzdWx0LnN0YXR1cywKICAgICAgaGVhZGVyczogcmVzdWx0LmhlYWRlcnMKICAgIH07CiAgfQoKICBpZiAocmVzdWx0VHlwZSA9PT0gUmVzdWx0VHlwZS5lcnJvcikgewogICAgcmV0dXJuIHsKICAgICAgdHlwZTogcmVzdWx0VHlwZSwKICAgICAgZXJyb3I6IHJlc3VsdAogICAgfTsKICB9CgogIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBEZWZlcnJlZERhdGEpIHsKICAgIHJldHVybiB7CiAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZGVmZXJyZWQsCiAgICAgIGRlZmVycmVkRGF0YTogcmVzdWx0CiAgICB9OwogIH0KCiAgcmV0dXJuIHsKICAgIHR5cGU6IFJlc3VsdFR5cGUuZGF0YSwKICAgIGRhdGE6IHJlc3VsdAogIH07Cn0KCmZ1bmN0aW9uIGNyZWF0ZVJlcXVlc3QobG9jYXRpb24sIHNpZ25hbCwgc3VibWlzc2lvbikgewogIGxldCB1cmwgPSBjcmVhdGVVUkwoc3RyaXBIYXNoRnJvbVBhdGgobG9jYXRpb24pKS50b1N0cmluZygpOwogIGxldCBpbml0ID0gewogICAgc2lnbmFsCiAgfTsKCiAgaWYgKHN1Ym1pc3Npb24pIHsKICAgIGxldCB7CiAgICAgIGZvcm1NZXRob2QsCiAgICAgIGZvcm1FbmNUeXBlLAogICAgICBmb3JtRGF0YQogICAgfSA9IHN1Ym1pc3Npb247CiAgICBpbml0Lm1ldGhvZCA9IGZvcm1NZXRob2QudG9VcHBlckNhc2UoKTsKICAgIGluaXQuYm9keSA9IGZvcm1FbmNUeXBlID09PSAiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIiA/IGNvbnZlcnRGb3JtRGF0YVRvU2VhcmNoUGFyYW1zKGZvcm1EYXRhKSA6IGZvcm1EYXRhOwogIH0gLy8gQ29udGVudC1UeXBlIGlzIGluZmVycmVkIChodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jZG9tLXJlcXVlc3QpCgoKICByZXR1cm4gbmV3IFJlcXVlc3QodXJsLCBpbml0KTsKfQoKZnVuY3Rpb24gY29udmVydEZvcm1EYXRhVG9TZWFyY2hQYXJhbXMoZm9ybURhdGEpIHsKICBsZXQgc2VhcmNoUGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcygpOwoKICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2YgZm9ybURhdGEuZW50cmllcygpKSB7CiAgICByb3V0ZXJfaW52YXJpYW50KHR5cGVvZiB2YWx1ZSA9PT0gInN0cmluZyIsICdGaWxlIGlucHV0cyBhcmUgbm90IHN1cHBvcnRlZCB3aXRoIGVuY1R5cGUgImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCIsICcgKyAncGxlYXNlIHVzZSAibXVsdGlwYXJ0L2Zvcm0tZGF0YSIgaW5zdGVhZC4nKTsKICAgIHNlYXJjaFBhcmFtcy5hcHBlbmQoa2V5LCB2YWx1ZSk7CiAgfQoKICByZXR1cm4gc2VhcmNoUGFyYW1zOwp9CgpmdW5jdGlvbiBwcm9jZXNzUm91dGVMb2FkZXJEYXRhKG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdFcnJvciwgYWN0aXZlRGVmZXJyZWRzKSB7CiAgLy8gRmlsbCBpbiBsb2FkZXJEYXRhL2Vycm9ycyBmcm9tIG91ciBsb2FkZXJzCiAgbGV0IGxvYWRlckRhdGEgPSB7fTsKICBsZXQgZXJyb3JzID0gbnVsbDsKICBsZXQgc3RhdHVzQ29kZTsKICBsZXQgZm91bmRFcnJvciA9IGZhbHNlOwogIGxldCBsb2FkZXJIZWFkZXJzID0ge307IC8vIFByb2Nlc3MgbG9hZGVyIHJlc3VsdHMgaW50byBzdGF0ZS5sb2FkZXJEYXRhL3N0YXRlLmVycm9ycwoKICByZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaW5kZXgpID0+IHsKICAgIGxldCBpZCA9IG1hdGNoZXNUb0xvYWRbaW5kZXhdLnJvdXRlLmlkOwogICAgcm91dGVyX2ludmFyaWFudCghaXNSZWRpcmVjdFJlc3VsdChyZXN1bHQpLCAiQ2Fubm90IGhhbmRsZSByZWRpcmVjdCByZXN1bHRzIGluIHByb2Nlc3NMb2FkZXJEYXRhIik7CgogICAgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkgewogICAgICAvLyBMb29rIHVwd2FyZHMgZnJvbSB0aGUgbWF0Y2hlZCByb3V0ZSBmb3IgdGhlIGNsb3Nlc3QgYW5jZXN0b3IKICAgICAgLy8gZXJyb3IgYm91bmRhcnksIGRlZmF1bHRpbmcgdG8gdGhlIHJvb3QgbWF0Y2gKICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIGlkKTsKICAgICAgbGV0IGVycm9yID0gcmVzdWx0LmVycm9yOyAvLyBJZiB3ZSBoYXZlIGEgcGVuZGluZyBhY3Rpb24gZXJyb3IsIHdlIHJlcG9ydCBpdCBhdCB0aGUgaGlnaGVzdC1yb3V0ZQogICAgICAvLyB0aGF0IHRocm93cyBhIGxvYWRlciBlcnJvciwgYW5kIHRoZW4gY2xlYXIgaXQgb3V0IHRvIGluZGljYXRlIHRoYXQKICAgICAgLy8gaXQgd2FzIGNvbnN1bWVkCgogICAgICBpZiAocGVuZGluZ0Vycm9yKSB7CiAgICAgICAgZXJyb3IgPSBPYmplY3QudmFsdWVzKHBlbmRpbmdFcnJvcilbMF07CiAgICAgICAgcGVuZGluZ0Vycm9yID0gdW5kZWZpbmVkOwogICAgICB9CgogICAgICBlcnJvcnMgPSBPYmplY3QuYXNzaWduKGVycm9ycyB8fCB7fSwgewogICAgICAgIFtib3VuZGFyeU1hdGNoLnJvdXRlLmlkXTogZXJyb3IKICAgICAgfSk7IC8vIE9uY2Ugd2UgZmluZCBvdXIgZmlyc3QgKGhpZ2hlc3QpIGVycm9yLCB3ZSBzZXQgdGhlIHN0YXR1cyBjb2RlIGFuZAogICAgICAvLyBwcmV2ZW50IGRlZXBlciBzdGF0dXMgY29kZXMgZnJvbSBvdmVycmlkaW5nCgogICAgICBpZiAoIWZvdW5kRXJyb3IpIHsKICAgICAgICBmb3VuZEVycm9yID0gdHJ1ZTsKICAgICAgICBzdGF0dXNDb2RlID0gaXNSb3V0ZUVycm9yUmVzcG9uc2UocmVzdWx0LmVycm9yKSA/IHJlc3VsdC5lcnJvci5zdGF0dXMgOiA1MDA7CiAgICAgIH0KCiAgICAgIGlmIChyZXN1bHQuaGVhZGVycykgewogICAgICAgIGxvYWRlckhlYWRlcnNbaWRdID0gcmVzdWx0LmhlYWRlcnM7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAoaXNEZWZlcnJlZFJlc3VsdChyZXN1bHQpKSB7CiAgICAgIGFjdGl2ZURlZmVycmVkcyAmJiBhY3RpdmVEZWZlcnJlZHMuc2V0KGlkLCByZXN1bHQuZGVmZXJyZWREYXRhKTsKICAgICAgbG9hZGVyRGF0YVtpZF0gPSByZXN1bHQuZGVmZXJyZWREYXRhLmRhdGE7IC8vIFRPRE86IEFkZCBzdGF0dXNDb2RlL2hlYWRlcnMgb25jZSB3ZSB3aXJlIHVwIHN0cmVhbWluZyBpbiBSZW1peAogICAgfSBlbHNlIHsKICAgICAgbG9hZGVyRGF0YVtpZF0gPSByZXN1bHQuZGF0YTsgLy8gRXJyb3Igc3RhdHVzIGNvZGVzIGFsd2F5cyBvdmVycmlkZSBzdWNjZXNzIHN0YXR1cyBjb2RlcywgYnV0IGlmIGFsbAogICAgICAvLyBsb2FkZXJzIGFyZSBzdWNjZXNzZnVsIHdlIHRha2UgdGhlIGRlZXBlc3Qgc3RhdHVzIGNvZGUuCgogICAgICBpZiAocmVzdWx0LnN0YXR1c0NvZGUgIT0gbnVsbCAmJiByZXN1bHQuc3RhdHVzQ29kZSAhPT0gMjAwICYmICFmb3VuZEVycm9yKSB7CiAgICAgICAgc3RhdHVzQ29kZSA9IHJlc3VsdC5zdGF0dXNDb2RlOwogICAgICB9CgogICAgICBpZiAocmVzdWx0LmhlYWRlcnMpIHsKICAgICAgICBsb2FkZXJIZWFkZXJzW2lkXSA9IHJlc3VsdC5oZWFkZXJzOwogICAgICB9CiAgICB9CiAgfSk7IC8vIElmIHdlIGRpZG4ndCBjb25zdW1lIHRoZSBwZW5kaW5nIGFjdGlvbiBlcnJvciAoaS5lLiwgYWxsIGxvYWRlcnMKICAvLyByZXNvbHZlZCksIHRoZW4gY29uc3VtZSBpdCBoZXJlCgogIGlmIChwZW5kaW5nRXJyb3IpIHsKICAgIGVycm9ycyA9IHBlbmRpbmdFcnJvcjsKICB9CgogIHJldHVybiB7CiAgICBsb2FkZXJEYXRhLAogICAgZXJyb3JzLAogICAgc3RhdHVzQ29kZTogc3RhdHVzQ29kZSB8fCAyMDAsCiAgICBsb2FkZXJIZWFkZXJzCiAgfTsKfQoKZnVuY3Rpb24gcHJvY2Vzc0xvYWRlckRhdGEoc3RhdGUsIG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdFcnJvciwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMsIGZldGNoZXJSZXN1bHRzLCBhY3RpdmVEZWZlcnJlZHMpIHsKICBsZXQgewogICAgbG9hZGVyRGF0YSwKICAgIGVycm9ycwogIH0gPSBwcm9jZXNzUm91dGVMb2FkZXJEYXRhKG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdFcnJvciwgYWN0aXZlRGVmZXJyZWRzKTsgLy8gUHJvY2VzcyByZXN1bHRzIGZyb20gb3VyIHJldmFsaWRhdGluZyBmZXRjaGVycwoKICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMubGVuZ3RoOyBpbmRleCsrKSB7CiAgICBsZXQgW2tleSwsIG1hdGNoXSA9IHJldmFsaWRhdGluZ0ZldGNoZXJzW2luZGV4XTsKICAgIHJvdXRlcl9pbnZhcmlhbnQoZmV0Y2hlclJlc3VsdHMgIT09IHVuZGVmaW5lZCAmJiBmZXRjaGVyUmVzdWx0c1tpbmRleF0gIT09IHVuZGVmaW5lZCwgIkRpZCBub3QgZmluZCBjb3JyZXNwb25kaW5nIGZldGNoZXIgcmVzdWx0Iik7CiAgICBsZXQgcmVzdWx0ID0gZmV0Y2hlclJlc3VsdHNbaW5kZXhdOyAvLyBQcm9jZXNzIGZldGNoZXIgbm9uLXJlZGlyZWN0IGVycm9ycwoKICAgIGlmIChpc0Vycm9yUmVzdWx0KHJlc3VsdCkpIHsKICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KHN0YXRlLm1hdGNoZXMsIG1hdGNoLnJvdXRlLmlkKTsKCiAgICAgIGlmICghKGVycm9ycyAmJiBlcnJvcnNbYm91bmRhcnlNYXRjaC5yb3V0ZS5pZF0pKSB7CiAgICAgICAgZXJyb3JzID0gX2V4dGVuZHMoe30sIGVycm9ycywgewogICAgICAgICAgW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZXJyb3IKICAgICAgICB9KTsKICAgICAgfQoKICAgICAgc3RhdGUuZmV0Y2hlcnMuZGVsZXRlKGtleSk7CiAgICB9IGVsc2UgaWYgKGlzUmVkaXJlY3RSZXN1bHQocmVzdWx0KSkgewogICAgICAvLyBTaG91bGQgbmV2ZXIgZ2V0IGhlcmUsIHJlZGlyZWN0cyBzaG91bGQgZ2V0IHByb2Nlc3NlZCBhYm92ZSwgYnV0IHdlCiAgICAgIC8vIGtlZXAgdGhpcyB0byB0eXBlIG5hcnJvdyB0byBhIHN1Y2Nlc3MgcmVzdWx0IGluIHRoZSBlbHNlCiAgICAgIHRocm93IG5ldyBFcnJvcigiVW5oYW5kbGVkIGZldGNoZXIgcmV2YWxpZGF0aW9uIHJlZGlyZWN0Iik7CiAgICB9IGVsc2UgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSkgewogICAgICAvLyBTaG91bGQgbmV2ZXIgZ2V0IGhlcmUsIGRlZmVycmVkIGRhdGEgc2hvdWxkIGJlIGF3YWl0ZWQgZm9yIGZldGNoZXJzCiAgICAgIC8vIGluIHJlc29sdmVEZWZlcnJlZFJlc3VsdHMKICAgICAgdGhyb3cgbmV3IEVycm9yKCJVbmhhbmRsZWQgZmV0Y2hlciBkZWZlcnJlZCBkYXRhIik7CiAgICB9IGVsc2UgewogICAgICBsZXQgZG9uZUZldGNoZXIgPSB7CiAgICAgICAgc3RhdGU6ICJpZGxlIiwKICAgICAgICBkYXRhOiByZXN1bHQuZGF0YSwKICAgICAgICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsCiAgICAgICAgZm9ybUFjdGlvbjogdW5kZWZpbmVkLAogICAgICAgIGZvcm1FbmNUeXBlOiB1bmRlZmluZWQsCiAgICAgICAgZm9ybURhdGE6IHVuZGVmaW5lZAogICAgICB9OwogICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBkb25lRmV0Y2hlcik7CiAgICB9CiAgfQoKICByZXR1cm4gewogICAgbG9hZGVyRGF0YSwKICAgIGVycm9ycwogIH07Cn0KCmZ1bmN0aW9uIG1lcmdlTG9hZGVyRGF0YShsb2FkZXJEYXRhLCBuZXdMb2FkZXJEYXRhLCBtYXRjaGVzKSB7CiAgbGV0IG1lcmdlZExvYWRlckRhdGEgPSBfZXh0ZW5kcyh7fSwgbmV3TG9hZGVyRGF0YSk7CgogIG1hdGNoZXMuZm9yRWFjaChtYXRjaCA9PiB7CiAgICBsZXQgaWQgPSBtYXRjaC5yb3V0ZS5pZDsKCiAgICBpZiAobmV3TG9hZGVyRGF0YVtpZF0gPT09IHVuZGVmaW5lZCAmJiBsb2FkZXJEYXRhW2lkXSAhPT0gdW5kZWZpbmVkKSB7CiAgICAgIG1lcmdlZExvYWRlckRhdGFbaWRdID0gbG9hZGVyRGF0YVtpZF07CiAgICB9CiAgfSk7CiAgcmV0dXJuIG1lcmdlZExvYWRlckRhdGE7Cn0gLy8gRmluZCB0aGUgbmVhcmVzdCBlcnJvciBib3VuZGFyeSwgbG9va2luZyB1cHdhcmRzIGZyb20gdGhlIGxlYWYgcm91dGUgKG9yIHRoZQovLyByb3V0ZSBzcGVjaWZpZWQgYnkgcm91dGVJZCkgZm9yIHRoZSBjbG9zZXN0IGFuY2VzdG9yIGVycm9yIGJvdW5kYXJ5LAovLyBkZWZhdWx0aW5nIHRvIHRoZSByb290IG1hdGNoCgoKZnVuY3Rpb24gZmluZE5lYXJlc3RCb3VuZGFyeShtYXRjaGVzLCByb3V0ZUlkKSB7CiAgbGV0IGVsaWdpYmxlTWF0Y2hlcyA9IHJvdXRlSWQgPyBtYXRjaGVzLnNsaWNlKDAsIG1hdGNoZXMuZmluZEluZGV4KG0gPT4gbS5yb3V0ZS5pZCA9PT0gcm91dGVJZCkgKyAxKSA6IFsuLi5tYXRjaGVzXTsKICByZXR1cm4gZWxpZ2libGVNYXRjaGVzLnJldmVyc2UoKS5maW5kKG0gPT4gbS5yb3V0ZS5oYXNFcnJvckJvdW5kYXJ5ID09PSB0cnVlKSB8fCBtYXRjaGVzWzBdOwp9CgpmdW5jdGlvbiBnZXRTaG9ydENpcmN1aXRNYXRjaGVzKHJvdXRlcywgc3RhdHVzLCBzdGF0dXNUZXh0KSB7CiAgLy8gUHJlZmVyIGEgcm9vdCBsYXlvdXQgcm91dGUgaWYgcHJlc2VudCwgb3RoZXJ3aXNlIHNoaW0gaW4gYSByb3V0ZSBvYmplY3QKICBsZXQgcm91dGUgPSByb3V0ZXMuZmluZChyID0+IHIuaW5kZXggfHwgIXIucGF0aCB8fCByLnBhdGggPT09ICIvIikgfHwgewogICAgaWQ6ICJfX3NoaW0tIiArIHN0YXR1cyArICItcm91dGVfXyIKICB9OwogIHJldHVybiB7CiAgICBtYXRjaGVzOiBbewogICAgICBwYXJhbXM6IHt9LAogICAgICBwYXRobmFtZTogIiIsCiAgICAgIHBhdGhuYW1lQmFzZTogIiIsCiAgICAgIHJvdXRlCiAgICB9XSwKICAgIHJvdXRlLAogICAgZXJyb3I6IG5ldyBFcnJvclJlc3BvbnNlKHN0YXR1cywgc3RhdHVzVGV4dCwgbnVsbCkKICB9Owp9CgpmdW5jdGlvbiBnZXROb3RGb3VuZE1hdGNoZXMocm91dGVzKSB7CiAgcmV0dXJuIGdldFNob3J0Q2lyY3VpdE1hdGNoZXMocm91dGVzLCA0MDQsICJOb3QgRm91bmQiKTsKfQoKZnVuY3Rpb24gZ2V0TWV0aG9kTm90QWxsb3dlZE1hdGNoZXMocm91dGVzKSB7CiAgcmV0dXJuIGdldFNob3J0Q2lyY3VpdE1hdGNoZXMocm91dGVzLCA0MDUsICJNZXRob2QgTm90IEFsbG93ZWQiKTsKfQoKZnVuY3Rpb24gZ2V0TWV0aG9kTm90QWxsb3dlZFJlc3VsdChwYXRoKSB7CiAgbGV0IGhyZWYgPSB0eXBlb2YgcGF0aCA9PT0gInN0cmluZyIgPyBwYXRoIDogcm91dGVyX2NyZWF0ZVBhdGgocGF0aCk7CiAgY29uc29sZS53YXJuKCJZb3UncmUgdHJ5aW5nIHRvIHN1Ym1pdCB0byBhIHJvdXRlIHRoYXQgZG9lcyBub3QgaGF2ZSBhbiBhY3Rpb24uICBUbyAiICsgImZpeCB0aGlzLCBwbGVhc2UgYWRkIGFuIGBhY3Rpb25gIGZ1bmN0aW9uIHRvIHRoZSByb3V0ZSBmb3IgIiArICgiWyIgKyBocmVmICsgIl0iKSk7CiAgcmV0dXJuIHsKICAgIHR5cGU6IFJlc3VsdFR5cGUuZXJyb3IsCiAgICBlcnJvcjogbmV3IEVycm9yUmVzcG9uc2UoNDA1LCAiTWV0aG9kIE5vdCBBbGxvd2VkIiwgIiIpCiAgfTsKfSAvLyBGaW5kIGFueSByZXR1cm5lZCByZWRpcmVjdCBlcnJvcnMsIHN0YXJ0aW5nIGZyb20gdGhlIGxvd2VzdCBtYXRjaAoKCmZ1bmN0aW9uIGZpbmRSZWRpcmVjdChyZXN1bHRzKSB7CiAgZm9yIChsZXQgaSA9IHJlc3VsdHMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHsKICAgIGxldCByZXN1bHQgPSByZXN1bHRzW2ldOwoKICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkpIHsKICAgICAgcmV0dXJuIHJlc3VsdDsKICAgIH0KICB9Cn0KCmZ1bmN0aW9uIHN0cmlwSGFzaEZyb21QYXRoKHBhdGgpIHsKICBsZXQgcGFyc2VkUGF0aCA9IHR5cGVvZiBwYXRoID09PSAic3RyaW5nIiA/IHBhcnNlUGF0aChwYXRoKSA6IHBhdGg7CiAgcmV0dXJuIHJvdXRlcl9jcmVhdGVQYXRoKF9leHRlbmRzKHt9LCBwYXJzZWRQYXRoLCB7CiAgICBoYXNoOiAiIgogIH0pKTsKfQoKZnVuY3Rpb24gaXNIYXNoQ2hhbmdlT25seShhLCBiKSB7CiAgcmV0dXJuIGEucGF0aG5hbWUgPT09IGIucGF0aG5hbWUgJiYgYS5zZWFyY2ggPT09IGIuc2VhcmNoICYmIGEuaGFzaCAhPT0gYi5oYXNoOwp9CgpmdW5jdGlvbiBpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkgewogIHJldHVybiByZXN1bHQudHlwZSA9PT0gUmVzdWx0VHlwZS5kZWZlcnJlZDsKfQoKZnVuY3Rpb24gaXNFcnJvclJlc3VsdChyZXN1bHQpIHsKICByZXR1cm4gcmVzdWx0LnR5cGUgPT09IFJlc3VsdFR5cGUuZXJyb3I7Cn0KCmZ1bmN0aW9uIGlzUmVkaXJlY3RSZXN1bHQocmVzdWx0KSB7CiAgcmV0dXJuIChyZXN1bHQgJiYgcmVzdWx0LnR5cGUpID09PSBSZXN1bHRUeXBlLnJlZGlyZWN0Owp9CgpmdW5jdGlvbiBpc1JlZGlyZWN0UmVzcG9uc2UocmVzdWx0KSB7CiAgaWYgKCEocmVzdWx0IGluc3RhbmNlb2YgUmVzcG9uc2UpKSB7CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICBsZXQgc3RhdHVzID0gcmVzdWx0LnN0YXR1czsKICBsZXQgbG9jYXRpb24gPSByZXN1bHQuaGVhZGVycy5nZXQoIkxvY2F0aW9uIik7CiAgcmV0dXJuIHN0YXR1cyA+PSAzMDAgJiYgc3RhdHVzIDw9IDM5OSAmJiBsb2NhdGlvbiAhPSBudWxsOwp9CgpmdW5jdGlvbiBpc1F1ZXJ5Um91dGVSZXNwb25zZShvYmopIHsKICByZXR1cm4gb2JqICYmIG9iai5yZXNwb25zZSBpbnN0YW5jZW9mIFJlc3BvbnNlICYmIChvYmoudHlwZSA9PT0gUmVzdWx0VHlwZS5kYXRhIHx8IFJlc3VsdFR5cGUuZXJyb3IpOwp9Cgphc3luYyBmdW5jdGlvbiByZXNvbHZlRGVmZXJyZWRSZXN1bHRzKGN1cnJlbnRNYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCByZXN1bHRzLCBzaWduYWwsIGlzRmV0Y2hlciwgY3VycmVudExvYWRlckRhdGEpIHsKICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgcmVzdWx0cy5sZW5ndGg7IGluZGV4KyspIHsKICAgIGxldCByZXN1bHQgPSByZXN1bHRzW2luZGV4XTsKICAgIGxldCBtYXRjaCA9IG1hdGNoZXNUb0xvYWRbaW5kZXhdOwogICAgbGV0IGN1cnJlbnRNYXRjaCA9IGN1cnJlbnRNYXRjaGVzLmZpbmQobSA9PiBtLnJvdXRlLmlkID09PSBtYXRjaC5yb3V0ZS5pZCk7CiAgICBsZXQgaXNSZXZhbGlkYXRpbmdMb2FkZXIgPSBjdXJyZW50TWF0Y2ggIT0gbnVsbCAmJiAhaXNOZXdSb3V0ZUluc3RhbmNlKGN1cnJlbnRNYXRjaCwgbWF0Y2gpICYmIChjdXJyZW50TG9hZGVyRGF0YSAmJiBjdXJyZW50TG9hZGVyRGF0YVttYXRjaC5yb3V0ZS5pZF0pICE9PSB1bmRlZmluZWQ7CgogICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSAmJiAoaXNGZXRjaGVyIHx8IGlzUmV2YWxpZGF0aW5nTG9hZGVyKSkgewogICAgICAvLyBOb3RlOiB3ZSBkbyBub3QgaGF2ZSB0byB0b3VjaCBhY3RpdmVEZWZlcnJlZHMgaGVyZSBzaW5jZSB3ZSByYWNlIHRoZW0KICAgICAgLy8gYWdhaW5zdCB0aGUgc2lnbmFsIGluIHJlc29sdmVEZWZlcnJlZERhdGEgYW5kIHRoZXknbGwgZ2V0IGFib3J0ZWQKICAgICAgLy8gdGhlcmUgaWYgbmVlZGVkCiAgICAgIGF3YWl0IHJlc29sdmVEZWZlcnJlZERhdGEocmVzdWx0LCBzaWduYWwsIGlzRmV0Y2hlcikudGhlbihyZXN1bHQgPT4gewogICAgICAgIGlmIChyZXN1bHQpIHsKICAgICAgICAgIHJlc3VsdHNbaW5kZXhdID0gcmVzdWx0IHx8IHJlc3VsdHNbaW5kZXhdOwogICAgICAgIH0KICAgICAgfSk7CiAgICB9CiAgfQp9Cgphc3luYyBmdW5jdGlvbiByZXNvbHZlRGVmZXJyZWREYXRhKHJlc3VsdCwgc2lnbmFsLCB1bndyYXApIHsKICBpZiAodW53cmFwID09PSB2b2lkIDApIHsKICAgIHVud3JhcCA9IGZhbHNlOwogIH0KCiAgbGV0IGFib3J0ZWQgPSBhd2FpdCByZXN1bHQuZGVmZXJyZWREYXRhLnJlc29sdmVEYXRhKHNpZ25hbCk7CgogIGlmIChhYm9ydGVkKSB7CiAgICByZXR1cm47CiAgfQoKICBpZiAodW53cmFwKSB7CiAgICB0cnkgewogICAgICByZXR1cm4gewogICAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZGF0YSwKICAgICAgICBkYXRhOiByZXN1bHQuZGVmZXJyZWREYXRhLnVud3JhcHBlZERhdGEKICAgICAgfTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgLy8gSGFuZGxlIGFueSBUcmFja2VkUHJvbWlzZS5fZXJyb3IgdmFsdWVzIGVuY291bnRlcmVkIHdoaWxlIHVud3JhcHBpbmcKICAgICAgcmV0dXJuIHsKICAgICAgICB0eXBlOiBSZXN1bHRUeXBlLmVycm9yLAogICAgICAgIGVycm9yOiBlCiAgICAgIH07CiAgICB9CiAgfQoKICByZXR1cm4gewogICAgdHlwZTogUmVzdWx0VHlwZS5kYXRhLAogICAgZGF0YTogcmVzdWx0LmRlZmVycmVkRGF0YS5kYXRhCiAgfTsKfQoKZnVuY3Rpb24gaGFzTmFrZWRJbmRleFF1ZXJ5KHNlYXJjaCkgewogIHJldHVybiBuZXcgVVJMU2VhcmNoUGFyYW1zKHNlYXJjaCkuZ2V0QWxsKCJpbmRleCIpLnNvbWUodiA9PiB2ID09PSAiIik7Cn0gLy8gTm90ZTogVGhpcyBzaG91bGQgbWF0Y2ggdGhlIGZvcm1hdCBleHBvcnRlZCBieSB1c2VNYXRjaGVzLCBzbyBpZiB5b3UgY2hhbmdlCi8vIHRoaXMgcGxlYXNlIGFsc28gY2hhbmdlIHRoYXQgOikgIEV2ZW50dWFsbHkgd2UnbGwgRFJZIHRoaXMgdXAKCgpmdW5jdGlvbiBjcmVhdGVVc2VNYXRjaGVzTWF0Y2gobWF0Y2gsIGxvYWRlckRhdGEpIHsKICBsZXQgewogICAgcm91dGUsCiAgICBwYXRobmFtZSwKICAgIHBhcmFtcwogIH0gPSBtYXRjaDsKICByZXR1cm4gewogICAgaWQ6IHJvdXRlLmlkLAogICAgcGF0aG5hbWUsCiAgICBwYXJhbXMsCiAgICBkYXRhOiBsb2FkZXJEYXRhW3JvdXRlLmlkXSwKICAgIGhhbmRsZTogcm91dGUuaGFuZGxlCiAgfTsKfQoKZnVuY3Rpb24gZ2V0VGFyZ2V0TWF0Y2gobWF0Y2hlcywgbG9jYXRpb24pIHsKICBsZXQgc2VhcmNoID0gdHlwZW9mIGxvY2F0aW9uID09PSAic3RyaW5nIiA/IHBhcnNlUGF0aChsb2NhdGlvbikuc2VhcmNoIDogbG9jYXRpb24uc2VhcmNoOwoKICBpZiAobWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdLnJvdXRlLmluZGV4ICYmIGhhc05ha2VkSW5kZXhRdWVyeShzZWFyY2ggfHwgIiIpKSB7CiAgICAvLyBSZXR1cm4gdGhlIGxlYWYgaW5kZXggcm91dGUgd2hlbiBpbmRleCBpcyBwcmVzZW50CiAgICByZXR1cm4gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdOwogIH0gLy8gT3RoZXJ3aXNlIGdyYWIgdGhlIGRlZXBlc3QgInBhdGggY29udHJpYnV0aW5nIiBtYXRjaCAoaWdub3JpbmcgaW5kZXggYW5kCiAgLy8gcGF0aGxlc3MgbGF5b3V0IHJvdXRlcykKCgogIGxldCBwYXRoTWF0Y2hlcyA9IGdldFBhdGhDb250cmlidXRpbmdNYXRjaGVzKG1hdGNoZXMpOwogIHJldHVybiBwYXRoTWF0Y2hlc1twYXRoTWF0Y2hlcy5sZW5ndGggLSAxXTsKfSAvLyNlbmRyZWdpb24KCgovLyMgc291cmNlTWFwcGluZ1VSTD1yb3V0ZXIuanMubWFwCgovLyBFWFRFUk5BTCBNT0RVTEU6IC4vbm9kZV9tb2R1bGVzL3JlYWN0L2luZGV4LmpzCnZhciByZWFjdCA9IF9fd2VicGFja19yZXF1aXJlX18oMjk0KTsKdmFyIHJlYWN0X25hbWVzcGFjZU9iamVjdCA9IC8qI19fUFVSRV9fKi9fX3dlYnBhY2tfcmVxdWlyZV9fLnQocmVhY3QsIDIpOwo7Ly8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9ub2RlX21vZHVsZXMvcmVhY3Qtcm91dGVyL2Rpc3QvaW5kZXguanMKLyoqCiAqIFJlYWN0IFJvdXRlciB2Ni40LjMKICoKICogQ29weXJpZ2h0IChjKSBSZW1peCBTb2Z0d2FyZSBJbmMuCiAqCiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZQogKiBMSUNFTlNFLm1kIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuCiAqCiAqIEBsaWNlbnNlIE1JVAogKi8KCgoKCmZ1bmN0aW9uIGRpc3RfZXh0ZW5kcygpIHsKICBkaXN0X2V4dGVuZHMgPSBPYmplY3QuYXNzaWduID8gT2JqZWN0LmFzc2lnbi5iaW5kKCkgOiBmdW5jdGlvbiAodGFyZ2V0KSB7CiAgICBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykgewogICAgICB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldOwoKICAgICAgZm9yICh2YXIga2V5IGluIHNvdXJjZSkgewogICAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoc291cmNlLCBrZXkpKSB7CiAgICAgICAgICB0YXJnZXRba2V5XSA9IHNvdXJjZVtrZXldOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIHJldHVybiB0YXJnZXQ7CiAgfTsKICByZXR1cm4gZGlzdF9leHRlbmRzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7Cn0KCi8qKgogKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy4KICoKICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlCiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS4KICovCi8qKgogKiBpbmxpbmVkIE9iamVjdC5pcyBwb2x5ZmlsbCB0byBhdm9pZCByZXF1aXJpbmcgY29uc3VtZXJzIHNoaXAgdGhlaXIgb3duCiAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL09iamVjdC9pcwogKi8KCmZ1bmN0aW9uIGlzUG9seWZpbGwoeCwgeSkgewogIHJldHVybiB4ID09PSB5ICYmICh4ICE9PSAwIHx8IDEgLyB4ID09PSAxIC8geSkgfHwgeCAhPT0geCAmJiB5ICE9PSB5IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tc2VsZi1jb21wYXJlCiAgOwp9Cgpjb25zdCBpcyA9IHR5cGVvZiBPYmplY3QuaXMgPT09ICJmdW5jdGlvbiIgPyBPYmplY3QuaXMgOiBpc1BvbHlmaWxsOyAvLyBJbnRlbnRpb25hbGx5IG5vdCB1c2luZyBuYW1lZCBpbXBvcnRzIGJlY2F1c2UgUm9sbHVwIHVzZXMgZHluYW1pYwovLyBkaXNwYXRjaCBmb3IgQ29tbW9uSlMgaW50ZXJvcCBuYW1lZCBpbXBvcnRzLgoKY29uc3QgewogIHVzZVN0YXRlLAogIHVzZUVmZmVjdCwKICB1c2VMYXlvdXRFZmZlY3QsCiAgdXNlRGVidWdWYWx1ZQp9ID0gcmVhY3RfbmFtZXNwYWNlT2JqZWN0OwpsZXQgZGlkV2Fybk9sZDE4QWxwaGEgPSBmYWxzZTsKbGV0IGRpZFdhcm5VbmNhY2hlZEdldFNuYXBzaG90ID0gZmFsc2U7IC8vIERpc2NsYWltZXI6IFRoaXMgc2hpbSBicmVha3MgbWFueSBvZiB0aGUgcnVsZXMgb2YgUmVhY3QsIGFuZCBvbmx5IHdvcmtzCi8vIGJlY2F1c2Ugb2YgYSB2ZXJ5IHBhcnRpY3VsYXIgc2V0IG9mIGltcGxlbWVudGF0aW9uIGRldGFpbHMgYW5kIGFzc3VtcHRpb25zCi8vIC0tIGNoYW5nZSBhbnkgb25lIG9mIHRoZW0gYW5kIGl0IHdpbGwgYnJlYWsuIFRoZSBtb3N0IGltcG9ydGFudCBhc3N1bXB0aW9uCi8vIGlzIHRoYXQgdXBkYXRlcyBhcmUgYWx3YXlzIHN5bmNocm9ub3VzLCBiZWNhdXNlIGNvbmN1cnJlbnQgcmVuZGVyaW5nIGlzCi8vIG9ubHkgYXZhaWxhYmxlIGluIHZlcnNpb25zIG9mIFJlYWN0IHRoYXQgYWxzbyBoYXZlIGEgYnVpbHQtaW4KLy8gdXNlU3luY0V4dGVybmFsU3RvcmUgQVBJLiBBbmQgd2Ugb25seSB1c2UgdGhpcyBzaGltIHdoZW4gdGhlIGJ1aWx0LWluIEFQSQovLyBkb2VzIG5vdCBleGlzdC4KLy8KLy8gRG8gbm90IGFzc3VtZSB0aGF0IHRoZSBjbGV2ZXIgaGFja3MgdXNlZCBieSB0aGlzIGhvb2sgYWxzbyB3b3JrIGluIGdlbmVyYWwuCi8vIFRoZSBwb2ludCBvZiB0aGlzIHNoaW0gaXMgdG8gcmVwbGFjZSB0aGUgbmVlZCBmb3IgaGFja3MgYnkgb3RoZXIgbGlicmFyaWVzLgoKZnVuY3Rpb24gdXNlU3luY0V4dGVybmFsU3RvcmUkMihzdWJzY3JpYmUsIGdldFNuYXBzaG90LCAvLyBOb3RlOiBUaGUgc2hpbSBkb2VzIG5vdCB1c2UgZ2V0U2VydmVyU25hcHNob3QsIGJlY2F1c2UgcHJlLTE4IHZlcnNpb25zIG9mCi8vIFJlYWN0IGRvIG5vdCBleHBvc2UgYSB3YXkgdG8gY2hlY2sgaWYgd2UncmUgaHlkcmF0aW5nLiBTbyB1c2VycyBvZiB0aGUgc2hpbQovLyB3aWxsIG5lZWQgdG8gdHJhY2sgdGhhdCB0aGVtc2VsdmVzIGFuZCByZXR1cm4gdGhlIGNvcnJlY3QgdmFsdWUKLy8gZnJvbSBgZ2V0U25hcHNob3RgLgpnZXRTZXJ2ZXJTbmFwc2hvdCkgewogIGlmIChmYWxzZSkge30gLy8gUmVhZCB0aGUgY3VycmVudCBzbmFwc2hvdCBmcm9tIHRoZSBzdG9yZSBvbiBldmVyeSByZW5kZXIuIEFnYWluLCB0aGlzCiAgLy8gYnJlYWtzIHRoZSBydWxlcyBvZiBSZWFjdCwgYW5kIG9ubHkgd29ya3MgaGVyZSBiZWNhdXNlIG9mIHNwZWNpZmljCiAgLy8gaW1wbGVtZW50YXRpb24gZGV0YWlscywgbW9zdCBpbXBvcnRhbnRseSB0aGF0IHVwZGF0ZXMgYXJlCiAgLy8gYWx3YXlzIHN5bmNocm9ub3VzLgoKCiAgY29uc3QgdmFsdWUgPSBnZXRTbmFwc2hvdCgpOwoKICBpZiAoZmFsc2UpIHt9IC8vIEJlY2F1c2UgdXBkYXRlcyBhcmUgc3luY2hyb25vdXMsIHdlIGRvbid0IHF1ZXVlIHRoZW0uIEluc3RlYWQgd2UgZm9yY2UgYQogIC8vIHJlLXJlbmRlciB3aGVuZXZlciB0aGUgc3Vic2NyaWJlZCBzdGF0ZSBjaGFuZ2VzIGJ5IHVwZGF0aW5nIGFuIHNvbWUKICAvLyBhcmJpdHJhcnkgdXNlU3RhdGUgaG9vay4gVGhlbiwgZHVyaW5nIHJlbmRlciwgd2UgY2FsbCBnZXRTbmFwc2hvdCB0byByZWFkCiAgLy8gdGhlIGN1cnJlbnQgdmFsdWUuCiAgLy8KICAvLyBCZWNhdXNlIHdlIGRvbid0IGFjdHVhbGx5IHVzZSB0aGUgc3RhdGUgcmV0dXJuZWQgYnkgdGhlIHVzZVN0YXRlIGhvb2ssIHdlCiAgLy8gY2FuIHNhdmUgYSBiaXQgb2YgbWVtb3J5IGJ5IHN0b3Jpbmcgb3RoZXIgc3R1ZmYgaW4gdGhhdCBzbG90LgogIC8vCiAgLy8gVG8gaW1wbGVtZW50IHRoZSBlYXJseSBiYWlsb3V0LCB3ZSBuZWVkIHRvIHRyYWNrIHNvbWUgdGhpbmdzIG9uIGEgbXV0YWJsZQogIC8vIG9iamVjdC4gVXN1YWxseSwgd2Ugd291bGQgcHV0IHRoYXQgaW4gYSB1c2VSZWYgaG9vaywgYnV0IHdlIGNhbiBzdGFzaCBpdCBpbgogIC8vIG91ciB1c2VTdGF0ZSBob29rIGluc3RlYWQuCiAgLy8KICAvLyBUbyBmb3JjZSBhIHJlLXJlbmRlciwgd2UgY2FsbCBmb3JjZVVwZGF0ZSh7aW5zdH0pLiBUaGF0IHdvcmtzIGJlY2F1c2UgdGhlCiAgLy8gbmV3IG9iamVjdCBhbHdheXMgZmFpbHMgYW4gZXF1YWxpdHkgY2hlY2suCgoKICBjb25zdCBbewogICAgaW5zdAogIH0sIGZvcmNlVXBkYXRlXSA9IHVzZVN0YXRlKHsKICAgIGluc3Q6IHsKICAgICAgdmFsdWUsCiAgICAgIGdldFNuYXBzaG90CiAgICB9CiAgfSk7IC8vIFRyYWNrIHRoZSBsYXRlc3QgZ2V0U25hcHNob3QgZnVuY3Rpb24gd2l0aCBhIHJlZi4gVGhpcyBuZWVkcyB0byBiZSB1cGRhdGVkCiAgLy8gaW4gdGhlIGxheW91dCBwaGFzZSBzbyB3ZSBjYW4gYWNjZXNzIGl0IGR1cmluZyB0aGUgdGVhcmluZyBjaGVjayB0aGF0CiAgLy8gaGFwcGVucyBvbiBzdWJzY3JpYmUuCgogIHVzZUxheW91dEVmZmVjdCgoKSA9PiB7CiAgICBpbnN0LnZhbHVlID0gdmFsdWU7CiAgICBpbnN0LmdldFNuYXBzaG90ID0gZ2V0U25hcHNob3Q7IC8vIFdoZW5ldmVyIGdldFNuYXBzaG90IG9yIHN1YnNjcmliZSBjaGFuZ2VzLCB3ZSBuZWVkIHRvIGNoZWNrIGluIHRoZQogICAgLy8gY29tbWl0IHBoYXNlIGlmIHRoZXJlIHdhcyBhbiBpbnRlcmxlYXZlZCBtdXRhdGlvbi4gSW4gY29uY3VycmVudCBtb2RlCiAgICAvLyB0aGlzIGNhbiBoYXBwZW4gYWxsIHRoZSB0aW1lLCBidXQgZXZlbiBpbiBzeW5jaHJvbm91cyBtb2RlLCBhbiBlYXJsaWVyCiAgICAvLyBlZmZlY3QgbWF5IGhhdmUgbXV0YXRlZCB0aGUgc3RvcmUuCgogICAgaWYgKGNoZWNrSWZTbmFwc2hvdENoYW5nZWQoaW5zdCkpIHsKICAgICAgLy8gRm9yY2UgYSByZS1yZW5kZXIuCiAgICAgIGZvcmNlVXBkYXRlKHsKICAgICAgICBpbnN0CiAgICAgIH0pOwogICAgfSAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVhY3QtaG9va3MvZXhoYXVzdGl2ZS1kZXBzCgogIH0sIFtzdWJzY3JpYmUsIHZhbHVlLCBnZXRTbmFwc2hvdF0pOwogIHVzZUVmZmVjdCgoKSA9PiB7CiAgICAvLyBDaGVjayBmb3IgY2hhbmdlcyByaWdodCBiZWZvcmUgc3Vic2NyaWJpbmcuIFN1YnNlcXVlbnQgY2hhbmdlcyB3aWxsIGJlCiAgICAvLyBkZXRlY3RlZCBpbiB0aGUgc3Vic2NyaXB0aW9uIGhhbmRsZXIuCiAgICBpZiAoY2hlY2tJZlNuYXBzaG90Q2hhbmdlZChpbnN0KSkgewogICAgICAvLyBGb3JjZSBhIHJlLXJlbmRlci4KICAgICAgZm9yY2VVcGRhdGUoewogICAgICAgIGluc3QKICAgICAgfSk7CiAgICB9CgogICAgY29uc3QgaGFuZGxlU3RvcmVDaGFuZ2UgPSAoKSA9PiB7CiAgICAgIC8vIFRPRE86IEJlY2F1c2UgdGhlcmUgaXMgbm8gY3Jvc3MtcmVuZGVyZXIgQVBJIGZvciBiYXRjaGluZyB1cGRhdGVzLCBpdCdzCiAgICAgIC8vIHVwIHRvIHRoZSBjb25zdW1lciBvZiB0aGlzIGxpYnJhcnkgdG8gd3JhcCB0aGVpciBzdWJzY3JpcHRpb24gZXZlbnQKICAgICAgLy8gd2l0aCB1bnN0YWJsZV9iYXRjaGVkVXBkYXRlcy4gU2hvdWxkIHdlIHRyeSB0byBkZXRlY3Qgd2hlbiB0aGlzIGlzbid0CiAgICAgIC8vIHRoZSBjYXNlIGFuZCBwcmludCBhIHdhcm5pbmcgaW4gZGV2ZWxvcG1lbnQ/CiAgICAgIC8vIFRoZSBzdG9yZSBjaGFuZ2VkLiBDaGVjayBpZiB0aGUgc25hcHNob3QgY2hhbmdlZCBzaW5jZSB0aGUgbGFzdCB0aW1lIHdlCiAgICAgIC8vIHJlYWQgZnJvbSB0aGUgc3RvcmUuCiAgICAgIGlmIChjaGVja0lmU25hcHNob3RDaGFuZ2VkKGluc3QpKSB7CiAgICAgICAgLy8gRm9yY2UgYSByZS1yZW5kZXIuCiAgICAgICAgZm9yY2VVcGRhdGUoewogICAgICAgICAgaW5zdAogICAgICAgIH0pOwogICAgICB9CiAgICB9OyAvLyBTdWJzY3JpYmUgdG8gdGhlIHN0b3JlIGFuZCByZXR1cm4gYSBjbGVhbi11cCBmdW5jdGlvbi4KCgogICAgcmV0dXJuIHN1YnNjcmliZShoYW5kbGVTdG9yZUNoYW5nZSk7IC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHMKICB9LCBbc3Vic2NyaWJlXSk7CiAgdXNlRGVidWdWYWx1ZSh2YWx1ZSk7CiAgcmV0dXJuIHZhbHVlOwp9CgpmdW5jdGlvbiBjaGVja0lmU25hcHNob3RDaGFuZ2VkKGluc3QpIHsKICBjb25zdCBsYXRlc3RHZXRTbmFwc2hvdCA9IGluc3QuZ2V0U25hcHNob3Q7CiAgY29uc3QgcHJldlZhbHVlID0gaW5zdC52YWx1ZTsKCiAgdHJ5IHsKICAgIGNvbnN0IG5leHRWYWx1ZSA9IGxhdGVzdEdldFNuYXBzaG90KCk7CiAgICByZXR1cm4gIWlzKHByZXZWYWx1ZSwgbmV4dFZhbHVlKTsKICB9IGNhdGNoIChlcnJvcikgewogICAgcmV0dXJuIHRydWU7CiAgfQp9CgovKioKICogQ29weXJpZ2h0IChjKSBGYWNlYm9vaywgSW5jLiBhbmQgaXRzIGFmZmlsaWF0ZXMuCiAqCiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZQogKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuCiAqCiAqIEBmbG93CiAqLwpmdW5jdGlvbiB1c2VTeW5jRXh0ZXJuYWxTdG9yZSQxKHN1YnNjcmliZSwgZ2V0U25hcHNob3QsIGdldFNlcnZlclNuYXBzaG90KSB7CiAgLy8gTm90ZTogVGhlIHNoaW0gZG9lcyBub3QgdXNlIGdldFNlcnZlclNuYXBzaG90LCBiZWNhdXNlIHByZS0xOCB2ZXJzaW9ucyBvZgogIC8vIFJlYWN0IGRvIG5vdCBleHBvc2UgYSB3YXkgdG8gY2hlY2sgaWYgd2UncmUgaHlkcmF0aW5nLiBTbyB1c2VycyBvZiB0aGUgc2hpbQogIC8vIHdpbGwgbmVlZCB0byB0cmFjayB0aGF0IHRoZW1zZWx2ZXMgYW5kIHJldHVybiB0aGUgY29ycmVjdCB2YWx1ZQogIC8vIGZyb20gYGdldFNuYXBzaG90YC4KICByZXR1cm4gZ2V0U25hcHNob3QoKTsKfQoKLyoqCiAqIElubGluZWQgaW50byB0aGUgcmVhY3Qtcm91dGVyIHJlcG8gc2luY2UgdXNlLXN5bmMtZXh0ZXJuYWwtc3RvcmUgZG9lcyBub3QKICogcHJvdmlkZSBhIFVNRC1jb21wYXRpYmxlIHBhY2thZ2UsIHNvIHdlIG5lZWQgdGhpcyB0byBiZSBhYmxlIHRvIGRpc3RyaWJ1dGUKICogVU1EIHJlYWN0LXJvdXRlciBidW5kbGVzCiAqLwpjb25zdCBjYW5Vc2VET00gPSAhISh0eXBlb2Ygd2luZG93ICE9PSAidW5kZWZpbmVkIiAmJiB0eXBlb2Ygd2luZG93LmRvY3VtZW50ICE9PSAidW5kZWZpbmVkIiAmJiB0eXBlb2Ygd2luZG93LmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQgIT09ICJ1bmRlZmluZWQiKTsKY29uc3QgaXNTZXJ2ZXJFbnZpcm9ubWVudCA9ICFjYW5Vc2VET007CmNvbnN0IHNoaW0gPSBpc1NlcnZlckVudmlyb25tZW50ID8gdXNlU3luY0V4dGVybmFsU3RvcmUkMSA6IHVzZVN5bmNFeHRlcm5hbFN0b3JlJDI7CmNvbnN0IHVzZVN5bmNFeHRlcm5hbFN0b3JlID0gIHRydWUgPyAobW9kdWxlID0+IG1vZHVsZS51c2VTeW5jRXh0ZXJuYWxTdG9yZSkocmVhY3RfbmFtZXNwYWNlT2JqZWN0KSA6IHNoaW07CgovLyBDb250ZXh0cyBmb3IgZGF0YSByb3V0ZXJzCmNvbnN0IERhdGFTdGF0aWNSb3V0ZXJDb250ZXh0ID0gLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUNvbnRleHQobnVsbCk7CgppZiAoZmFsc2UpIHt9Cgpjb25zdCBEYXRhUm91dGVyQ29udGV4dCA9IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVDb250ZXh0KG51bGwpOwoKaWYgKGZhbHNlKSB7fQoKY29uc3QgRGF0YVJvdXRlclN0YXRlQ29udGV4dCA9IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVDb250ZXh0KG51bGwpOwoKaWYgKGZhbHNlKSB7fQoKY29uc3QgQXdhaXRDb250ZXh0ID0gLyojX19QVVJFX18qLygvKiB1bnVzZWQgcHVyZSBleHByZXNzaW9uIG9yIHN1cGVyICovIG51bGwgJiYgKFJlYWN0LmNyZWF0ZUNvbnRleHQobnVsbCkpKTsKCmlmIChmYWxzZSkge30KCmNvbnN0IE5hdmlnYXRpb25Db250ZXh0ID0gLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUNvbnRleHQobnVsbCk7CgppZiAoZmFsc2UpIHt9Cgpjb25zdCBMb2NhdGlvbkNvbnRleHQgPSAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlQ29udGV4dChudWxsKTsKCmlmIChmYWxzZSkge30KCmNvbnN0IFJvdXRlQ29udGV4dCA9IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVDb250ZXh0KHsKICBvdXRsZXQ6IG51bGwsCiAgbWF0Y2hlczogW10KfSk7CgppZiAoZmFsc2UpIHt9Cgpjb25zdCBSb3V0ZUVycm9yQ29udGV4dCA9IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVDb250ZXh0KG51bGwpOwoKaWYgKGZhbHNlKSB7fQoKLyoqCiAqIFJldHVybnMgdGhlIGZ1bGwgaHJlZiBmb3IgdGhlIGdpdmVuICJ0byIgdmFsdWUuIFRoaXMgaXMgdXNlZnVsIGZvciBidWlsZGluZwogKiBjdXN0b20gbGlua3MgdGhhdCBhcmUgYWxzbyBhY2Nlc3NpYmxlIGFuZCBwcmVzZXJ2ZSByaWdodC1jbGljayBiZWhhdmlvci4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1ocmVmCiAqLwoKZnVuY3Rpb24gdXNlSHJlZih0bywgX3RlbXApIHsKICBsZXQgewogICAgcmVsYXRpdmUKICB9ID0gX3RlbXAgPT09IHZvaWQgMCA/IHt9IDogX3RlbXA7CiAgIXVzZUluUm91dGVyQ29udGV4dCgpID8gIGZhbHNlID8gMCA6IHJvdXRlcl9pbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIGxldCB7CiAgICBiYXNlbmFtZSwKICAgIG5hdmlnYXRvcgogIH0gPSByZWFjdC51c2VDb250ZXh0KE5hdmlnYXRpb25Db250ZXh0KTsKICBsZXQgewogICAgaGFzaCwKICAgIHBhdGhuYW1lLAogICAgc2VhcmNoCiAgfSA9IGRpc3RfdXNlUmVzb2x2ZWRQYXRoKHRvLCB7CiAgICByZWxhdGl2ZQogIH0pOwogIGxldCBqb2luZWRQYXRobmFtZSA9IHBhdGhuYW1lOyAvLyBJZiB3ZSdyZSBvcGVyYXRpbmcgd2l0aGluIGEgYmFzZW5hbWUsIHByZXBlbmQgaXQgdG8gdGhlIHBhdGhuYW1lIHByaW9yCiAgLy8gdG8gY3JlYXRpbmcgdGhlIGhyZWYuICBJZiB0aGlzIGlzIGEgcm9vdCBuYXZpZ2F0aW9uLCB0aGVuIGp1c3QgdXNlIHRoZSByYXcKICAvLyBiYXNlbmFtZSB3aGljaCBhbGxvd3MgdGhlIGJhc2VuYW1lIHRvIGhhdmUgZnVsbCBjb250cm9sIG92ZXIgdGhlIHByZXNlbmNlCiAgLy8gb2YgYSB0cmFpbGluZyBzbGFzaCBvbiByb290IGxpbmtzCgogIGlmIChiYXNlbmFtZSAhPT0gIi8iKSB7CiAgICBqb2luZWRQYXRobmFtZSA9IHBhdGhuYW1lID09PSAiLyIgPyBiYXNlbmFtZSA6IHJvdXRlcl9qb2luUGF0aHMoW2Jhc2VuYW1lLCBwYXRobmFtZV0pOwogIH0KCiAgcmV0dXJuIG5hdmlnYXRvci5jcmVhdGVIcmVmKHsKICAgIHBhdGhuYW1lOiBqb2luZWRQYXRobmFtZSwKICAgIHNlYXJjaCwKICAgIGhhc2gKICB9KTsKfQovKioKICogUmV0dXJucyB0cnVlIGlmIHRoaXMgY29tcG9uZW50IGlzIGEgZGVzY2VuZGFudCBvZiBhIDxSb3V0ZXI+LgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvaG9va3MvdXNlLWluLXJvdXRlci1jb250ZXh0CiAqLwoKZnVuY3Rpb24gdXNlSW5Sb3V0ZXJDb250ZXh0KCkgewogIHJldHVybiByZWFjdC51c2VDb250ZXh0KExvY2F0aW9uQ29udGV4dCkgIT0gbnVsbDsKfQovKioKICogUmV0dXJucyB0aGUgY3VycmVudCBsb2NhdGlvbiBvYmplY3QsIHdoaWNoIHJlcHJlc2VudHMgdGhlIGN1cnJlbnQgVVJMIGluIHdlYgogKiBicm93c2Vycy4KICoKICogTm90ZTogSWYgeW91J3JlIHVzaW5nIHRoaXMgaXQgbWF5IG1lYW4geW91J3JlIGRvaW5nIHNvbWUgb2YgeW91ciBvd24KICogInJvdXRpbmciIGluIHlvdXIgYXBwLCBhbmQgd2UnZCBsaWtlIHRvIGtub3cgd2hhdCB5b3VyIHVzZSBjYXNlIGlzLiBXZSBtYXkKICogYmUgYWJsZSB0byBwcm92aWRlIHNvbWV0aGluZyBoaWdoZXItbGV2ZWwgdG8gYmV0dGVyIHN1aXQgeW91ciBuZWVkcy4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1sb2NhdGlvbgogKi8KCmZ1bmN0aW9uIGRpc3RfdXNlTG9jYXRpb24oKSB7CiAgIXVzZUluUm91dGVyQ29udGV4dCgpID8gIGZhbHNlID8gMCA6IHJvdXRlcl9pbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIHJldHVybiByZWFjdC51c2VDb250ZXh0KExvY2F0aW9uQ29udGV4dCkubG9jYXRpb247Cn0KLyoqCiAqIFJldHVybnMgdGhlIGN1cnJlbnQgbmF2aWdhdGlvbiBhY3Rpb24gd2hpY2ggZGVzY3JpYmVzIGhvdyB0aGUgcm91dGVyIGNhbWUgdG8KICogdGhlIGN1cnJlbnQgbG9jYXRpb24sIGVpdGhlciBieSBhIHBvcCwgcHVzaCwgb3IgcmVwbGFjZSBvbiB0aGUgaGlzdG9yeSBzdGFjay4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1uYXZpZ2F0aW9uLXR5cGUKICovCgpmdW5jdGlvbiB1c2VOYXZpZ2F0aW9uVHlwZSgpIHsKICByZXR1cm4gUmVhY3QudXNlQ29udGV4dChMb2NhdGlvbkNvbnRleHQpLm5hdmlnYXRpb25UeXBlOwp9Ci8qKgogKiBSZXR1cm5zIHRydWUgaWYgdGhlIFVSTCBmb3IgdGhlIGdpdmVuICJ0byIgdmFsdWUgbWF0Y2hlcyB0aGUgY3VycmVudCBVUkwuCiAqIFRoaXMgaXMgdXNlZnVsIGZvciBjb21wb25lbnRzIHRoYXQgbmVlZCB0byBrbm93ICJhY3RpdmUiIHN0YXRlLCBlLmcuCiAqIDxOYXZMaW5rPi4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1tYXRjaAogKi8KCmZ1bmN0aW9uIHVzZU1hdGNoKHBhdHRlcm4pIHsKICAhdXNlSW5Sb3V0ZXJDb250ZXh0KCkgPyAgZmFsc2UgPyAwIDogaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICBsZXQgewogICAgcGF0aG5hbWUKICB9ID0gZGlzdF91c2VMb2NhdGlvbigpOwogIHJldHVybiBSZWFjdC51c2VNZW1vKCgpID0+IG1hdGNoUGF0aChwYXR0ZXJuLCBwYXRobmFtZSksIFtwYXRobmFtZSwgcGF0dGVybl0pOwp9Ci8qKgogKiBUaGUgaW50ZXJmYWNlIGZvciB0aGUgbmF2aWdhdGUoKSBmdW5jdGlvbiByZXR1cm5lZCBmcm9tIHVzZU5hdmlnYXRlKCkuCiAqLwoKLyoqCiAqIFJldHVybnMgYW4gaW1wZXJhdGl2ZSBtZXRob2QgZm9yIGNoYW5naW5nIHRoZSBsb2NhdGlvbi4gVXNlZCBieSA8TGluaz5zLCBidXQKICogbWF5IGFsc28gYmUgdXNlZCBieSBvdGhlciBlbGVtZW50cyB0byBjaGFuZ2UgdGhlIGxvY2F0aW9uLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvaG9va3MvdXNlLW5hdmlnYXRlCiAqLwpmdW5jdGlvbiBkaXN0X3VzZU5hdmlnYXRlKCkgewogICF1c2VJblJvdXRlckNvbnRleHQoKSA/ICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICBsZXQgewogICAgYmFzZW5hbWUsCiAgICBuYXZpZ2F0b3IKICB9ID0gcmVhY3QudXNlQ29udGV4dChOYXZpZ2F0aW9uQ29udGV4dCk7CiAgbGV0IHsKICAgIG1hdGNoZXMKICB9ID0gcmVhY3QudXNlQ29udGV4dChSb3V0ZUNvbnRleHQpOwogIGxldCB7CiAgICBwYXRobmFtZTogbG9jYXRpb25QYXRobmFtZQogIH0gPSBkaXN0X3VzZUxvY2F0aW9uKCk7CiAgbGV0IHJvdXRlUGF0aG5hbWVzSnNvbiA9IEpTT04uc3RyaW5naWZ5KGdldFBhdGhDb250cmlidXRpbmdNYXRjaGVzKG1hdGNoZXMpLm1hcChtYXRjaCA9PiBtYXRjaC5wYXRobmFtZUJhc2UpKTsKICBsZXQgYWN0aXZlUmVmID0gcmVhY3QudXNlUmVmKGZhbHNlKTsKICByZWFjdC51c2VFZmZlY3QoKCkgPT4gewogICAgYWN0aXZlUmVmLmN1cnJlbnQgPSB0cnVlOwogIH0pOwogIGxldCBuYXZpZ2F0ZSA9IHJlYWN0LnVzZUNhbGxiYWNrKGZ1bmN0aW9uICh0bywgb3B0aW9ucykgewogICAgaWYgKG9wdGlvbnMgPT09IHZvaWQgMCkgewogICAgICBvcHRpb25zID0ge307CiAgICB9CgogICAgIGZhbHNlID8gMCA6IHZvaWQgMDsKICAgIGlmICghYWN0aXZlUmVmLmN1cnJlbnQpIHJldHVybjsKCiAgICBpZiAodHlwZW9mIHRvID09PSAibnVtYmVyIikgewogICAgICBuYXZpZ2F0b3IuZ28odG8pOwogICAgICByZXR1cm47CiAgICB9CgogICAgbGV0IHBhdGggPSByZXNvbHZlVG8odG8sIEpTT04ucGFyc2Uocm91dGVQYXRobmFtZXNKc29uKSwgbG9jYXRpb25QYXRobmFtZSwgb3B0aW9ucy5yZWxhdGl2ZSA9PT0gInBhdGgiKTsgLy8gSWYgd2UncmUgb3BlcmF0aW5nIHdpdGhpbiBhIGJhc2VuYW1lLCBwcmVwZW5kIGl0IHRvIHRoZSBwYXRobmFtZSBwcmlvcgogICAgLy8gdG8gaGFuZGluZyBvZmYgdG8gaGlzdG9yeS4gIElmIHRoaXMgaXMgYSByb290IG5hdmlnYXRpb24sIHRoZW4gd2UKICAgIC8vIG5hdmlnYXRlIHRvIHRoZSByYXcgYmFzZW5hbWUgd2hpY2ggYWxsb3dzIHRoZSBiYXNlbmFtZSB0byBoYXZlIGZ1bGwKICAgIC8vIGNvbnRyb2wgb3ZlciB0aGUgcHJlc2VuY2Ugb2YgYSB0cmFpbGluZyBzbGFzaCBvbiByb290IGxpbmtzCgogICAgaWYgKGJhc2VuYW1lICE9PSAiLyIpIHsKICAgICAgcGF0aC5wYXRobmFtZSA9IHBhdGgucGF0aG5hbWUgPT09ICIvIiA/IGJhc2VuYW1lIDogcm91dGVyX2pvaW5QYXRocyhbYmFzZW5hbWUsIHBhdGgucGF0aG5hbWVdKTsKICAgIH0KCiAgICAoISFvcHRpb25zLnJlcGxhY2UgPyBuYXZpZ2F0b3IucmVwbGFjZSA6IG5hdmlnYXRvci5wdXNoKShwYXRoLCBvcHRpb25zLnN0YXRlLCBvcHRpb25zKTsKICB9LCBbYmFzZW5hbWUsIG5hdmlnYXRvciwgcm91dGVQYXRobmFtZXNKc29uLCBsb2NhdGlvblBhdGhuYW1lXSk7CiAgcmV0dXJuIG5hdmlnYXRlOwp9CmNvbnN0IE91dGxldENvbnRleHQgPSAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlQ29udGV4dChudWxsKTsKLyoqCiAqIFJldHVybnMgdGhlIGNvbnRleHQgKGlmIHByb3ZpZGVkKSBmb3IgdGhlIGNoaWxkIHJvdXRlIGF0IHRoaXMgbGV2ZWwgb2YgdGhlIHJvdXRlCiAqIGhpZXJhcmNoeS4KICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1vdXRsZXQtY29udGV4dAogKi8KCmZ1bmN0aW9uIHVzZU91dGxldENvbnRleHQoKSB7CiAgcmV0dXJuIFJlYWN0LnVzZUNvbnRleHQoT3V0bGV0Q29udGV4dCk7Cn0KLyoqCiAqIFJldHVybnMgdGhlIGVsZW1lbnQgZm9yIHRoZSBjaGlsZCByb3V0ZSBhdCB0aGlzIGxldmVsIG9mIHRoZSByb3V0ZQogKiBoaWVyYXJjaHkuIFVzZWQgaW50ZXJuYWxseSBieSA8T3V0bGV0PiB0byByZW5kZXIgY2hpbGQgcm91dGVzLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvaG9va3MvdXNlLW91dGxldAogKi8KCmZ1bmN0aW9uIHVzZU91dGxldChjb250ZXh0KSB7CiAgbGV0IG91dGxldCA9IHJlYWN0LnVzZUNvbnRleHQoUm91dGVDb250ZXh0KS5vdXRsZXQ7CgogIGlmIChvdXRsZXQpIHsKICAgIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChPdXRsZXRDb250ZXh0LlByb3ZpZGVyLCB7CiAgICAgIHZhbHVlOiBjb250ZXh0CiAgICB9LCBvdXRsZXQpOwogIH0KCiAgcmV0dXJuIG91dGxldDsKfQovKioKICogUmV0dXJucyBhbiBvYmplY3Qgb2Yga2V5L3ZhbHVlIHBhaXJzIG9mIHRoZSBkeW5hbWljIHBhcmFtcyBmcm9tIHRoZSBjdXJyZW50CiAqIFVSTCB0aGF0IHdlcmUgbWF0Y2hlZCBieSB0aGUgcm91dGUgcGF0aC4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2hvb2tzL3VzZS1wYXJhbXMKICovCgpmdW5jdGlvbiB1c2VQYXJhbXMoKSB7CiAgbGV0IHsKICAgIG1hdGNoZXMKICB9ID0gUmVhY3QudXNlQ29udGV4dChSb3V0ZUNvbnRleHQpOwogIGxldCByb3V0ZU1hdGNoID0gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdOwogIHJldHVybiByb3V0ZU1hdGNoID8gcm91dGVNYXRjaC5wYXJhbXMgOiB7fTsKfQovKioKICogUmVzb2x2ZXMgdGhlIHBhdGhuYW1lIG9mIHRoZSBnaXZlbiBgdG9gIHZhbHVlIGFnYWluc3QgdGhlIGN1cnJlbnQgbG9jYXRpb24uCiAqCiAqIEBzZWUgaHR0cHM6Ly9yZWFjdHJvdXRlci5jb20vZG9jcy9lbi92Ni9ob29rcy91c2UtcmVzb2x2ZWQtcGF0aAogKi8KCmZ1bmN0aW9uIGRpc3RfdXNlUmVzb2x2ZWRQYXRoKHRvLCBfdGVtcDIpIHsKICBsZXQgewogICAgcmVsYXRpdmUKICB9ID0gX3RlbXAyID09PSB2b2lkIDAgPyB7fSA6IF90ZW1wMjsKICBsZXQgewogICAgbWF0Y2hlcwogIH0gPSByZWFjdC51c2VDb250ZXh0KFJvdXRlQ29udGV4dCk7CiAgbGV0IHsKICAgIHBhdGhuYW1lOiBsb2NhdGlvblBhdGhuYW1lCiAgfSA9IGRpc3RfdXNlTG9jYXRpb24oKTsKICBsZXQgcm91dGVQYXRobmFtZXNKc29uID0gSlNPTi5zdHJpbmdpZnkoZ2V0UGF0aENvbnRyaWJ1dGluZ01hdGNoZXMobWF0Y2hlcykubWFwKG1hdGNoID0+IG1hdGNoLnBhdGhuYW1lQmFzZSkpOwogIHJldHVybiByZWFjdC51c2VNZW1vKCgpID0+IHJlc29sdmVUbyh0bywgSlNPTi5wYXJzZShyb3V0ZVBhdGhuYW1lc0pzb24pLCBsb2NhdGlvblBhdGhuYW1lLCByZWxhdGl2ZSA9PT0gInBhdGgiKSwgW3RvLCByb3V0ZVBhdGhuYW1lc0pzb24sIGxvY2F0aW9uUGF0aG5hbWUsIHJlbGF0aXZlXSk7Cn0KLyoqCiAqIFJldHVybnMgdGhlIGVsZW1lbnQgb2YgdGhlIHJvdXRlIHRoYXQgbWF0Y2hlZCB0aGUgY3VycmVudCBsb2NhdGlvbiwgcHJlcGFyZWQKICogd2l0aCB0aGUgY29ycmVjdCBjb250ZXh0IHRvIHJlbmRlciB0aGUgcmVtYWluZGVyIG9mIHRoZSByb3V0ZSB0cmVlLiBSb3V0ZQogKiBlbGVtZW50cyBpbiB0aGUgdHJlZSBtdXN0IHJlbmRlciBhbiA8T3V0bGV0PiB0byByZW5kZXIgdGhlaXIgY2hpbGQgcm91dGUncwogKiBlbGVtZW50LgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvaG9va3MvdXNlLXJvdXRlcwogKi8KCmZ1bmN0aW9uIHVzZVJvdXRlcyhyb3V0ZXMsIGxvY2F0aW9uQXJnKSB7CiAgIXVzZUluUm91dGVyQ29udGV4dCgpID8gIGZhbHNlID8gMCA6IHJvdXRlcl9pbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIGxldCBkYXRhUm91dGVyU3RhdGVDb250ZXh0ID0gcmVhY3QudXNlQ29udGV4dChEYXRhUm91dGVyU3RhdGVDb250ZXh0KTsKICBsZXQgewogICAgbWF0Y2hlczogcGFyZW50TWF0Y2hlcwogIH0gPSByZWFjdC51c2VDb250ZXh0KFJvdXRlQ29udGV4dCk7CiAgbGV0IHJvdXRlTWF0Y2ggPSBwYXJlbnRNYXRjaGVzW3BhcmVudE1hdGNoZXMubGVuZ3RoIC0gMV07CiAgbGV0IHBhcmVudFBhcmFtcyA9IHJvdXRlTWF0Y2ggPyByb3V0ZU1hdGNoLnBhcmFtcyA6IHt9OwogIGxldCBwYXJlbnRQYXRobmFtZSA9IHJvdXRlTWF0Y2ggPyByb3V0ZU1hdGNoLnBhdGhuYW1lIDogIi8iOwogIGxldCBwYXJlbnRQYXRobmFtZUJhc2UgPSByb3V0ZU1hdGNoID8gcm91dGVNYXRjaC5wYXRobmFtZUJhc2UgOiAiLyI7CiAgbGV0IHBhcmVudFJvdXRlID0gcm91dGVNYXRjaCAmJiByb3V0ZU1hdGNoLnJvdXRlOwoKICBpZiAoZmFsc2UpIHt9CgogIGxldCBsb2NhdGlvbkZyb21Db250ZXh0ID0gZGlzdF91c2VMb2NhdGlvbigpOwogIGxldCBsb2NhdGlvbjsKCiAgaWYgKGxvY2F0aW9uQXJnKSB7CiAgICB2YXIgX3BhcnNlZExvY2F0aW9uQXJnJHBhOwoKICAgIGxldCBwYXJzZWRMb2NhdGlvbkFyZyA9IHR5cGVvZiBsb2NhdGlvbkFyZyA9PT0gInN0cmluZyIgPyBwYXJzZVBhdGgobG9jYXRpb25BcmcpIDogbG9jYXRpb25Bcmc7CiAgICAhKHBhcmVudFBhdGhuYW1lQmFzZSA9PT0gIi8iIHx8ICgoX3BhcnNlZExvY2F0aW9uQXJnJHBhID0gcGFyc2VkTG9jYXRpb25BcmcucGF0aG5hbWUpID09IG51bGwgPyB2b2lkIDAgOiBfcGFyc2VkTG9jYXRpb25BcmckcGEuc3RhcnRzV2l0aChwYXJlbnRQYXRobmFtZUJhc2UpKSkgPyAgZmFsc2UgPyAwIDogcm91dGVyX2ludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgICBsb2NhdGlvbiA9IHBhcnNlZExvY2F0aW9uQXJnOwogIH0gZWxzZSB7CiAgICBsb2NhdGlvbiA9IGxvY2F0aW9uRnJvbUNvbnRleHQ7CiAgfQoKICBsZXQgcGF0aG5hbWUgPSBsb2NhdGlvbi5wYXRobmFtZSB8fCAiLyI7CiAgbGV0IHJlbWFpbmluZ1BhdGhuYW1lID0gcGFyZW50UGF0aG5hbWVCYXNlID09PSAiLyIgPyBwYXRobmFtZSA6IHBhdGhuYW1lLnNsaWNlKHBhcmVudFBhdGhuYW1lQmFzZS5sZW5ndGgpIHx8ICIvIjsKICBsZXQgbWF0Y2hlcyA9IG1hdGNoUm91dGVzKHJvdXRlcywgewogICAgcGF0aG5hbWU6IHJlbWFpbmluZ1BhdGhuYW1lCiAgfSk7CgogIGlmIChmYWxzZSkge30KCiAgbGV0IHJlbmRlcmVkTWF0Y2hlcyA9IF9yZW5kZXJNYXRjaGVzKG1hdGNoZXMgJiYgbWF0Y2hlcy5tYXAobWF0Y2ggPT4gT2JqZWN0LmFzc2lnbih7fSwgbWF0Y2gsIHsKICAgIHBhcmFtczogT2JqZWN0LmFzc2lnbih7fSwgcGFyZW50UGFyYW1zLCBtYXRjaC5wYXJhbXMpLAogICAgcGF0aG5hbWU6IHJvdXRlcl9qb2luUGF0aHMoW3BhcmVudFBhdGhuYW1lQmFzZSwgbWF0Y2gucGF0aG5hbWVdKSwKICAgIHBhdGhuYW1lQmFzZTogbWF0Y2gucGF0aG5hbWVCYXNlID09PSAiLyIgPyBwYXJlbnRQYXRobmFtZUJhc2UgOiByb3V0ZXJfam9pblBhdGhzKFtwYXJlbnRQYXRobmFtZUJhc2UsIG1hdGNoLnBhdGhuYW1lQmFzZV0pCiAgfSkpLCBwYXJlbnRNYXRjaGVzLCBkYXRhUm91dGVyU3RhdGVDb250ZXh0IHx8IHVuZGVmaW5lZCk7IC8vIFdoZW4gYSB1c2VyIHBhc3NlcyBpbiBhIGBsb2NhdGlvbkFyZ2AsIHRoZSBhc3NvY2lhdGVkIHJvdXRlcyBuZWVkIHRvCiAgLy8gYmUgd3JhcHBlZCBpbiBhIG5ldyBgTG9jYXRpb25Db250ZXh0LlByb3ZpZGVyYCBpbiBvcmRlciBmb3IgYHVzZUxvY2F0aW9uYAogIC8vIHRvIHVzZSB0aGUgc2NvcGVkIGxvY2F0aW9uIGluc3RlYWQgb2YgdGhlIGdsb2JhbCBsb2NhdGlvbi4KCgogIGlmIChsb2NhdGlvbkFyZyAmJiByZW5kZXJlZE1hdGNoZXMpIHsKICAgIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChMb2NhdGlvbkNvbnRleHQuUHJvdmlkZXIsIHsKICAgICAgdmFsdWU6IHsKICAgICAgICBsb2NhdGlvbjogZGlzdF9leHRlbmRzKHsKICAgICAgICAgIHBhdGhuYW1lOiAiLyIsCiAgICAgICAgICBzZWFyY2g6ICIiLAogICAgICAgICAgaGFzaDogIiIsCiAgICAgICAgICBzdGF0ZTogbnVsbCwKICAgICAgICAgIGtleTogImRlZmF1bHQiCiAgICAgICAgfSwgbG9jYXRpb24pLAogICAgICAgIG5hdmlnYXRpb25UeXBlOiBBY3Rpb24uUG9wCiAgICAgIH0KICAgIH0sIHJlbmRlcmVkTWF0Y2hlcyk7CiAgfQoKICByZXR1cm4gcmVuZGVyZWRNYXRjaGVzOwp9CgpmdW5jdGlvbiBEZWZhdWx0RXJyb3JFbGVtZW50KCkgewogIGxldCBlcnJvciA9IHVzZVJvdXRlRXJyb3IoKTsKICBsZXQgbWVzc2FnZSA9IGlzUm91dGVFcnJvclJlc3BvbnNlKGVycm9yKSA/IGVycm9yLnN0YXR1cyArICIgIiArIGVycm9yLnN0YXR1c1RleHQgOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IEpTT04uc3RyaW5naWZ5KGVycm9yKTsKICBsZXQgc3RhY2sgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3Iuc3RhY2sgOiBudWxsOwogIGxldCBsaWdodGdyZXkgPSAicmdiYSgyMDAsMjAwLDIwMCwgMC41KSI7CiAgbGV0IHByZVN0eWxlcyA9IHsKICAgIHBhZGRpbmc6ICIwLjVyZW0iLAogICAgYmFja2dyb3VuZENvbG9yOiBsaWdodGdyZXkKICB9OwogIGxldCBjb2RlU3R5bGVzID0gewogICAgcGFkZGluZzogIjJweCA0cHgiLAogICAgYmFja2dyb3VuZENvbG9yOiBsaWdodGdyZXkKICB9OwogIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChyZWFjdC5GcmFnbWVudCwgbnVsbCwgLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoImgyIiwgbnVsbCwgIlVuaGFuZGxlZCBUaHJvd24gRXJyb3IhIiksIC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KCJoMyIsIHsKICAgIHN0eWxlOiB7CiAgICAgIGZvbnRTdHlsZTogIml0YWxpYyIKICAgIH0KICB9LCBtZXNzYWdlKSwgc3RhY2sgPyAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudCgicHJlIiwgewogICAgc3R5bGU6IHByZVN0eWxlcwogIH0sIHN0YWNrKSA6IG51bGwsIC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KCJwIiwgbnVsbCwgIlx1RDgzRFx1RENCRiBIZXkgZGV2ZWxvcGVyIFx1RDgzRFx1REM0QiIpLCAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudCgicCIsIG51bGwsICJZb3UgY2FuIHByb3ZpZGUgYSB3YXkgYmV0dGVyIFVYIHRoYW4gdGhpcyB3aGVuIHlvdXIgYXBwIHRocm93cyBlcnJvcnMgYnkgcHJvdmlkaW5nIHlvdXIgb3duXHhBMCIsIC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KCJjb2RlIiwgewogICAgc3R5bGU6IGNvZGVTdHlsZXMKICB9LCAiZXJyb3JFbGVtZW50IiksICIgcHJvcHMgb25ceEEwIiwgLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoImNvZGUiLCB7CiAgICBzdHlsZTogY29kZVN0eWxlcwogIH0sICI8Um91dGU+IikpKTsKfQoKY2xhc3MgUmVuZGVyRXJyb3JCb3VuZGFyeSBleHRlbmRzIHJlYWN0LkNvbXBvbmVudCB7CiAgY29uc3RydWN0b3IocHJvcHMpIHsKICAgIHN1cGVyKHByb3BzKTsKICAgIHRoaXMuc3RhdGUgPSB7CiAgICAgIGxvY2F0aW9uOiBwcm9wcy5sb2NhdGlvbiwKICAgICAgZXJyb3I6IHByb3BzLmVycm9yCiAgICB9OwogIH0KCiAgc3RhdGljIGdldERlcml2ZWRTdGF0ZUZyb21FcnJvcihlcnJvcikgewogICAgcmV0dXJuIHsKICAgICAgZXJyb3I6IGVycm9yCiAgICB9OwogIH0KCiAgc3RhdGljIGdldERlcml2ZWRTdGF0ZUZyb21Qcm9wcyhwcm9wcywgc3RhdGUpIHsKICAgIC8vIFdoZW4gd2UgZ2V0IGludG8gYW4gZXJyb3Igc3RhdGUsIHRoZSB1c2VyIHdpbGwgbGlrZWx5IGNsaWNrICJiYWNrIiB0byB0aGUKICAgIC8vIHByZXZpb3VzIHBhZ2UgdGhhdCBkaWRuJ3QgaGF2ZSBhbiBlcnJvci4gQmVjYXVzZSB0aGlzIHdyYXBzIHRoZSBlbnRpcmUKICAgIC8vIGFwcGxpY2F0aW9uLCB0aGF0IHdpbGwgaGF2ZSBubyBlZmZlY3QtLXRoZSBlcnJvciBwYWdlIGNvbnRpbnVlcyB0byBkaXNwbGF5LgogICAgLy8gVGhpcyBnaXZlcyB1cyBhIG1lY2hhbmlzbSB0byByZWNvdmVyIGZyb20gdGhlIGVycm9yIHdoZW4gdGhlIGxvY2F0aW9uIGNoYW5nZXMuCiAgICAvLwogICAgLy8gV2hldGhlciB3ZSdyZSBpbiBhbiBlcnJvciBzdGF0ZSBvciBub3QsIHdlIHVwZGF0ZSB0aGUgbG9jYXRpb24gaW4gc3RhdGUKICAgIC8vIHNvIHRoYXQgd2hlbiB3ZSBhcmUgaW4gYW4gZXJyb3Igc3RhdGUsIGl0IGdldHMgcmVzZXQgd2hlbiBhIG5ldyBsb2NhdGlvbgogICAgLy8gY29tZXMgaW4gYW5kIHRoZSB1c2VyIHJlY292ZXJzIGZyb20gdGhlIGVycm9yLgogICAgaWYgKHN0YXRlLmxvY2F0aW9uICE9PSBwcm9wcy5sb2NhdGlvbikgewogICAgICByZXR1cm4gewogICAgICAgIGVycm9yOiBwcm9wcy5lcnJvciwKICAgICAgICBsb2NhdGlvbjogcHJvcHMubG9jYXRpb24KICAgICAgfTsKICAgIH0gLy8gSWYgd2UncmUgbm90IGNoYW5naW5nIGxvY2F0aW9ucywgcHJlc2VydmUgdGhlIGxvY2F0aW9uIGJ1dCBzdGlsbCBzdXJmYWNlCiAgICAvLyBhbnkgbmV3IGVycm9ycyB0aGF0IG1heSBjb21lIHRocm91Z2guIFdlIHJldGFpbiB0aGUgZXhpc3RpbmcgZXJyb3IsIHdlIGRvCiAgICAvLyB0aGlzIGJlY2F1c2UgdGhlIGVycm9yIHByb3ZpZGVkIGZyb20gdGhlIGFwcCBzdGF0ZSBtYXkgYmUgY2xlYXJlZCB3aXRob3V0CiAgICAvLyB0aGUgbG9jYXRpb24gY2hhbmdpbmcuCgoKICAgIHJldHVybiB7CiAgICAgIGVycm9yOiBwcm9wcy5lcnJvciB8fCBzdGF0ZS5lcnJvciwKICAgICAgbG9jYXRpb246IHN0YXRlLmxvY2F0aW9uCiAgICB9OwogIH0KCiAgY29tcG9uZW50RGlkQ2F0Y2goZXJyb3IsIGVycm9ySW5mbykgewogICAgY29uc29sZS5lcnJvcigiUmVhY3QgUm91dGVyIGNhdWdodCB0aGUgZm9sbG93aW5nIGVycm9yIGR1cmluZyByZW5kZXIiLCBlcnJvciwgZXJyb3JJbmZvKTsKICB9CgogIHJlbmRlcigpIHsKICAgIHJldHVybiB0aGlzLnN0YXRlLmVycm9yID8gLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoUm91dGVFcnJvckNvbnRleHQuUHJvdmlkZXIsIHsKICAgICAgdmFsdWU6IHRoaXMuc3RhdGUuZXJyb3IsCiAgICAgIGNoaWxkcmVuOiB0aGlzLnByb3BzLmNvbXBvbmVudAogICAgfSkgOiB0aGlzLnByb3BzLmNoaWxkcmVuOwogIH0KCn0KCmZ1bmN0aW9uIFJlbmRlcmVkUm91dGUoX3JlZikgewogIGxldCB7CiAgICByb3V0ZUNvbnRleHQsCiAgICBtYXRjaCwKICAgIGNoaWxkcmVuCiAgfSA9IF9yZWY7CiAgbGV0IGRhdGFTdGF0aWNSb3V0ZXJDb250ZXh0ID0gcmVhY3QudXNlQ29udGV4dChEYXRhU3RhdGljUm91dGVyQ29udGV4dCk7IC8vIFRyYWNrIGhvdyBkZWVwIHdlIGdvdCBpbiBvdXIgcmVuZGVyIHBhc3MgdG8gZW11bGF0ZSBTU1IgY29tcG9uZW50RGlkQ2F0Y2gKICAvLyBpbiBhIERhdGFTdGF0aWNSb3V0ZXIKCiAgaWYgKGRhdGFTdGF0aWNSb3V0ZXJDb250ZXh0ICYmIG1hdGNoLnJvdXRlLmVycm9yRWxlbWVudCkgewogICAgZGF0YVN0YXRpY1JvdXRlckNvbnRleHQuX2RlZXBlc3RSZW5kZXJlZEJvdW5kYXJ5SWQgPSBtYXRjaC5yb3V0ZS5pZDsKICB9CgogIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChSb3V0ZUNvbnRleHQuUHJvdmlkZXIsIHsKICAgIHZhbHVlOiByb3V0ZUNvbnRleHQKICB9LCBjaGlsZHJlbik7Cn0KCmZ1bmN0aW9uIF9yZW5kZXJNYXRjaGVzKG1hdGNoZXMsIHBhcmVudE1hdGNoZXMsIGRhdGFSb3V0ZXJTdGF0ZSkgewogIGlmIChwYXJlbnRNYXRjaGVzID09PSB2b2lkIDApIHsKICAgIHBhcmVudE1hdGNoZXMgPSBbXTsKICB9CgogIGlmIChtYXRjaGVzID09IG51bGwpIHsKICAgIGlmIChkYXRhUm91dGVyU3RhdGUgIT0gbnVsbCAmJiBkYXRhUm91dGVyU3RhdGUuZXJyb3JzKSB7CiAgICAgIC8vIERvbid0IGJhaWwgaWYgd2UgaGF2ZSBkYXRhIHJvdXRlciBlcnJvcnMgc28gd2UgY2FuIHJlbmRlciB0aGVtIGluIHRoZQogICAgICAvLyBib3VuZGFyeS4gIFVzZSB0aGUgcHJlLW1hdGNoZWQgKG9yIHNoaW1tZWQpIG1hdGNoZXMKICAgICAgbWF0Y2hlcyA9IGRhdGFSb3V0ZXJTdGF0ZS5tYXRjaGVzOwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfQoKICBsZXQgcmVuZGVyZWRNYXRjaGVzID0gbWF0Y2hlczsgLy8gSWYgd2UgaGF2ZSBkYXRhIGVycm9ycywgdHJpbSBtYXRjaGVzIHRvIHRoZSBoaWdoZXN0IGVycm9yIGJvdW5kYXJ5CgogIGxldCBlcnJvcnMgPSBkYXRhUm91dGVyU3RhdGUgPT0gbnVsbCA/IHZvaWQgMCA6IGRhdGFSb3V0ZXJTdGF0ZS5lcnJvcnM7CgogIGlmIChlcnJvcnMgIT0gbnVsbCkgewogICAgbGV0IGVycm9ySW5kZXggPSByZW5kZXJlZE1hdGNoZXMuZmluZEluZGV4KG0gPT4gbS5yb3V0ZS5pZCAmJiAoZXJyb3JzID09IG51bGwgPyB2b2lkIDAgOiBlcnJvcnNbbS5yb3V0ZS5pZF0pKTsKICAgICEoZXJyb3JJbmRleCA+PSAwKSA/ICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICAgIHJlbmRlcmVkTWF0Y2hlcyA9IHJlbmRlcmVkTWF0Y2hlcy5zbGljZSgwLCBNYXRoLm1pbihyZW5kZXJlZE1hdGNoZXMubGVuZ3RoLCBlcnJvckluZGV4ICsgMSkpOwogIH0KCiAgcmV0dXJuIHJlbmRlcmVkTWF0Y2hlcy5yZWR1Y2VSaWdodCgob3V0bGV0LCBtYXRjaCwgaW5kZXgpID0+IHsKICAgIGxldCBlcnJvciA9IG1hdGNoLnJvdXRlLmlkID8gZXJyb3JzID09IG51bGwgPyB2b2lkIDAgOiBlcnJvcnNbbWF0Y2gucm91dGUuaWRdIDogbnVsbDsgLy8gT25seSBkYXRhIHJvdXRlcnMgaGFuZGxlIGVycm9ycwoKICAgIGxldCBlcnJvckVsZW1lbnQgPSBkYXRhUm91dGVyU3RhdGUgPyBtYXRjaC5yb3V0ZS5lcnJvckVsZW1lbnQgfHwgLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoRGVmYXVsdEVycm9yRWxlbWVudCwgbnVsbCkgOiBudWxsOwoKICAgIGxldCBnZXRDaGlsZHJlbiA9ICgpID0+IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KFJlbmRlcmVkUm91dGUsIHsKICAgICAgbWF0Y2g6IG1hdGNoLAogICAgICByb3V0ZUNvbnRleHQ6IHsKICAgICAgICBvdXRsZXQsCiAgICAgICAgbWF0Y2hlczogcGFyZW50TWF0Y2hlcy5jb25jYXQocmVuZGVyZWRNYXRjaGVzLnNsaWNlKDAsIGluZGV4ICsgMSkpCiAgICAgIH0KICAgIH0sIGVycm9yID8gZXJyb3JFbGVtZW50IDogbWF0Y2gucm91dGUuZWxlbWVudCAhPT0gdW5kZWZpbmVkID8gbWF0Y2gucm91dGUuZWxlbWVudCA6IG91dGxldCk7IC8vIE9ubHkgd3JhcCBpbiBhbiBlcnJvciBib3VuZGFyeSB3aXRoaW4gZGF0YSByb3V0ZXIgdXNhZ2VzIHdoZW4gd2UgaGF2ZSBhbgogICAgLy8gZXJyb3JFbGVtZW50IG9uIHRoaXMgcm91dGUuICBPdGhlcndpc2UgbGV0IGl0IGJ1YmJsZSB1cCB0byBhbiBhbmNlc3RvcgogICAgLy8gZXJyb3JFbGVtZW50CgoKICAgIHJldHVybiBkYXRhUm91dGVyU3RhdGUgJiYgKG1hdGNoLnJvdXRlLmVycm9yRWxlbWVudCB8fCBpbmRleCA9PT0gMCkgPyAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChSZW5kZXJFcnJvckJvdW5kYXJ5LCB7CiAgICAgIGxvY2F0aW9uOiBkYXRhUm91dGVyU3RhdGUubG9jYXRpb24sCiAgICAgIGNvbXBvbmVudDogZXJyb3JFbGVtZW50LAogICAgICBlcnJvcjogZXJyb3IsCiAgICAgIGNoaWxkcmVuOiBnZXRDaGlsZHJlbigpCiAgICB9KSA6IGdldENoaWxkcmVuKCk7CiAgfSwgbnVsbCk7Cn0KdmFyIERhdGFSb3V0ZXJIb29rOwoKKGZ1bmN0aW9uIChEYXRhUm91dGVySG9vaykgewogIERhdGFSb3V0ZXJIb29rWyJVc2VSZXZhbGlkYXRvciJdID0gInVzZVJldmFsaWRhdG9yIjsKfSkoRGF0YVJvdXRlckhvb2sgfHwgKERhdGFSb3V0ZXJIb29rID0ge30pKTsKCnZhciBEYXRhUm91dGVyU3RhdGVIb29rOwoKKGZ1bmN0aW9uIChEYXRhUm91dGVyU3RhdGVIb29rKSB7CiAgRGF0YVJvdXRlclN0YXRlSG9va1siVXNlTG9hZGVyRGF0YSJdID0gInVzZUxvYWRlckRhdGEiOwogIERhdGFSb3V0ZXJTdGF0ZUhvb2tbIlVzZUFjdGlvbkRhdGEiXSA9ICJ1c2VBY3Rpb25EYXRhIjsKICBEYXRhUm91dGVyU3RhdGVIb29rWyJVc2VSb3V0ZUVycm9yIl0gPSAidXNlUm91dGVFcnJvciI7CiAgRGF0YVJvdXRlclN0YXRlSG9va1siVXNlTmF2aWdhdGlvbiJdID0gInVzZU5hdmlnYXRpb24iOwogIERhdGFSb3V0ZXJTdGF0ZUhvb2tbIlVzZVJvdXRlTG9hZGVyRGF0YSJdID0gInVzZVJvdXRlTG9hZGVyRGF0YSI7CiAgRGF0YVJvdXRlclN0YXRlSG9va1siVXNlTWF0Y2hlcyJdID0gInVzZU1hdGNoZXMiOwogIERhdGFSb3V0ZXJTdGF0ZUhvb2tbIlVzZVJldmFsaWRhdG9yIl0gPSAidXNlUmV2YWxpZGF0b3IiOwp9KShEYXRhUm91dGVyU3RhdGVIb29rIHx8IChEYXRhUm91dGVyU3RhdGVIb29rID0ge30pKTsKCmZ1bmN0aW9uIGdldERhdGFSb3V0ZXJDb25zb2xlRXJyb3IoaG9va05hbWUpIHsKICByZXR1cm4gaG9va05hbWUgKyAiIG11c3QgYmUgdXNlZCB3aXRoaW4gYSBkYXRhIHJvdXRlci4gIFNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9lbi9tYWluL3JvdXRlcnMvcGlja2luZy1hLXJvdXRlci4iOwp9CgpmdW5jdGlvbiB1c2VEYXRhUm91dGVyQ29udGV4dChob29rTmFtZSkgewogIGxldCBjdHggPSBSZWFjdC51c2VDb250ZXh0KERhdGFSb3V0ZXJDb250ZXh0KTsKICAhY3R4ID8gIGZhbHNlID8gMCA6IGludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgcmV0dXJuIGN0eDsKfQoKZnVuY3Rpb24gdXNlRGF0YVJvdXRlclN0YXRlKGhvb2tOYW1lKSB7CiAgbGV0IHN0YXRlID0gcmVhY3QudXNlQ29udGV4dChEYXRhUm91dGVyU3RhdGVDb250ZXh0KTsKICAhc3RhdGUgPyAgZmFsc2UgPyAwIDogcm91dGVyX2ludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgcmV0dXJuIHN0YXRlOwp9Ci8qKgogKiBSZXR1cm5zIHRoZSBjdXJyZW50IG5hdmlnYXRpb24sIGRlZmF1bHRpbmcgdG8gYW4gImlkbGUiIG5hdmlnYXRpb24gd2hlbgogKiBubyBuYXZpZ2F0aW9uIGlzIGluIHByb2dyZXNzCiAqLwoKCmZ1bmN0aW9uIGRpc3RfdXNlTmF2aWdhdGlvbigpIHsKICBsZXQgc3RhdGUgPSB1c2VEYXRhUm91dGVyU3RhdGUoRGF0YVJvdXRlclN0YXRlSG9vay5Vc2VOYXZpZ2F0aW9uKTsKICByZXR1cm4gc3RhdGUubmF2aWdhdGlvbjsKfQovKioKICogUmV0dXJucyBhIHJldmFsaWRhdGUgZnVuY3Rpb24gZm9yIG1hbnVhbGx5IHRyaWdnZXJpbmcgcmV2YWxpZGF0aW9uLCBhcyB3ZWxsCiAqIGFzIHRoZSBjdXJyZW50IHN0YXRlIG9mIGFueSBtYW51YWwgcmV2YWxpZGF0aW9ucwogKi8KCmZ1bmN0aW9uIHVzZVJldmFsaWRhdG9yKCkgewogIGxldCBkYXRhUm91dGVyQ29udGV4dCA9IHVzZURhdGFSb3V0ZXJDb250ZXh0KERhdGFSb3V0ZXJIb29rLlVzZVJldmFsaWRhdG9yKTsKICBsZXQgc3RhdGUgPSB1c2VEYXRhUm91dGVyU3RhdGUoRGF0YVJvdXRlclN0YXRlSG9vay5Vc2VSZXZhbGlkYXRvcik7CiAgcmV0dXJuIHsKICAgIHJldmFsaWRhdGU6IGRhdGFSb3V0ZXJDb250ZXh0LnJvdXRlci5yZXZhbGlkYXRlLAogICAgc3RhdGU6IHN0YXRlLnJldmFsaWRhdGlvbgogIH07Cn0KLyoqCiAqIFJldHVybnMgdGhlIGFjdGl2ZSByb3V0ZSBtYXRjaGVzLCB1c2VmdWwgZm9yIGFjY2Vzc2luZyBsb2FkZXJEYXRhIGZvcgogKiBwYXJlbnQvY2hpbGQgcm91dGVzIG9yIHRoZSByb3V0ZSAiaGFuZGxlIiBwcm9wZXJ0eQogKi8KCmZ1bmN0aW9uIGRpc3RfdXNlTWF0Y2hlcygpIHsKICBsZXQgewogICAgbWF0Y2hlcywKICAgIGxvYWRlckRhdGEKICB9ID0gdXNlRGF0YVJvdXRlclN0YXRlKERhdGFSb3V0ZXJTdGF0ZUhvb2suVXNlTWF0Y2hlcyk7CiAgcmV0dXJuIFJlYWN0LnVzZU1lbW8oKCkgPT4gbWF0Y2hlcy5tYXAobWF0Y2ggPT4gewogICAgbGV0IHsKICAgICAgcGF0aG5hbWUsCiAgICAgIHBhcmFtcwogICAgfSA9IG1hdGNoOyAvLyBOb3RlOiBUaGlzIHN0cnVjdHVyZSBtYXRjaGVzIHRoYXQgY3JlYXRlZCBieSBjcmVhdGVVc2VNYXRjaGVzTWF0Y2gKICAgIC8vIGluIHRoZSBAcmVtaXgtcnVuL3JvdXRlciAsIHNvIGlmIHlvdSBjaGFuZ2UgdGhpcyBwbGVhc2UgYWxzbyBjaGFuZ2UKICAgIC8vIHRoYXQgOikgIEV2ZW50dWFsbHkgd2UnbGwgRFJZIHRoaXMgdXAKCiAgICByZXR1cm4gewogICAgICBpZDogbWF0Y2gucm91dGUuaWQsCiAgICAgIHBhdGhuYW1lLAogICAgICBwYXJhbXMsCiAgICAgIGRhdGE6IGxvYWRlckRhdGFbbWF0Y2gucm91dGUuaWRdLAogICAgICBoYW5kbGU6IG1hdGNoLnJvdXRlLmhhbmRsZQogICAgfTsKICB9KSwgW21hdGNoZXMsIGxvYWRlckRhdGFdKTsKfQovKioKICogUmV0dXJucyB0aGUgbG9hZGVyIGRhdGEgZm9yIHRoZSBuZWFyZXN0IGFuY2VzdG9yIFJvdXRlIGxvYWRlcgogKi8KCmZ1bmN0aW9uIHVzZUxvYWRlckRhdGEoKSB7CiAgbGV0IHN0YXRlID0gdXNlRGF0YVJvdXRlclN0YXRlKERhdGFSb3V0ZXJTdGF0ZUhvb2suVXNlTG9hZGVyRGF0YSk7CiAgbGV0IHJvdXRlID0gUmVhY3QudXNlQ29udGV4dChSb3V0ZUNvbnRleHQpOwogICFyb3V0ZSA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIGxldCB0aGlzUm91dGUgPSByb3V0ZS5tYXRjaGVzW3JvdXRlLm1hdGNoZXMubGVuZ3RoIC0gMV07CiAgIXRoaXNSb3V0ZS5yb3V0ZS5pZCA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIHJldHVybiBzdGF0ZS5sb2FkZXJEYXRhW3RoaXNSb3V0ZS5yb3V0ZS5pZF07Cn0KLyoqCiAqIFJldHVybnMgdGhlIGxvYWRlckRhdGEgZm9yIHRoZSBnaXZlbiByb3V0ZUlkCiAqLwoKZnVuY3Rpb24gdXNlUm91dGVMb2FkZXJEYXRhKHJvdXRlSWQpIHsKICBsZXQgc3RhdGUgPSB1c2VEYXRhUm91dGVyU3RhdGUoRGF0YVJvdXRlclN0YXRlSG9vay5Vc2VSb3V0ZUxvYWRlckRhdGEpOwogIHJldHVybiBzdGF0ZS5sb2FkZXJEYXRhW3JvdXRlSWRdOwp9Ci8qKgogKiBSZXR1cm5zIHRoZSBhY3Rpb24gZGF0YSBmb3IgdGhlIG5lYXJlc3QgYW5jZXN0b3IgUm91dGUgYWN0aW9uCiAqLwoKZnVuY3Rpb24gdXNlQWN0aW9uRGF0YSgpIHsKICBsZXQgc3RhdGUgPSB1c2VEYXRhUm91dGVyU3RhdGUoRGF0YVJvdXRlclN0YXRlSG9vay5Vc2VBY3Rpb25EYXRhKTsKICBsZXQgcm91dGUgPSBSZWFjdC51c2VDb250ZXh0KFJvdXRlQ29udGV4dCk7CiAgIXJvdXRlID8gIGZhbHNlID8gMCA6IGludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgcmV0dXJuIE9iamVjdC52YWx1ZXMoKHN0YXRlID09IG51bGwgPyB2b2lkIDAgOiBzdGF0ZS5hY3Rpb25EYXRhKSB8fCB7fSlbMF07Cn0KLyoqCiAqIFJldHVybnMgdGhlIG5lYXJlc3QgYW5jZXN0b3IgUm91dGUgZXJyb3IsIHdoaWNoIGNvdWxkIGJlIGEgbG9hZGVyL2FjdGlvbgogKiBlcnJvciBvciBhIHJlbmRlciBlcnJvci4gIFRoaXMgaXMgaW50ZW5kZWQgdG8gYmUgY2FsbGVkIGZyb20geW91cgogKiBlcnJvckVsZW1lbnQgdG8gZGlzcGxheSBhIHByb3BlciBlcnJvciBtZXNzYWdlLgogKi8KCmZ1bmN0aW9uIHVzZVJvdXRlRXJyb3IoKSB7CiAgdmFyIF9zdGF0ZSRlcnJvcnM7CgogIGxldCBlcnJvciA9IHJlYWN0LnVzZUNvbnRleHQoUm91dGVFcnJvckNvbnRleHQpOwogIGxldCBzdGF0ZSA9IHVzZURhdGFSb3V0ZXJTdGF0ZShEYXRhUm91dGVyU3RhdGVIb29rLlVzZVJvdXRlRXJyb3IpOwogIGxldCByb3V0ZSA9IHJlYWN0LnVzZUNvbnRleHQoUm91dGVDb250ZXh0KTsKICBsZXQgdGhpc1JvdXRlID0gcm91dGUubWF0Y2hlc1tyb3V0ZS5tYXRjaGVzLmxlbmd0aCAtIDFdOyAvLyBJZiB0aGlzIHdhcyBhIHJlbmRlciBlcnJvciwgd2UgcHV0IGl0IGluIGEgUm91dGVFcnJvciBjb250ZXh0IGluc2lkZQogIC8vIG9mIFJlbmRlckVycm9yQm91bmRhcnkKCiAgaWYgKGVycm9yKSB7CiAgICByZXR1cm4gZXJyb3I7CiAgfQoKICAhcm91dGUgPyAgZmFsc2UgPyAwIDogcm91dGVyX2ludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgIXRoaXNSb3V0ZS5yb3V0ZS5pZCA/ICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsgLy8gT3RoZXJ3aXNlIGxvb2sgZm9yIGVycm9ycyBmcm9tIG91ciBkYXRhIHJvdXRlciBzdGF0ZQoKICByZXR1cm4gKF9zdGF0ZSRlcnJvcnMgPSBzdGF0ZS5lcnJvcnMpID09IG51bGwgPyB2b2lkIDAgOiBfc3RhdGUkZXJyb3JzW3RoaXNSb3V0ZS5yb3V0ZS5pZF07Cn0KLyoqCiAqIFJldHVybnMgdGhlIGhhcHB5LXBhdGggZGF0YSBmcm9tIHRoZSBuZWFyZXN0IGFuY2VzdG9yIDxBd2FpdCAvPiB2YWx1ZQogKi8KCmZ1bmN0aW9uIHVzZUFzeW5jVmFsdWUoKSB7CiAgbGV0IHZhbHVlID0gUmVhY3QudXNlQ29udGV4dChBd2FpdENvbnRleHQpOwogIHJldHVybiB2YWx1ZSA9PSBudWxsID8gdm9pZCAwIDogdmFsdWUuX2RhdGE7Cn0KLyoqCiAqIFJldHVybnMgdGhlIGVycm9yIGZyb20gdGhlIG5lYXJlc3QgYW5jZXN0b3IgPEF3YWl0IC8+IHZhbHVlCiAqLwoKZnVuY3Rpb24gdXNlQXN5bmNFcnJvcigpIHsKICBsZXQgdmFsdWUgPSBSZWFjdC51c2VDb250ZXh0KEF3YWl0Q29udGV4dCk7CiAgcmV0dXJuIHZhbHVlID09IG51bGwgPyB2b2lkIDAgOiB2YWx1ZS5fZXJyb3I7Cn0KY29uc3QgYWxyZWFkeVdhcm5lZCA9IHt9OwoKZnVuY3Rpb24gd2FybmluZ09uY2Uoa2V5LCBjb25kLCBtZXNzYWdlKSB7CiAgaWYgKCFjb25kICYmICFhbHJlYWR5V2FybmVkW2tleV0pIHsKICAgIGFscmVhZHlXYXJuZWRba2V5XSA9IHRydWU7CiAgICAgZmFsc2UgPyAwIDogdm9pZCAwOwogIH0KfQoKLyoqCiAqIEdpdmVuIGEgUmVtaXggUm91dGVyIGluc3RhbmNlLCByZW5kZXIgdGhlIGFwcHJvcHJpYXRlIFVJCiAqLwpmdW5jdGlvbiBSb3V0ZXJQcm92aWRlcihfcmVmKSB7CiAgbGV0IHsKICAgIGZhbGxiYWNrRWxlbWVudCwKICAgIHJvdXRlcgogIH0gPSBfcmVmOwogIC8vIFN5bmMgcm91dGVyIHN0YXRlIHRvIG91ciBjb21wb25lbnQgc3RhdGUgdG8gZm9yY2UgcmUtcmVuZGVycwogIGxldCBzdGF0ZSA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKHJvdXRlci5zdWJzY3JpYmUsICgpID0+IHJvdXRlci5zdGF0ZSwgLy8gV2UgaGF2ZSB0byBwcm92aWRlIHRoaXMgc28gUmVhY3RAMTggZG9lc24ndCBjb21wbGFpbiBkdXJpbmcgaHlkcmF0aW9uLAogIC8vIGJ1dCB3ZSBwYXNzIG91ciBzZXJpYWxpemVkIGh5ZHJhdGlvbiBkYXRhIGludG8gdGhlIHJvdXRlciBzbyBzdGF0ZSBoZXJlCiAgLy8gaXMgYWxyZWFkeSBzeW5jZWQgd2l0aCB3aGF0IHRoZSBzZXJ2ZXIgc2F3CiAgKCkgPT4gcm91dGVyLnN0YXRlKTsKICBsZXQgbmF2aWdhdG9yID0gUmVhY3QudXNlTWVtbygoKSA9PiB7CiAgICByZXR1cm4gewogICAgICBjcmVhdGVIcmVmOiByb3V0ZXIuY3JlYXRlSHJlZiwKICAgICAgZ286IG4gPT4gcm91dGVyLm5hdmlnYXRlKG4pLAogICAgICBwdXNoOiAodG8sIHN0YXRlLCBvcHRzKSA9PiByb3V0ZXIubmF2aWdhdGUodG8sIHsKICAgICAgICBzdGF0ZSwKICAgICAgICBwcmV2ZW50U2Nyb2xsUmVzZXQ6IG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMucHJldmVudFNjcm9sbFJlc2V0CiAgICAgIH0pLAogICAgICByZXBsYWNlOiAodG8sIHN0YXRlLCBvcHRzKSA9PiByb3V0ZXIubmF2aWdhdGUodG8sIHsKICAgICAgICByZXBsYWNlOiB0cnVlLAogICAgICAgIHN0YXRlLAogICAgICAgIHByZXZlbnRTY3JvbGxSZXNldDogb3B0cyA9PSBudWxsID8gdm9pZCAwIDogb3B0cy5wcmV2ZW50U2Nyb2xsUmVzZXQKICAgICAgfSkKICAgIH07CiAgfSwgW3JvdXRlcl0pOwogIGxldCBiYXNlbmFtZSA9IHJvdXRlci5iYXNlbmFtZSB8fCAiLyI7CiAgcmV0dXJuIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KERhdGFSb3V0ZXJDb250ZXh0LlByb3ZpZGVyLCB7CiAgICB2YWx1ZTogewogICAgICByb3V0ZXIsCiAgICAgIG5hdmlnYXRvciwKICAgICAgc3RhdGljOiBmYWxzZSwKICAgICAgLy8gRG8gd2UgbmVlZCB0aGlzPwogICAgICBiYXNlbmFtZQogICAgfQogIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KERhdGFSb3V0ZXJTdGF0ZUNvbnRleHQuUHJvdmlkZXIsIHsKICAgIHZhbHVlOiBzdGF0ZQogIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KGRpc3RfUm91dGVyLCB7CiAgICBiYXNlbmFtZTogcm91dGVyLmJhc2VuYW1lLAogICAgbG9jYXRpb246IHJvdXRlci5zdGF0ZS5sb2NhdGlvbiwKICAgIG5hdmlnYXRpb25UeXBlOiByb3V0ZXIuc3RhdGUuaGlzdG9yeUFjdGlvbiwKICAgIG5hdmlnYXRvcjogbmF2aWdhdG9yCiAgfSwgcm91dGVyLnN0YXRlLmluaXRpYWxpemVkID8gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoUm91dGVzLCBudWxsKSA6IGZhbGxiYWNrRWxlbWVudCkpKTsKfQoKLyoqCiAqIEEgPFJvdXRlcj4gdGhhdCBzdG9yZXMgYWxsIGVudHJpZXMgaW4gbWVtb3J5LgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvcm91dGVycy9tZW1vcnktcm91dGVyCiAqLwpmdW5jdGlvbiBNZW1vcnlSb3V0ZXIoX3JlZjIpIHsKICBsZXQgewogICAgYmFzZW5hbWUsCiAgICBjaGlsZHJlbiwKICAgIGluaXRpYWxFbnRyaWVzLAogICAgaW5pdGlhbEluZGV4CiAgfSA9IF9yZWYyOwogIGxldCBoaXN0b3J5UmVmID0gcmVhY3QudXNlUmVmKCk7CgogIGlmIChoaXN0b3J5UmVmLmN1cnJlbnQgPT0gbnVsbCkgewogICAgaGlzdG9yeVJlZi5jdXJyZW50ID0gcm91dGVyX2NyZWF0ZU1lbW9yeUhpc3RvcnkoewogICAgICBpbml0aWFsRW50cmllcywKICAgICAgaW5pdGlhbEluZGV4LAogICAgICB2NUNvbXBhdDogdHJ1ZQogICAgfSk7CiAgfQoKICBsZXQgaGlzdG9yeSA9IGhpc3RvcnlSZWYuY3VycmVudDsKICBsZXQgW3N0YXRlLCBzZXRTdGF0ZV0gPSByZWFjdC51c2VTdGF0ZSh7CiAgICBhY3Rpb246IGhpc3RvcnkuYWN0aW9uLAogICAgbG9jYXRpb246IGhpc3RvcnkubG9jYXRpb24KICB9KTsKICByZWFjdC51c2VMYXlvdXRFZmZlY3QoKCkgPT4gaGlzdG9yeS5saXN0ZW4oc2V0U3RhdGUpLCBbaGlzdG9yeV0pOwogIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChkaXN0X1JvdXRlciwgewogICAgYmFzZW5hbWU6IGJhc2VuYW1lLAogICAgY2hpbGRyZW46IGNoaWxkcmVuLAogICAgbG9jYXRpb246IHN0YXRlLmxvY2F0aW9uLAogICAgbmF2aWdhdGlvblR5cGU6IHN0YXRlLmFjdGlvbiwKICAgIG5hdmlnYXRvcjogaGlzdG9yeQogIH0pOwp9CgovKioKICogQ2hhbmdlcyB0aGUgY3VycmVudCBsb2NhdGlvbi4KICoKICogTm90ZTogVGhpcyBBUEkgaXMgbW9zdGx5IHVzZWZ1bCBpbiBSZWFjdC5Db21wb25lbnQgc3ViY2xhc3NlcyB0aGF0IGFyZSBub3QKICogYWJsZSB0byB1c2UgaG9va3MuIEluIGZ1bmN0aW9uYWwgY29tcG9uZW50cywgd2UgcmVjb21tZW5kIHlvdSB1c2UgdGhlCiAqIGB1c2VOYXZpZ2F0ZWAgaG9vayBpbnN0ZWFkLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvY29tcG9uZW50cy9uYXZpZ2F0ZQogKi8KZnVuY3Rpb24gTmF2aWdhdGUoX3JlZjMpIHsKICBsZXQgewogICAgdG8sCiAgICByZXBsYWNlLAogICAgc3RhdGUsCiAgICByZWxhdGl2ZQogIH0gPSBfcmVmMzsKICAhdXNlSW5Sb3V0ZXJDb250ZXh0KCkgPyAgZmFsc2UgPyAwIDogcm91dGVyX2ludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgIGZhbHNlID8gMCA6IHZvaWQgMDsKICBsZXQgZGF0YVJvdXRlclN0YXRlID0gcmVhY3QudXNlQ29udGV4dChEYXRhUm91dGVyU3RhdGVDb250ZXh0KTsKICBsZXQgbmF2aWdhdGUgPSBkaXN0X3VzZU5hdmlnYXRlKCk7CiAgcmVhY3QudXNlRWZmZWN0KCgpID0+IHsKICAgIC8vIEF2b2lkIGtpY2tpbmcgb2ZmIG11bHRpcGxlIG5hdmlnYXRpb25zIGlmIHdlJ3JlIGluIHRoZSBtaWRkbGUgb2YgYQogICAgLy8gZGF0YS1yb3V0ZXIgbmF2aWdhdGlvbiwgc2luY2UgY29tcG9uZW50cyBnZXQgcmUtcmVuZGVyZWQgd2hlbiB3ZSBlbnRlcgogICAgLy8gYSBzdWJtaXR0aW5nL2xvYWRpbmcgc3RhdGUKICAgIGlmIChkYXRhUm91dGVyU3RhdGUgJiYgZGF0YVJvdXRlclN0YXRlLm5hdmlnYXRpb24uc3RhdGUgIT09ICJpZGxlIikgewogICAgICByZXR1cm47CiAgICB9CgogICAgbmF2aWdhdGUodG8sIHsKICAgICAgcmVwbGFjZSwKICAgICAgc3RhdGUsCiAgICAgIHJlbGF0aXZlCiAgICB9KTsKICB9KTsKICByZXR1cm4gbnVsbDsKfQoKLyoqCiAqIFJlbmRlcnMgdGhlIGNoaWxkIHJvdXRlJ3MgZWxlbWVudCwgaWYgdGhlcmUgaXMgb25lLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvY29tcG9uZW50cy9vdXRsZXQKICovCmZ1bmN0aW9uIE91dGxldChwcm9wcykgewogIHJldHVybiB1c2VPdXRsZXQocHJvcHMuY29udGV4dCk7Cn0KCi8qKgogKiBEZWNsYXJlcyBhbiBlbGVtZW50IHRoYXQgc2hvdWxkIGJlIHJlbmRlcmVkIGF0IGEgY2VydGFpbiBVUkwgcGF0aC4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L2NvbXBvbmVudHMvcm91dGUKICovCmZ1bmN0aW9uIFJvdXRlKF9wcm9wcykgewogICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA7Cn0KCi8qKgogKiBQcm92aWRlcyBsb2NhdGlvbiBjb250ZXh0IGZvciB0aGUgcmVzdCBvZiB0aGUgYXBwLgogKgogKiBOb3RlOiBZb3UgdXN1YWxseSB3b24ndCByZW5kZXIgYSA8Um91dGVyPiBkaXJlY3RseS4gSW5zdGVhZCwgeW91J2xsIHJlbmRlciBhCiAqIHJvdXRlciB0aGF0IGlzIG1vcmUgc3BlY2lmaWMgdG8geW91ciBlbnZpcm9ubWVudCBzdWNoIGFzIGEgPEJyb3dzZXJSb3V0ZXI+CiAqIGluIHdlYiBicm93c2VycyBvciBhIDxTdGF0aWNSb3V0ZXI+IGZvciBzZXJ2ZXIgcmVuZGVyaW5nLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvcm91dGVycy9yb3V0ZXIKICovCmZ1bmN0aW9uIGRpc3RfUm91dGVyKF9yZWY0KSB7CiAgbGV0IHsKICAgIGJhc2VuYW1lOiBiYXNlbmFtZVByb3AgPSAiLyIsCiAgICBjaGlsZHJlbiA9IG51bGwsCiAgICBsb2NhdGlvbjogbG9jYXRpb25Qcm9wLAogICAgbmF2aWdhdGlvblR5cGUgPSBBY3Rpb24uUG9wLAogICAgbmF2aWdhdG9yLAogICAgc3RhdGljOiBzdGF0aWNQcm9wID0gZmFsc2UKICB9ID0gX3JlZjQ7CiAgISF1c2VJblJvdXRlckNvbnRleHQoKSA/ICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsgLy8gUHJlc2VydmUgdHJhaWxpbmcgc2xhc2hlcyBvbiBiYXNlbmFtZSwgc28gd2UgY2FuIGxldCB0aGUgdXNlciBjb250cm9sCiAgLy8gdGhlIGVuZm9yY2VtZW50IG9mIHRyYWlsaW5nIHNsYXNoZXMgdGhyb3VnaG91dCB0aGUgYXBwCgogIGxldCBiYXNlbmFtZSA9IGJhc2VuYW1lUHJvcC5yZXBsYWNlKC9eXC8qLywgIi8iKTsKICBsZXQgbmF2aWdhdGlvbkNvbnRleHQgPSByZWFjdC51c2VNZW1vKCgpID0+ICh7CiAgICBiYXNlbmFtZSwKICAgIG5hdmlnYXRvciwKICAgIHN0YXRpYzogc3RhdGljUHJvcAogIH0pLCBbYmFzZW5hbWUsIG5hdmlnYXRvciwgc3RhdGljUHJvcF0pOwoKICBpZiAodHlwZW9mIGxvY2F0aW9uUHJvcCA9PT0gInN0cmluZyIpIHsKICAgIGxvY2F0aW9uUHJvcCA9IHBhcnNlUGF0aChsb2NhdGlvblByb3ApOwogIH0KCiAgbGV0IHsKICAgIHBhdGhuYW1lID0gIi8iLAogICAgc2VhcmNoID0gIiIsCiAgICBoYXNoID0gIiIsCiAgICBzdGF0ZSA9IG51bGwsCiAgICBrZXkgPSAiZGVmYXVsdCIKICB9ID0gbG9jYXRpb25Qcm9wOwogIGxldCBsb2NhdGlvbiA9IHJlYWN0LnVzZU1lbW8oKCkgPT4gewogICAgbGV0IHRyYWlsaW5nUGF0aG5hbWUgPSBzdHJpcEJhc2VuYW1lKHBhdGhuYW1lLCBiYXNlbmFtZSk7CgogICAgaWYgKHRyYWlsaW5nUGF0aG5hbWUgPT0gbnVsbCkgewogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZXR1cm4gewogICAgICBwYXRobmFtZTogdHJhaWxpbmdQYXRobmFtZSwKICAgICAgc2VhcmNoLAogICAgICBoYXNoLAogICAgICBzdGF0ZSwKICAgICAga2V5CiAgICB9OwogIH0sIFtiYXNlbmFtZSwgcGF0aG5hbWUsIHNlYXJjaCwgaGFzaCwgc3RhdGUsIGtleV0pOwogICBmYWxzZSA/IDAgOiB2b2lkIDA7CgogIGlmIChsb2NhdGlvbiA9PSBudWxsKSB7CiAgICByZXR1cm4gbnVsbDsKICB9CgogIHJldHVybiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChOYXZpZ2F0aW9uQ29udGV4dC5Qcm92aWRlciwgewogICAgdmFsdWU6IG5hdmlnYXRpb25Db250ZXh0CiAgfSwgLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoTG9jYXRpb25Db250ZXh0LlByb3ZpZGVyLCB7CiAgICBjaGlsZHJlbjogY2hpbGRyZW4sCiAgICB2YWx1ZTogewogICAgICBsb2NhdGlvbiwKICAgICAgbmF2aWdhdGlvblR5cGUKICAgIH0KICB9KSk7Cn0KCi8qKgogKiBBIGNvbnRhaW5lciBmb3IgYSBuZXN0ZWQgdHJlZSBvZiA8Um91dGU+IGVsZW1lbnRzIHRoYXQgcmVuZGVycyB0aGUgYnJhbmNoCiAqIHRoYXQgYmVzdCBtYXRjaGVzIHRoZSBjdXJyZW50IGxvY2F0aW9uLgogKgogKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2RvY3MvZW4vdjYvY29tcG9uZW50cy9yb3V0ZXMKICovCmZ1bmN0aW9uIFJvdXRlcyhfcmVmNSkgewogIGxldCB7CiAgICBjaGlsZHJlbiwKICAgIGxvY2F0aW9uCiAgfSA9IF9yZWY1OwogIGxldCBkYXRhUm91dGVyQ29udGV4dCA9IHJlYWN0LnVzZUNvbnRleHQoRGF0YVJvdXRlckNvbnRleHQpOyAvLyBXaGVuIGluIGEgRGF0YVJvdXRlckNvbnRleHQgX3dpdGhvdXRfIGNoaWxkcmVuLCB3ZSB1c2UgdGhlIHJvdXRlciByb3V0ZXMKICAvLyBkaXJlY3RseS4gIElmIHdlIGhhdmUgY2hpbGRyZW4sIHRoZW4gd2UncmUgaW4gYSBkZXNjZW5kYW50IHRyZWUgYW5kIHdlCiAgLy8gbmVlZCB0byB1c2UgY2hpbGQgcm91dGVzLgoKICBsZXQgcm91dGVzID0gZGF0YVJvdXRlckNvbnRleHQgJiYgIWNoaWxkcmVuID8gZGF0YVJvdXRlckNvbnRleHQucm91dGVyLnJvdXRlcyA6IGNyZWF0ZVJvdXRlc0Zyb21DaGlsZHJlbihjaGlsZHJlbik7CiAgcmV0dXJuIHVzZVJvdXRlcyhyb3V0ZXMsIGxvY2F0aW9uKTsKfQoKLyoqCiAqIENvbXBvbmVudCB0byB1c2UgZm9yIHJlbmRlcmluZyBsYXppbHkgbG9hZGVkIGRhdGEgZnJvbSByZXR1cm5pbmcgZGVmZXIoKQogKiBpbiBhIGxvYWRlciBmdW5jdGlvbgogKi8KZnVuY3Rpb24gQXdhaXQoX3JlZjYpIHsKICBsZXQgewogICAgY2hpbGRyZW4sCiAgICBlcnJvckVsZW1lbnQsCiAgICByZXNvbHZlCiAgfSA9IF9yZWY2OwogIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChBd2FpdEVycm9yQm91bmRhcnksIHsKICAgIHJlc29sdmU6IHJlc29sdmUsCiAgICBlcnJvckVsZW1lbnQ6IGVycm9yRWxlbWVudAogIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFJlc29sdmVBd2FpdCwgbnVsbCwgY2hpbGRyZW4pKTsKfQp2YXIgQXdhaXRSZW5kZXJTdGF0dXM7CgooZnVuY3Rpb24gKEF3YWl0UmVuZGVyU3RhdHVzKSB7CiAgQXdhaXRSZW5kZXJTdGF0dXNbQXdhaXRSZW5kZXJTdGF0dXNbInBlbmRpbmciXSA9IDBdID0gInBlbmRpbmciOwogIEF3YWl0UmVuZGVyU3RhdHVzW0F3YWl0UmVuZGVyU3RhdHVzWyJzdWNjZXNzIl0gPSAxXSA9ICJzdWNjZXNzIjsKICBBd2FpdFJlbmRlclN0YXR1c1tBd2FpdFJlbmRlclN0YXR1c1siZXJyb3IiXSA9IDJdID0gImVycm9yIjsKfSkoQXdhaXRSZW5kZXJTdGF0dXMgfHwgKEF3YWl0UmVuZGVyU3RhdHVzID0ge30pKTsKCmNvbnN0IG5ldmVyU2V0dGxlZFByb21pc2UgPSBuZXcgUHJvbWlzZSgoKSA9PiB7fSk7CgpjbGFzcyBBd2FpdEVycm9yQm91bmRhcnkgZXh0ZW5kcyByZWFjdC5Db21wb25lbnQgewogIGNvbnN0cnVjdG9yKHByb3BzKSB7CiAgICBzdXBlcihwcm9wcyk7CiAgICB0aGlzLnN0YXRlID0gewogICAgICBlcnJvcjogbnVsbAogICAgfTsKICB9CgogIHN0YXRpYyBnZXREZXJpdmVkU3RhdGVGcm9tRXJyb3IoZXJyb3IpIHsKICAgIHJldHVybiB7CiAgICAgIGVycm9yCiAgICB9OwogIH0KCiAgY29tcG9uZW50RGlkQ2F0Y2goZXJyb3IsIGVycm9ySW5mbykgewogICAgY29uc29sZS5lcnJvcigiPEF3YWl0PiBjYXVnaHQgdGhlIGZvbGxvd2luZyBlcnJvciBkdXJpbmcgcmVuZGVyIiwgZXJyb3IsIGVycm9ySW5mbyk7CiAgfQoKICByZW5kZXIoKSB7CiAgICBsZXQgewogICAgICBjaGlsZHJlbiwKICAgICAgZXJyb3JFbGVtZW50LAogICAgICByZXNvbHZlCiAgICB9ID0gdGhpcy5wcm9wczsKICAgIGxldCBwcm9taXNlID0gbnVsbDsKICAgIGxldCBzdGF0dXMgPSBBd2FpdFJlbmRlclN0YXR1cy5wZW5kaW5nOwoKICAgIGlmICghKHJlc29sdmUgaW5zdGFuY2VvZiBQcm9taXNlKSkgewogICAgICAvLyBEaWRuJ3QgZ2V0IGEgcHJvbWlzZSAtIHByb3ZpZGUgYXMgYSByZXNvbHZlZCBwcm9taXNlCiAgICAgIHN0YXR1cyA9IEF3YWl0UmVuZGVyU3RhdHVzLnN1Y2Nlc3M7CiAgICAgIHByb21pc2UgPSBQcm9taXNlLnJlc29sdmUoKTsKICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsICJfdHJhY2tlZCIsIHsKICAgICAgICBnZXQ6ICgpID0+IHRydWUKICAgICAgfSk7CiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShwcm9taXNlLCAiX2RhdGEiLCB7CiAgICAgICAgZ2V0OiAoKSA9PiByZXNvbHZlCiAgICAgIH0pOwogICAgfSBlbHNlIGlmICh0aGlzLnN0YXRlLmVycm9yKSB7CiAgICAgIC8vIENhdWdodCBhIHJlbmRlciBlcnJvciwgcHJvdmlkZSBpdCBhcyBhIHJlamVjdGVkIHByb21pc2UKICAgICAgc3RhdHVzID0gQXdhaXRSZW5kZXJTdGF0dXMuZXJyb3I7CiAgICAgIGxldCByZW5kZXJFcnJvciA9IHRoaXMuc3RhdGUuZXJyb3I7CiAgICAgIHByb21pc2UgPSBQcm9taXNlLnJlamVjdCgpLmNhdGNoKCgpID0+IHt9KTsgLy8gQXZvaWQgdW5oYW5kbGVkIHJlamVjdGlvbiB3YXJuaW5ncwoKICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsICJfdHJhY2tlZCIsIHsKICAgICAgICBnZXQ6ICgpID0+IHRydWUKICAgICAgfSk7CiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShwcm9taXNlLCAiX2Vycm9yIiwgewogICAgICAgIGdldDogKCkgPT4gcmVuZGVyRXJyb3IKICAgICAgfSk7CiAgICB9IGVsc2UgaWYgKHJlc29sdmUuX3RyYWNrZWQpIHsKICAgICAgLy8gQWxyZWFkeSB0cmFja2VkIHByb21pc2UgLSBjaGVjayBjb250ZW50cwogICAgICBwcm9taXNlID0gcmVzb2x2ZTsKICAgICAgc3RhdHVzID0gcHJvbWlzZS5fZXJyb3IgIT09IHVuZGVmaW5lZCA/IEF3YWl0UmVuZGVyU3RhdHVzLmVycm9yIDogcHJvbWlzZS5fZGF0YSAhPT0gdW5kZWZpbmVkID8gQXdhaXRSZW5kZXJTdGF0dXMuc3VjY2VzcyA6IEF3YWl0UmVuZGVyU3RhdHVzLnBlbmRpbmc7CiAgICB9IGVsc2UgewogICAgICAvLyBSYXcgKHVudHJhY2tlZCkgcHJvbWlzZSAtIHRyYWNrIGl0CiAgICAgIHN0YXR1cyA9IEF3YWl0UmVuZGVyU3RhdHVzLnBlbmRpbmc7CiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXNvbHZlLCAiX3RyYWNrZWQiLCB7CiAgICAgICAgZ2V0OiAoKSA9PiB0cnVlCiAgICAgIH0pOwogICAgICBwcm9taXNlID0gcmVzb2x2ZS50aGVuKGRhdGEgPT4gT2JqZWN0LmRlZmluZVByb3BlcnR5KHJlc29sdmUsICJfZGF0YSIsIHsKICAgICAgICBnZXQ6ICgpID0+IGRhdGEKICAgICAgfSksIGVycm9yID0+IE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXNvbHZlLCAiX2Vycm9yIiwgewogICAgICAgIGdldDogKCkgPT4gZXJyb3IKICAgICAgfSkpOwogICAgfQoKICAgIGlmIChzdGF0dXMgPT09IEF3YWl0UmVuZGVyU3RhdHVzLmVycm9yICYmIHByb21pc2UuX2Vycm9yIGluc3RhbmNlb2YgQWJvcnRlZERlZmVycmVkRXJyb3IpIHsKICAgICAgLy8gRnJlZXplIHRoZSBVSSBieSB0aHJvd2luZyBhIG5ldmVyIHJlc29sdmVkIHByb21pc2UKICAgICAgdGhyb3cgbmV2ZXJTZXR0bGVkUHJvbWlzZTsKICAgIH0KCiAgICBpZiAoc3RhdHVzID09PSBBd2FpdFJlbmRlclN0YXR1cy5lcnJvciAmJiAhZXJyb3JFbGVtZW50KSB7CiAgICAgIC8vIE5vIGVycm9yRWxlbWVudCwgdGhyb3cgdG8gdGhlIG5lYXJlc3Qgcm91dGUtbGV2ZWwgZXJyb3IgYm91bmRhcnkKICAgICAgdGhyb3cgcHJvbWlzZS5fZXJyb3I7CiAgICB9CgogICAgaWYgKHN0YXR1cyA9PT0gQXdhaXRSZW5kZXJTdGF0dXMuZXJyb3IpIHsKICAgICAgLy8gUmVuZGVyIHZpYSBvdXIgZXJyb3JFbGVtZW50CiAgICAgIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChBd2FpdENvbnRleHQuUHJvdmlkZXIsIHsKICAgICAgICB2YWx1ZTogcHJvbWlzZSwKICAgICAgICBjaGlsZHJlbjogZXJyb3JFbGVtZW50CiAgICAgIH0pOwogICAgfQoKICAgIGlmIChzdGF0dXMgPT09IEF3YWl0UmVuZGVyU3RhdHVzLnN1Y2Nlc3MpIHsKICAgICAgLy8gUmVuZGVyIGNoaWxkcmVuIHdpdGggcmVzb2x2ZWQgdmFsdWUKICAgICAgcmV0dXJuIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KEF3YWl0Q29udGV4dC5Qcm92aWRlciwgewogICAgICAgIHZhbHVlOiBwcm9taXNlLAogICAgICAgIGNoaWxkcmVuOiBjaGlsZHJlbgogICAgICB9KTsKICAgIH0gLy8gVGhyb3cgdG8gdGhlIHN1c3BlbnNlIGJvdW5kYXJ5CgoKICAgIHRocm93IHByb21pc2U7CiAgfQoKfQovKioKICogQHByaXZhdGUKICogSW5kaXJlY3Rpb24gdG8gbGV2ZXJhZ2UgdXNlQXN5bmNWYWx1ZSBmb3IgYSByZW5kZXItcHJvcCBBUEkgb24gPEF3YWl0PgogKi8KCgpmdW5jdGlvbiBSZXNvbHZlQXdhaXQoX3JlZjcpIHsKICBsZXQgewogICAgY2hpbGRyZW4KICB9ID0gX3JlZjc7CiAgbGV0IGRhdGEgPSB1c2VBc3luY1ZhbHVlKCk7CgogIGlmICh0eXBlb2YgY2hpbGRyZW4gPT09ICJmdW5jdGlvbiIpIHsKICAgIHJldHVybiBjaGlsZHJlbihkYXRhKTsKICB9CgogIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChSZWFjdC5GcmFnbWVudCwgbnVsbCwgY2hpbGRyZW4pOwp9IC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KLy8gVVRJTFMKLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKLyoqCiAqIENyZWF0ZXMgYSByb3V0ZSBjb25maWcgZnJvbSBhIFJlYWN0ICJjaGlsZHJlbiIgb2JqZWN0LCB3aGljaCBpcyB1c3VhbGx5CiAqIGVpdGhlciBhIGA8Um91dGU+YCBlbGVtZW50IG9yIGFuIGFycmF5IG9mIHRoZW0uIFVzZWQgaW50ZXJuYWxseSBieQogKiBgPFJvdXRlcz5gIHRvIGNyZWF0ZSBhIHJvdXRlIGNvbmZpZyBmcm9tIGl0cyBjaGlsZHJlbi4KICoKICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS9kb2NzL2VuL3Y2L3V0aWxzL2NyZWF0ZS1yb3V0ZXMtZnJvbS1jaGlsZHJlbgogKi8KCgpmdW5jdGlvbiBjcmVhdGVSb3V0ZXNGcm9tQ2hpbGRyZW4oY2hpbGRyZW4sIHBhcmVudFBhdGgpIHsKICBpZiAocGFyZW50UGF0aCA9PT0gdm9pZCAwKSB7CiAgICBwYXJlbnRQYXRoID0gW107CiAgfQoKICBsZXQgcm91dGVzID0gW107CiAgcmVhY3QuQ2hpbGRyZW4uZm9yRWFjaChjaGlsZHJlbiwgKGVsZW1lbnQsIGluZGV4KSA9PiB7CiAgICBpZiAoISAvKiNfX1BVUkVfXyovcmVhY3QuaXNWYWxpZEVsZW1lbnQoZWxlbWVudCkpIHsKICAgICAgLy8gSWdub3JlIG5vbi1lbGVtZW50cy4gVGhpcyBhbGxvd3MgcGVvcGxlIHRvIG1vcmUgZWFzaWx5IGlubGluZQogICAgICAvLyBjb25kaXRpb25hbHMgaW4gdGhlaXIgcm91dGUgY29uZmlnLgogICAgICByZXR1cm47CiAgICB9CgogICAgaWYgKGVsZW1lbnQudHlwZSA9PT0gcmVhY3QuRnJhZ21lbnQpIHsKICAgICAgLy8gVHJhbnNwYXJlbnRseSBzdXBwb3J0IFJlYWN0LkZyYWdtZW50IGFuZCBpdHMgY2hpbGRyZW4uCiAgICAgIHJvdXRlcy5wdXNoLmFwcGx5KHJvdXRlcywgY3JlYXRlUm91dGVzRnJvbUNoaWxkcmVuKGVsZW1lbnQucHJvcHMuY2hpbGRyZW4sIHBhcmVudFBhdGgpKTsKICAgICAgcmV0dXJuOwogICAgfQoKICAgICEoZWxlbWVudC50eXBlID09PSBSb3V0ZSkgPyAgZmFsc2UgPyAwIDogcm91dGVyX2ludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgICAhKCFlbGVtZW50LnByb3BzLmluZGV4IHx8ICFlbGVtZW50LnByb3BzLmNoaWxkcmVuKSA/ICBmYWxzZSA/IDAgOiByb3V0ZXJfaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICAgIGxldCB0cmVlUGF0aCA9IFsuLi5wYXJlbnRQYXRoLCBpbmRleF07CiAgICBsZXQgcm91dGUgPSB7CiAgICAgIGlkOiBlbGVtZW50LnByb3BzLmlkIHx8IHRyZWVQYXRoLmpvaW4oIi0iKSwKICAgICAgY2FzZVNlbnNpdGl2ZTogZWxlbWVudC5wcm9wcy5jYXNlU2Vuc2l0aXZlLAogICAgICBlbGVtZW50OiBlbGVtZW50LnByb3BzLmVsZW1lbnQsCiAgICAgIGluZGV4OiBlbGVtZW50LnByb3BzLmluZGV4LAogICAgICBwYXRoOiBlbGVtZW50LnByb3BzLnBhdGgsCiAgICAgIGxvYWRlcjogZWxlbWVudC5wcm9wcy5sb2FkZXIsCiAgICAgIGFjdGlvbjogZWxlbWVudC5wcm9wcy5hY3Rpb24sCiAgICAgIGVycm9yRWxlbWVudDogZWxlbWVudC5wcm9wcy5lcnJvckVsZW1lbnQsCiAgICAgIGhhc0Vycm9yQm91bmRhcnk6IGVsZW1lbnQucHJvcHMuZXJyb3JFbGVtZW50ICE9IG51bGwsCiAgICAgIHNob3VsZFJldmFsaWRhdGU6IGVsZW1lbnQucHJvcHMuc2hvdWxkUmV2YWxpZGF0ZSwKICAgICAgaGFuZGxlOiBlbGVtZW50LnByb3BzLmhhbmRsZQogICAgfTsKCiAgICBpZiAoZWxlbWVudC5wcm9wcy5jaGlsZHJlbikgewogICAgICByb3V0ZS5jaGlsZHJlbiA9IGNyZWF0ZVJvdXRlc0Zyb21DaGlsZHJlbihlbGVtZW50LnByb3BzLmNoaWxkcmVuLCB0cmVlUGF0aCk7CiAgICB9CgogICAgcm91dGVzLnB1c2gocm91dGUpOwogIH0pOwogIHJldHVybiByb3V0ZXM7Cn0KLyoqCiAqIFJlbmRlcnMgdGhlIHJlc3VsdCBvZiBgbWF0Y2hSb3V0ZXMoKWAgaW50byBhIFJlYWN0IGVsZW1lbnQuCiAqLwoKZnVuY3Rpb24gcmVuZGVyTWF0Y2hlcyhtYXRjaGVzKSB7CiAgcmV0dXJuIF9yZW5kZXJNYXRjaGVzKG1hdGNoZXMpOwp9Ci8qKgogKiBAcHJpdmF0ZQogKiBXYWxrIHRoZSByb3V0ZSB0cmVlIGFuZCBhZGQgaGFzRXJyb3JCb3VuZGFyeSBpZiBpdCdzIG5vdCBwcm92aWRlZCwgc28gdGhhdAogKiB1c2VycyBwcm92aWRpbmcgbWFudWFsIHJvdXRlIGFycmF5cyBjYW4ganVzdCBzcGVjaWZ5IGVycm9yRWxlbWVudAogKi8KCmZ1bmN0aW9uIGVuaGFuY2VNYW51YWxSb3V0ZU9iamVjdHMocm91dGVzKSB7CiAgcmV0dXJuIHJvdXRlcy5tYXAocm91dGUgPT4gewogICAgbGV0IHJvdXRlQ2xvbmUgPSBkaXN0X2V4dGVuZHMoe30sIHJvdXRlKTsKCiAgICBpZiAocm91dGVDbG9uZS5oYXNFcnJvckJvdW5kYXJ5ID09IG51bGwpIHsKICAgICAgcm91dGVDbG9uZS5oYXNFcnJvckJvdW5kYXJ5ID0gcm91dGVDbG9uZS5lcnJvckVsZW1lbnQgIT0gbnVsbDsKICAgIH0KCiAgICBpZiAocm91dGVDbG9uZS5jaGlsZHJlbikgewogICAgICByb3V0ZUNsb25lLmNoaWxkcmVuID0gZW5oYW5jZU1hbnVhbFJvdXRlT2JqZWN0cyhyb3V0ZUNsb25lLmNoaWxkcmVuKTsKICAgIH0KCiAgICByZXR1cm4gcm91dGVDbG9uZTsKICB9KTsKfQoKZnVuY3Rpb24gY3JlYXRlTWVtb3J5Um91dGVyKHJvdXRlcywgb3B0cykgewogIHJldHVybiBjcmVhdGVSb3V0ZXIoewogICAgYmFzZW5hbWU6IG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMuYmFzZW5hbWUsCiAgICBoaXN0b3J5OiBjcmVhdGVNZW1vcnlIaXN0b3J5KHsKICAgICAgaW5pdGlhbEVudHJpZXM6IG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMuaW5pdGlhbEVudHJpZXMsCiAgICAgIGluaXRpYWxJbmRleDogb3B0cyA9PSBudWxsID8gdm9pZCAwIDogb3B0cy5pbml0aWFsSW5kZXgKICAgIH0pLAogICAgaHlkcmF0aW9uRGF0YTogb3B0cyA9PSBudWxsID8gdm9pZCAwIDogb3B0cy5oeWRyYXRpb25EYXRhLAogICAgcm91dGVzOiBlbmhhbmNlTWFudWFsUm91dGVPYmplY3RzKHJvdXRlcykKICB9KS5pbml0aWFsaXplKCk7Cn0gLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKCi8vIyBzb3VyY2VNYXBwaW5nVVJMPWluZGV4LmpzLm1hcAoKOy8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vbm9kZV9tb2R1bGVzL3JlYWN0LXJvdXRlci1kb20vZGlzdC9pbmRleC5qcwovKioKICogUmVhY3QgUm91dGVyIERPTSB2Ni40LjMKICoKICogQ29weXJpZ2h0IChjKSBSZW1peCBTb2Z0d2FyZSBJbmMuCiAqCiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZQogKiBMSUNFTlNFLm1kIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuCiAqCiAqIEBsaWNlbnNlIE1JVAogKi8KCgoKCgpmdW5jdGlvbiByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcygpIHsKICByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcyA9IE9iamVjdC5hc3NpZ24gPyBPYmplY3QuYXNzaWduLmJpbmQoKSA6IGZ1bmN0aW9uICh0YXJnZXQpIHsKICAgIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBzb3VyY2UgPSBhcmd1bWVudHNbaV07CgogICAgICBmb3IgKHZhciBrZXkgaW4gc291cmNlKSB7CiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzb3VyY2UsIGtleSkpIHsKICAgICAgICAgIHRhcmdldFtrZXldID0gc291cmNlW2tleV07CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgcmV0dXJuIHRhcmdldDsKICB9OwogIHJldHVybiByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcy5hcHBseSh0aGlzLCBhcmd1bWVudHMpOwp9CgpmdW5jdGlvbiBfb2JqZWN0V2l0aG91dFByb3BlcnRpZXNMb29zZShzb3VyY2UsIGV4Y2x1ZGVkKSB7CiAgaWYgKHNvdXJjZSA9PSBudWxsKSByZXR1cm4ge307CiAgdmFyIHRhcmdldCA9IHt9OwogIHZhciBzb3VyY2VLZXlzID0gT2JqZWN0LmtleXMoc291cmNlKTsKICB2YXIga2V5LCBpOwoKICBmb3IgKGkgPSAwOyBpIDwgc291cmNlS2V5cy5sZW5ndGg7IGkrKykgewogICAga2V5ID0gc291cmNlS2V5c1tpXTsKICAgIGlmIChleGNsdWRlZC5pbmRleE9mKGtleSkgPj0gMCkgY29udGludWU7CiAgICB0YXJnZXRba2V5XSA9IHNvdXJjZVtrZXldOwogIH0KCiAgcmV0dXJuIHRhcmdldDsKfQoKY29uc3QgZGVmYXVsdE1ldGhvZCA9ICJnZXQiOwpjb25zdCBkZWZhdWx0RW5jVHlwZSA9ICJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiOwpmdW5jdGlvbiBpc0h0bWxFbGVtZW50KG9iamVjdCkgewogIHJldHVybiBvYmplY3QgIT0gbnVsbCAmJiB0eXBlb2Ygb2JqZWN0LnRhZ05hbWUgPT09ICJzdHJpbmciOwp9CmZ1bmN0aW9uIGlzQnV0dG9uRWxlbWVudChvYmplY3QpIHsKICByZXR1cm4gaXNIdG1sRWxlbWVudChvYmplY3QpICYmIG9iamVjdC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICJidXR0b24iOwp9CmZ1bmN0aW9uIGlzRm9ybUVsZW1lbnQob2JqZWN0KSB7CiAgcmV0dXJuIGlzSHRtbEVsZW1lbnQob2JqZWN0KSAmJiBvYmplY3QudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAiZm9ybSI7Cn0KZnVuY3Rpb24gaXNJbnB1dEVsZW1lbnQob2JqZWN0KSB7CiAgcmV0dXJuIGlzSHRtbEVsZW1lbnQob2JqZWN0KSAmJiBvYmplY3QudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAiaW5wdXQiOwp9CgpmdW5jdGlvbiBpc01vZGlmaWVkRXZlbnQoZXZlbnQpIHsKICByZXR1cm4gISEoZXZlbnQubWV0YUtleSB8fCBldmVudC5hbHRLZXkgfHwgZXZlbnQuY3RybEtleSB8fCBldmVudC5zaGlmdEtleSk7Cn0KCmZ1bmN0aW9uIHNob3VsZFByb2Nlc3NMaW5rQ2xpY2soZXZlbnQsIHRhcmdldCkgewogIHJldHVybiBldmVudC5idXR0b24gPT09IDAgJiYgKCAvLyBJZ25vcmUgZXZlcnl0aGluZyBidXQgbGVmdCBjbGlja3MKICAhdGFyZ2V0IHx8IHRhcmdldCA9PT0gIl9zZWxmIikgJiYgLy8gTGV0IGJyb3dzZXIgaGFuZGxlICJ0YXJnZXQ9X2JsYW5rIiBldGMuCiAgIWlzTW9kaWZpZWRFdmVudChldmVudCkgLy8gSWdub3JlIGNsaWNrcyB3aXRoIG1vZGlmaWVyIGtleXMKICA7Cn0KLyoqCiAqIENyZWF0ZXMgYSBVUkxTZWFyY2hQYXJhbXMgb2JqZWN0IHVzaW5nIHRoZSBnaXZlbiBpbml0aWFsaXplci4KICoKICogVGhpcyBpcyBpZGVudGljYWwgdG8gYG5ldyBVUkxTZWFyY2hQYXJhbXMoaW5pdClgIGV4Y2VwdCBpdCBhbHNvCiAqIHN1cHBvcnRzIGFycmF5cyBhcyB2YWx1ZXMgaW4gdGhlIG9iamVjdCBmb3JtIG9mIHRoZSBpbml0aWFsaXplcgogKiBpbnN0ZWFkIG9mIGp1c3Qgc3RyaW5ncy4gVGhpcyBpcyBjb252ZW5pZW50IHdoZW4geW91IG5lZWQgbXVsdGlwbGUKICogdmFsdWVzIGZvciBhIGdpdmVuIGtleSwgYnV0IGRvbid0IHdhbnQgdG8gdXNlIGFuIGFycmF5IGluaXRpYWxpemVyLgogKgogKiBGb3IgZXhhbXBsZSwgaW5zdGVhZCBvZjoKICoKICogICBsZXQgc2VhcmNoUGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhbCiAqICAgICBbJ3NvcnQnLCAnbmFtZSddLAogKiAgICAgWydzb3J0JywgJ3ByaWNlJ10KICogICBdKTsKICoKICogeW91IGNhbiBkbzoKICoKICogICBsZXQgc2VhcmNoUGFyYW1zID0gY3JlYXRlU2VhcmNoUGFyYW1zKHsKICogICAgIHNvcnQ6IFsnbmFtZScsICdwcmljZSddCiAqICAgfSk7CiAqLwoKZnVuY3Rpb24gY3JlYXRlU2VhcmNoUGFyYW1zKGluaXQpIHsKICBpZiAoaW5pdCA9PT0gdm9pZCAwKSB7CiAgICBpbml0ID0gIiI7CiAgfQoKICByZXR1cm4gbmV3IFVSTFNlYXJjaFBhcmFtcyh0eXBlb2YgaW5pdCA9PT0gInN0cmluZyIgfHwgQXJyYXkuaXNBcnJheShpbml0KSB8fCBpbml0IGluc3RhbmNlb2YgVVJMU2VhcmNoUGFyYW1zID8gaW5pdCA6IE9iamVjdC5rZXlzKGluaXQpLnJlZHVjZSgobWVtbywga2V5KSA9PiB7CiAgICBsZXQgdmFsdWUgPSBpbml0W2tleV07CiAgICByZXR1cm4gbWVtby5jb25jYXQoQXJyYXkuaXNBcnJheSh2YWx1ZSkgPyB2YWx1ZS5tYXAodiA9PiBba2V5LCB2XSkgOiBbW2tleSwgdmFsdWVdXSk7CiAgfSwgW10pKTsKfQpmdW5jdGlvbiBnZXRTZWFyY2hQYXJhbXNGb3JMb2NhdGlvbihsb2NhdGlvblNlYXJjaCwgZGVmYXVsdFNlYXJjaFBhcmFtcykgewogIGxldCBzZWFyY2hQYXJhbXMgPSBjcmVhdGVTZWFyY2hQYXJhbXMobG9jYXRpb25TZWFyY2gpOwoKICBmb3IgKGxldCBrZXkgb2YgZGVmYXVsdFNlYXJjaFBhcmFtcy5rZXlzKCkpIHsKICAgIGlmICghc2VhcmNoUGFyYW1zLmhhcyhrZXkpKSB7CiAgICAgIGRlZmF1bHRTZWFyY2hQYXJhbXMuZ2V0QWxsKGtleSkuZm9yRWFjaCh2YWx1ZSA9PiB7CiAgICAgICAgc2VhcmNoUGFyYW1zLmFwcGVuZChrZXksIHZhbHVlKTsKICAgICAgfSk7CiAgICB9CiAgfQoKICByZXR1cm4gc2VhcmNoUGFyYW1zOwp9CmZ1bmN0aW9uIGdldEZvcm1TdWJtaXNzaW9uSW5mbyh0YXJnZXQsIGRlZmF1bHRBY3Rpb24sIG9wdGlvbnMpIHsKICBsZXQgbWV0aG9kOwogIGxldCBhY3Rpb247CiAgbGV0IGVuY1R5cGU7CiAgbGV0IGZvcm1EYXRhOwoKICBpZiAoaXNGb3JtRWxlbWVudCh0YXJnZXQpKSB7CiAgICBsZXQgc3VibWlzc2lvblRyaWdnZXIgPSBvcHRpb25zLnN1Ym1pc3Npb25UcmlnZ2VyOwogICAgbWV0aG9kID0gb3B0aW9ucy5tZXRob2QgfHwgdGFyZ2V0LmdldEF0dHJpYnV0ZSgibWV0aG9kIikgfHwgZGVmYXVsdE1ldGhvZDsKICAgIGFjdGlvbiA9IG9wdGlvbnMuYWN0aW9uIHx8IHRhcmdldC5nZXRBdHRyaWJ1dGUoImFjdGlvbiIpIHx8IGRlZmF1bHRBY3Rpb247CiAgICBlbmNUeXBlID0gb3B0aW9ucy5lbmNUeXBlIHx8IHRhcmdldC5nZXRBdHRyaWJ1dGUoImVuY3R5cGUiKSB8fCBkZWZhdWx0RW5jVHlwZTsKICAgIGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKHRhcmdldCk7CgogICAgaWYgKHN1Ym1pc3Npb25UcmlnZ2VyICYmIHN1Ym1pc3Npb25UcmlnZ2VyLm5hbWUpIHsKICAgICAgZm9ybURhdGEuYXBwZW5kKHN1Ym1pc3Npb25UcmlnZ2VyLm5hbWUsIHN1Ym1pc3Npb25UcmlnZ2VyLnZhbHVlKTsKICAgIH0KICB9IGVsc2UgaWYgKGlzQnV0dG9uRWxlbWVudCh0YXJnZXQpIHx8IGlzSW5wdXRFbGVtZW50KHRhcmdldCkgJiYgKHRhcmdldC50eXBlID09PSAic3VibWl0IiB8fCB0YXJnZXQudHlwZSA9PT0gImltYWdlIikpIHsKICAgIGxldCBmb3JtID0gdGFyZ2V0LmZvcm07CgogICAgaWYgKGZvcm0gPT0gbnVsbCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCBzdWJtaXQgYSA8YnV0dG9uPiBvciA8aW5wdXQgdHlwZT1cInN1Ym1pdFwiPiB3aXRob3V0IGEgPGZvcm0+Iik7CiAgICB9IC8vIDxidXR0b24+LzxpbnB1dCB0eXBlPSJzdWJtaXQiPiBtYXkgb3ZlcnJpZGUgYXR0cmlidXRlcyBvZiA8Zm9ybT4KCgogICAgbWV0aG9kID0gb3B0aW9ucy5tZXRob2QgfHwgdGFyZ2V0LmdldEF0dHJpYnV0ZSgiZm9ybW1ldGhvZCIpIHx8IGZvcm0uZ2V0QXR0cmlidXRlKCJtZXRob2QiKSB8fCBkZWZhdWx0TWV0aG9kOwogICAgYWN0aW9uID0gb3B0aW9ucy5hY3Rpb24gfHwgdGFyZ2V0LmdldEF0dHJpYnV0ZSgiZm9ybWFjdGlvbiIpIHx8IGZvcm0uZ2V0QXR0cmlidXRlKCJhY3Rpb24iKSB8fCBkZWZhdWx0QWN0aW9uOwogICAgZW5jVHlwZSA9IG9wdGlvbnMuZW5jVHlwZSB8fCB0YXJnZXQuZ2V0QXR0cmlidXRlKCJmb3JtZW5jdHlwZSIpIHx8IGZvcm0uZ2V0QXR0cmlidXRlKCJlbmN0eXBlIikgfHwgZGVmYXVsdEVuY1R5cGU7CiAgICBmb3JtRGF0YSA9IG5ldyBGb3JtRGF0YShmb3JtKTsgLy8gSW5jbHVkZSBuYW1lICsgdmFsdWUgZnJvbSBhIDxidXR0b24+LCBhcHBlbmRpbmcgaW4gY2FzZSB0aGUgYnV0dG9uIG5hbWUKICAgIC8vIG1hdGNoZXMgYW4gZXhpc3RpbmcgaW5wdXQgbmFtZQoKICAgIGlmICh0YXJnZXQubmFtZSkgewogICAgICBmb3JtRGF0YS5hcHBlbmQodGFyZ2V0Lm5hbWUsIHRhcmdldC52YWx1ZSk7CiAgICB9CiAgfSBlbHNlIGlmIChpc0h0bWxFbGVtZW50KHRhcmdldCkpIHsKICAgIHRocm93IG5ldyBFcnJvcigiQ2Fubm90IHN1Ym1pdCBlbGVtZW50IHRoYXQgaXMgbm90IDxmb3JtPiwgPGJ1dHRvbj4sIG9yICIgKyAiPGlucHV0IHR5cGU9XCJzdWJtaXR8aW1hZ2VcIj4iKTsKICB9IGVsc2UgewogICAgbWV0aG9kID0gb3B0aW9ucy5tZXRob2QgfHwgZGVmYXVsdE1ldGhvZDsKICAgIGFjdGlvbiA9IG9wdGlvbnMuYWN0aW9uIHx8IGRlZmF1bHRBY3Rpb247CiAgICBlbmNUeXBlID0gb3B0aW9ucy5lbmNUeXBlIHx8IGRlZmF1bHRFbmNUeXBlOwoKICAgIGlmICh0YXJnZXQgaW5zdGFuY2VvZiBGb3JtRGF0YSkgewogICAgICBmb3JtRGF0YSA9IHRhcmdldDsKICAgIH0gZWxzZSB7CiAgICAgIGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7CgogICAgICBpZiAodGFyZ2V0IGluc3RhbmNlb2YgVVJMU2VhcmNoUGFyYW1zKSB7CiAgICAgICAgZm9yIChsZXQgW25hbWUsIHZhbHVlXSBvZiB0YXJnZXQpIHsKICAgICAgICAgIGZvcm1EYXRhLmFwcGVuZChuYW1lLCB2YWx1ZSk7CiAgICAgICAgfQogICAgICB9IGVsc2UgaWYgKHRhcmdldCAhPSBudWxsKSB7CiAgICAgICAgZm9yIChsZXQgbmFtZSBvZiBPYmplY3Qua2V5cyh0YXJnZXQpKSB7CiAgICAgICAgICBmb3JtRGF0YS5hcHBlbmQobmFtZSwgdGFyZ2V0W25hbWVdKTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9CgogIGxldCB7CiAgICBwcm90b2NvbCwKICAgIGhvc3QKICB9ID0gd2luZG93LmxvY2F0aW9uOwogIGxldCB1cmwgPSBuZXcgVVJMKGFjdGlvbiwgcHJvdG9jb2wgKyAiLy8iICsgaG9zdCk7CiAgcmV0dXJuIHsKICAgIHVybCwKICAgIG1ldGhvZCwKICAgIGVuY1R5cGUsCiAgICBmb3JtRGF0YQogIH07Cn0KCmNvbnN0IF9leGNsdWRlZCA9IFsib25DbGljayIsICJyZWxhdGl2ZSIsICJyZWxvYWREb2N1bWVudCIsICJyZXBsYWNlIiwgInN0YXRlIiwgInRhcmdldCIsICJ0byIsICJwcmV2ZW50U2Nyb2xsUmVzZXQiXSwKICAgICAgX2V4Y2x1ZGVkMiA9IFsiYXJpYS1jdXJyZW50IiwgImNhc2VTZW5zaXRpdmUiLCAiY2xhc3NOYW1lIiwgImVuZCIsICJzdHlsZSIsICJ0byIsICJjaGlsZHJlbiJdLAogICAgICBfZXhjbHVkZWQzID0gKC8qIHVudXNlZCBwdXJlIGV4cHJlc3Npb24gb3Igc3VwZXIgKi8gbnVsbCAmJiAoWyJyZWxvYWREb2N1bWVudCIsICJyZXBsYWNlIiwgIm1ldGhvZCIsICJhY3Rpb24iLCAib25TdWJtaXQiLCAiZmV0Y2hlcktleSIsICJyb3V0ZUlkIiwgInJlbGF0aXZlIl0pKTsKLy8jcmVnaW9uIFJvdXRlcnMKLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCmZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJSb3V0ZXIocm91dGVzLCBvcHRzKSB7CiAgdmFyIF93aW5kb3c7CgogIHJldHVybiBjcmVhdGVSb3V0ZXIoewogICAgYmFzZW5hbWU6IG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMuYmFzZW5hbWUsCiAgICBoaXN0b3J5OiBjcmVhdGVCcm93c2VySGlzdG9yeSh7CiAgICAgIHdpbmRvdzogb3B0cyA9PSBudWxsID8gdm9pZCAwIDogb3B0cy53aW5kb3cKICAgIH0pLAogICAgaHlkcmF0aW9uRGF0YTogKG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMuaHlkcmF0aW9uRGF0YSkgfHwgKChfd2luZG93ID0gd2luZG93KSA9PSBudWxsID8gdm9pZCAwIDogX3dpbmRvdy5fX3N0YXRpY1JvdXRlckh5ZHJhdGlvbkRhdGEpLAogICAgcm91dGVzOiBVTlNBRkVfZW5oYW5jZU1hbnVhbFJvdXRlT2JqZWN0cyhyb3V0ZXMpCiAgfSkuaW5pdGlhbGl6ZSgpOwp9CmZ1bmN0aW9uIGNyZWF0ZUhhc2hSb3V0ZXIocm91dGVzLCBvcHRzKSB7CiAgdmFyIF93aW5kb3cyOwoKICByZXR1cm4gY3JlYXRlUm91dGVyKHsKICAgIGJhc2VuYW1lOiBvcHRzID09IG51bGwgPyB2b2lkIDAgOiBvcHRzLmJhc2VuYW1lLAogICAgaGlzdG9yeTogY3JlYXRlSGFzaEhpc3RvcnkoewogICAgICB3aW5kb3c6IG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMud2luZG93CiAgICB9KSwKICAgIGh5ZHJhdGlvbkRhdGE6IChvcHRzID09IG51bGwgPyB2b2lkIDAgOiBvcHRzLmh5ZHJhdGlvbkRhdGEpIHx8ICgoX3dpbmRvdzIgPSB3aW5kb3cpID09IG51bGwgPyB2b2lkIDAgOiBfd2luZG93Mi5fX3N0YXRpY1JvdXRlckh5ZHJhdGlvbkRhdGEpLAogICAgcm91dGVzOiBVTlNBRkVfZW5oYW5jZU1hbnVhbFJvdXRlT2JqZWN0cyhyb3V0ZXMpCiAgfSkuaW5pdGlhbGl6ZSgpOwp9Ci8qKgogKiBBIGA8Um91dGVyPmAgZm9yIHVzZSBpbiB3ZWIgYnJvd3NlcnMuIFByb3ZpZGVzIHRoZSBjbGVhbmVzdCBVUkxzLgogKi8KCmZ1bmN0aW9uIEJyb3dzZXJSb3V0ZXIoX3JlZikgewogIGxldCB7CiAgICBiYXNlbmFtZSwKICAgIGNoaWxkcmVuLAogICAgd2luZG93CiAgfSA9IF9yZWY7CiAgbGV0IGhpc3RvcnlSZWYgPSBSZWFjdC51c2VSZWYoKTsKCiAgaWYgKGhpc3RvcnlSZWYuY3VycmVudCA9PSBudWxsKSB7CiAgICBoaXN0b3J5UmVmLmN1cnJlbnQgPSBjcmVhdGVCcm93c2VySGlzdG9yeSh7CiAgICAgIHdpbmRvdywKICAgICAgdjVDb21wYXQ6IHRydWUKICAgIH0pOwogIH0KCiAgbGV0IGhpc3RvcnkgPSBoaXN0b3J5UmVmLmN1cnJlbnQ7CiAgbGV0IFtzdGF0ZSwgc2V0U3RhdGVdID0gUmVhY3QudXNlU3RhdGUoewogICAgYWN0aW9uOiBoaXN0b3J5LmFjdGlvbiwKICAgIGxvY2F0aW9uOiBoaXN0b3J5LmxvY2F0aW9uCiAgfSk7CiAgUmVhY3QudXNlTGF5b3V0RWZmZWN0KCgpID0+IGhpc3RvcnkubGlzdGVuKHNldFN0YXRlKSwgW2hpc3RvcnldKTsKICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoUm91dGVyLCB7CiAgICBiYXNlbmFtZTogYmFzZW5hbWUsCiAgICBjaGlsZHJlbjogY2hpbGRyZW4sCiAgICBsb2NhdGlvbjogc3RhdGUubG9jYXRpb24sCiAgICBuYXZpZ2F0aW9uVHlwZTogc3RhdGUuYWN0aW9uLAogICAgbmF2aWdhdG9yOiBoaXN0b3J5CiAgfSk7Cn0KLyoqCiAqIEEgYDxSb3V0ZXI+YCBmb3IgdXNlIGluIHdlYiBicm93c2Vycy4gU3RvcmVzIHRoZSBsb2NhdGlvbiBpbiB0aGUgaGFzaAogKiBwb3J0aW9uIG9mIHRoZSBVUkwgc28gaXQgaXMgbm90IHNlbnQgdG8gdGhlIHNlcnZlci4KICovCgpmdW5jdGlvbiBIYXNoUm91dGVyKF9yZWYyKSB7CiAgbGV0IHsKICAgIGJhc2VuYW1lLAogICAgY2hpbGRyZW4sCiAgICB3aW5kb3cKICB9ID0gX3JlZjI7CiAgbGV0IGhpc3RvcnlSZWYgPSBSZWFjdC51c2VSZWYoKTsKCiAgaWYgKGhpc3RvcnlSZWYuY3VycmVudCA9PSBudWxsKSB7CiAgICBoaXN0b3J5UmVmLmN1cnJlbnQgPSBjcmVhdGVIYXNoSGlzdG9yeSh7CiAgICAgIHdpbmRvdywKICAgICAgdjVDb21wYXQ6IHRydWUKICAgIH0pOwogIH0KCiAgbGV0IGhpc3RvcnkgPSBoaXN0b3J5UmVmLmN1cnJlbnQ7CiAgbGV0IFtzdGF0ZSwgc2V0U3RhdGVdID0gUmVhY3QudXNlU3RhdGUoewogICAgYWN0aW9uOiBoaXN0b3J5LmFjdGlvbiwKICAgIGxvY2F0aW9uOiBoaXN0b3J5LmxvY2F0aW9uCiAgfSk7CiAgUmVhY3QudXNlTGF5b3V0RWZmZWN0KCgpID0+IGhpc3RvcnkubGlzdGVuKHNldFN0YXRlKSwgW2hpc3RvcnldKTsKICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoUm91dGVyLCB7CiAgICBiYXNlbmFtZTogYmFzZW5hbWUsCiAgICBjaGlsZHJlbjogY2hpbGRyZW4sCiAgICBsb2NhdGlvbjogc3RhdGUubG9jYXRpb24sCiAgICBuYXZpZ2F0aW9uVHlwZTogc3RhdGUuYWN0aW9uLAogICAgbmF2aWdhdG9yOiBoaXN0b3J5CiAgfSk7Cn0KLyoqCiAqIEEgYDxSb3V0ZXI+YCB0aGF0IGFjY2VwdHMgYSBwcmUtaW5zdGFudGlhdGVkIGhpc3Rvcnkgb2JqZWN0LiBJdCdzIGltcG9ydGFudAogKiB0byBub3RlIHRoYXQgdXNpbmcgeW91ciBvd24gaGlzdG9yeSBvYmplY3QgaXMgaGlnaGx5IGRpc2NvdXJhZ2VkIGFuZCBtYXkgYWRkCiAqIHR3byB2ZXJzaW9ucyBvZiB0aGUgaGlzdG9yeSBsaWJyYXJ5IHRvIHlvdXIgYnVuZGxlcyB1bmxlc3MgeW91IHVzZSB0aGUgc2FtZQogKiB2ZXJzaW9uIG9mIHRoZSBoaXN0b3J5IGxpYnJhcnkgdGhhdCBSZWFjdCBSb3V0ZXIgdXNlcyBpbnRlcm5hbGx5LgogKi8KCmZ1bmN0aW9uIEhpc3RvcnlSb3V0ZXIoX3JlZjMpIHsKICBsZXQgewogICAgYmFzZW5hbWUsCiAgICBjaGlsZHJlbiwKICAgIGhpc3RvcnkKICB9ID0gX3JlZjM7CiAgY29uc3QgW3N0YXRlLCBzZXRTdGF0ZV0gPSBSZWFjdC51c2VTdGF0ZSh7CiAgICBhY3Rpb246IGhpc3RvcnkuYWN0aW9uLAogICAgbG9jYXRpb246IGhpc3RvcnkubG9jYXRpb24KICB9KTsKICBSZWFjdC51c2VMYXlvdXRFZmZlY3QoKCkgPT4gaGlzdG9yeS5saXN0ZW4oc2V0U3RhdGUpLCBbaGlzdG9yeV0pOwogIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChSb3V0ZXIsIHsKICAgIGJhc2VuYW1lOiBiYXNlbmFtZSwKICAgIGNoaWxkcmVuOiBjaGlsZHJlbiwKICAgIGxvY2F0aW9uOiBzdGF0ZS5sb2NhdGlvbiwKICAgIG5hdmlnYXRpb25UeXBlOiBzdGF0ZS5hY3Rpb24sCiAgICBuYXZpZ2F0b3I6IGhpc3RvcnkKICB9KTsKfQoKaWYgKGZhbHNlKSB7fQovKioKICogVGhlIHB1YmxpYyBBUEkgZm9yIHJlbmRlcmluZyBhIGhpc3RvcnktYXdhcmUgPGE+LgogKi8KCmNvbnN0IExpbmsgPSAvKiNfX1BVUkVfXyovcmVhY3QuZm9yd2FyZFJlZihmdW5jdGlvbiBMaW5rV2l0aFJlZihfcmVmNCwgcmVmKSB7CiAgbGV0IHsKICAgIG9uQ2xpY2ssCiAgICByZWxhdGl2ZSwKICAgIHJlbG9hZERvY3VtZW50LAogICAgcmVwbGFjZSwKICAgIHN0YXRlLAogICAgdGFyZ2V0LAogICAgdG8sCiAgICBwcmV2ZW50U2Nyb2xsUmVzZXQKICB9ID0gX3JlZjQsCiAgICAgIHJlc3QgPSBfb2JqZWN0V2l0aG91dFByb3BlcnRpZXNMb29zZShfcmVmNCwgX2V4Y2x1ZGVkKTsKCiAgbGV0IGhyZWYgPSB1c2VIcmVmKHRvLCB7CiAgICByZWxhdGl2ZQogIH0pOwogIGxldCBpbnRlcm5hbE9uQ2xpY2sgPSB1c2VMaW5rQ2xpY2tIYW5kbGVyKHRvLCB7CiAgICByZXBsYWNlLAogICAgc3RhdGUsCiAgICB0YXJnZXQsCiAgICBwcmV2ZW50U2Nyb2xsUmVzZXQsCiAgICByZWxhdGl2ZQogIH0pOwoKICBmdW5jdGlvbiBoYW5kbGVDbGljayhldmVudCkgewogICAgaWYgKG9uQ2xpY2spIG9uQ2xpY2soZXZlbnQpOwoKICAgIGlmICghZXZlbnQuZGVmYXVsdFByZXZlbnRlZCkgewogICAgICBpbnRlcm5hbE9uQ2xpY2soZXZlbnQpOwogICAgfQogIH0KCiAgcmV0dXJuICgKICAgIC8qI19fUFVSRV9fKi8KICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBqc3gtYTExeS9hbmNob3ItaGFzLWNvbnRlbnQKICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoImEiLCByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcyh7fSwgcmVzdCwgewogICAgICBocmVmOiBocmVmLAogICAgICBvbkNsaWNrOiByZWxvYWREb2N1bWVudCA/IG9uQ2xpY2sgOiBoYW5kbGVDbGljaywKICAgICAgcmVmOiByZWYsCiAgICAgIHRhcmdldDogdGFyZ2V0CiAgICB9KSkKICApOwp9KTsKCmlmIChmYWxzZSkge30KLyoqCiAqIEEgPExpbms+IHdyYXBwZXIgdGhhdCBrbm93cyBpZiBpdCdzICJhY3RpdmUiIG9yIG5vdC4KICovCgoKY29uc3QgTmF2TGluayA9IC8qI19fUFVSRV9fKi9yZWFjdC5mb3J3YXJkUmVmKGZ1bmN0aW9uIE5hdkxpbmtXaXRoUmVmKF9yZWY1LCByZWYpIHsKICBsZXQgewogICAgImFyaWEtY3VycmVudCI6IGFyaWFDdXJyZW50UHJvcCA9ICJwYWdlIiwKICAgIGNhc2VTZW5zaXRpdmUgPSBmYWxzZSwKICAgIGNsYXNzTmFtZTogY2xhc3NOYW1lUHJvcCA9ICIiLAogICAgZW5kID0gZmFsc2UsCiAgICBzdHlsZTogc3R5bGVQcm9wLAogICAgdG8sCiAgICBjaGlsZHJlbgogIH0gPSBfcmVmNSwKICAgICAgcmVzdCA9IF9vYmplY3RXaXRob3V0UHJvcGVydGllc0xvb3NlKF9yZWY1LCBfZXhjbHVkZWQyKTsKCiAgbGV0IHBhdGggPSBkaXN0X3VzZVJlc29sdmVkUGF0aCh0bywgewogICAgcmVsYXRpdmU6IHJlc3QucmVsYXRpdmUKICB9KTsKICBsZXQgbG9jYXRpb24gPSBkaXN0X3VzZUxvY2F0aW9uKCk7CiAgbGV0IHJvdXRlclN0YXRlID0gcmVhY3QudXNlQ29udGV4dChEYXRhUm91dGVyU3RhdGVDb250ZXh0KTsKICBsZXQgdG9QYXRobmFtZSA9IHBhdGgucGF0aG5hbWU7CiAgbGV0IGxvY2F0aW9uUGF0aG5hbWUgPSBsb2NhdGlvbi5wYXRobmFtZTsKICBsZXQgbmV4dExvY2F0aW9uUGF0aG5hbWUgPSByb3V0ZXJTdGF0ZSAmJiByb3V0ZXJTdGF0ZS5uYXZpZ2F0aW9uICYmIHJvdXRlclN0YXRlLm5hdmlnYXRpb24ubG9jYXRpb24gPyByb3V0ZXJTdGF0ZS5uYXZpZ2F0aW9uLmxvY2F0aW9uLnBhdGhuYW1lIDogbnVsbDsKCiAgaWYgKCFjYXNlU2Vuc2l0aXZlKSB7CiAgICBsb2NhdGlvblBhdGhuYW1lID0gbG9jYXRpb25QYXRobmFtZS50b0xvd2VyQ2FzZSgpOwogICAgbmV4dExvY2F0aW9uUGF0aG5hbWUgPSBuZXh0TG9jYXRpb25QYXRobmFtZSA/IG5leHRMb2NhdGlvblBhdGhuYW1lLnRvTG93ZXJDYXNlKCkgOiBudWxsOwogICAgdG9QYXRobmFtZSA9IHRvUGF0aG5hbWUudG9Mb3dlckNhc2UoKTsKICB9CgogIGxldCBpc0FjdGl2ZSA9IGxvY2F0aW9uUGF0aG5hbWUgPT09IHRvUGF0aG5hbWUgfHwgIWVuZCAmJiBsb2NhdGlvblBhdGhuYW1lLnN0YXJ0c1dpdGgodG9QYXRobmFtZSkgJiYgbG9jYXRpb25QYXRobmFtZS5jaGFyQXQodG9QYXRobmFtZS5sZW5ndGgpID09PSAiLyI7CiAgbGV0IGlzUGVuZGluZyA9IG5leHRMb2NhdGlvblBhdGhuYW1lICE9IG51bGwgJiYgKG5leHRMb2NhdGlvblBhdGhuYW1lID09PSB0b1BhdGhuYW1lIHx8ICFlbmQgJiYgbmV4dExvY2F0aW9uUGF0aG5hbWUuc3RhcnRzV2l0aCh0b1BhdGhuYW1lKSAmJiBuZXh0TG9jYXRpb25QYXRobmFtZS5jaGFyQXQodG9QYXRobmFtZS5sZW5ndGgpID09PSAiLyIpOwogIGxldCBhcmlhQ3VycmVudCA9IGlzQWN0aXZlID8gYXJpYUN1cnJlbnRQcm9wIDogdW5kZWZpbmVkOwogIGxldCBjbGFzc05hbWU7CgogIGlmICh0eXBlb2YgY2xhc3NOYW1lUHJvcCA9PT0gImZ1bmN0aW9uIikgewogICAgY2xhc3NOYW1lID0gY2xhc3NOYW1lUHJvcCh7CiAgICAgIGlzQWN0aXZlLAogICAgICBpc1BlbmRpbmcKICAgIH0pOwogIH0gZWxzZSB7CiAgICAvLyBJZiB0aGUgY2xhc3NOYW1lIHByb3AgaXMgbm90IGEgZnVuY3Rpb24sIHdlIHVzZSBhIGRlZmF1bHQgYGFjdGl2ZWAKICAgIC8vIGNsYXNzIGZvciA8TmF2TGluayAvPnMgdGhhdCBhcmUgYWN0aXZlLiBJbiB2NSBgYWN0aXZlYCB3YXMgdGhlIGRlZmF1bHQKICAgIC8vIHZhbHVlIGZvciBgYWN0aXZlQ2xhc3NOYW1lYCwgYnV0IHdlIGFyZSByZW1vdmluZyB0aGF0IEFQSSBhbmQgY2FuIHN0aWxsCiAgICAvLyB1c2UgdGhlIG9sZCBkZWZhdWx0IGJlaGF2aW9yIGZvciBhIGNsZWFuZXIgdXBncmFkZSBwYXRoIGFuZCBrZWVwIHRoZQogICAgLy8gc2ltcGxlIHN0eWxpbmcgcnVsZXMgd29ya2luZyBhcyB0aGV5IGN1cnJlbnRseSBkby4KICAgIGNsYXNzTmFtZSA9IFtjbGFzc05hbWVQcm9wLCBpc0FjdGl2ZSA/ICJhY3RpdmUiIDogbnVsbCwgaXNQZW5kaW5nID8gInBlbmRpbmciIDogbnVsbF0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oIiAiKTsKICB9CgogIGxldCBzdHlsZSA9IHR5cGVvZiBzdHlsZVByb3AgPT09ICJmdW5jdGlvbiIgPyBzdHlsZVByb3AoewogICAgaXNBY3RpdmUsCiAgICBpc1BlbmRpbmcKICB9KSA6IHN0eWxlUHJvcDsKICByZXR1cm4gLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQoTGluaywgcmVhY3Rfcm91dGVyX2RvbV9kaXN0X2V4dGVuZHMoe30sIHJlc3QsIHsKICAgICJhcmlhLWN1cnJlbnQiOiBhcmlhQ3VycmVudCwKICAgIGNsYXNzTmFtZTogY2xhc3NOYW1lLAogICAgcmVmOiByZWYsCiAgICBzdHlsZTogc3R5bGUsCiAgICB0bzogdG8KICB9KSwgdHlwZW9mIGNoaWxkcmVuID09PSAiZnVuY3Rpb24iID8gY2hpbGRyZW4oewogICAgaXNBY3RpdmUsCiAgICBpc1BlbmRpbmcKICB9KSA6IGNoaWxkcmVuKTsKfSk7CgppZiAoZmFsc2UpIHt9Ci8qKgogKiBBIGBAcmVtaXgtcnVuL3JvdXRlcmAtYXdhcmUgYDxmb3JtPmAuIEl0IGJlaGF2ZXMgbGlrZSBhIG5vcm1hbCBmb3JtIGV4Y2VwdAogKiB0aGF0IHRoZSBpbnRlcmFjdGlvbiB3aXRoIHRoZSBzZXJ2ZXIgaXMgd2l0aCBgZmV0Y2hgIGluc3RlYWQgb2YgbmV3IGRvY3VtZW50CiAqIHJlcXVlc3RzLCBhbGxvd2luZyBjb21wb25lbnRzIHRvIGFkZCBuaWNlciBVWCB0byB0aGUgcGFnZSBhcyB0aGUgZm9ybSBpcwogKiBzdWJtaXR0ZWQgYW5kIHJldHVybnMgd2l0aCBkYXRhLgogKi8KCgpjb25zdCBGb3JtID0gLyojX19QVVJFX18qLygvKiB1bnVzZWQgcHVyZSBleHByZXNzaW9uIG9yIHN1cGVyICovIG51bGwgJiYgKFJlYWN0LmZvcndhcmRSZWYoKHByb3BzLCByZWYpID0+IHsKICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoRm9ybUltcGwsIHJlYWN0X3JvdXRlcl9kb21fZGlzdF9leHRlbmRzKHt9LCBwcm9wcywgewogICAgcmVmOiByZWYKICB9KSk7Cn0pKSk7CgppZiAoZmFsc2UpIHt9Cgpjb25zdCBGb3JtSW1wbCA9IC8qI19fUFVSRV9fKi8oLyogdW51c2VkIHB1cmUgZXhwcmVzc2lvbiBvciBzdXBlciAqLyBudWxsICYmIChSZWFjdC5mb3J3YXJkUmVmKChfcmVmNiwgZm9yd2FyZGVkUmVmKSA9PiB7CiAgbGV0IHsKICAgIHJlbG9hZERvY3VtZW50LAogICAgcmVwbGFjZSwKICAgIG1ldGhvZCA9IGRlZmF1bHRNZXRob2QsCiAgICBhY3Rpb24sCiAgICBvblN1Ym1pdCwKICAgIGZldGNoZXJLZXksCiAgICByb3V0ZUlkLAogICAgcmVsYXRpdmUKICB9ID0gX3JlZjYsCiAgICAgIHByb3BzID0gX29iamVjdFdpdGhvdXRQcm9wZXJ0aWVzTG9vc2UoX3JlZjYsIF9leGNsdWRlZDMpOwoKICBsZXQgc3VibWl0ID0gdXNlU3VibWl0SW1wbChmZXRjaGVyS2V5LCByb3V0ZUlkKTsKICBsZXQgZm9ybU1ldGhvZCA9IG1ldGhvZC50b0xvd2VyQ2FzZSgpID09PSAiZ2V0IiA/ICJnZXQiIDogInBvc3QiOwogIGxldCBmb3JtQWN0aW9uID0gdXNlRm9ybUFjdGlvbihhY3Rpb24sIHsKICAgIHJlbGF0aXZlCiAgfSk7CgogIGxldCBzdWJtaXRIYW5kbGVyID0gZXZlbnQgPT4gewogICAgb25TdWJtaXQgJiYgb25TdWJtaXQoZXZlbnQpOwogICAgaWYgKGV2ZW50LmRlZmF1bHRQcmV2ZW50ZWQpIHJldHVybjsKICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7CiAgICBsZXQgc3VibWl0dGVyID0gZXZlbnQubmF0aXZlRXZlbnQuc3VibWl0dGVyOwogICAgc3VibWl0KHN1Ym1pdHRlciB8fCBldmVudC5jdXJyZW50VGFyZ2V0LCB7CiAgICAgIG1ldGhvZCwKICAgICAgcmVwbGFjZSwKICAgICAgcmVsYXRpdmUKICAgIH0pOwogIH07CgogIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudCgiZm9ybSIsIHJlYWN0X3JvdXRlcl9kb21fZGlzdF9leHRlbmRzKHsKICAgIHJlZjogZm9yd2FyZGVkUmVmLAogICAgbWV0aG9kOiBmb3JtTWV0aG9kLAogICAgYWN0aW9uOiBmb3JtQWN0aW9uLAogICAgb25TdWJtaXQ6IHJlbG9hZERvY3VtZW50ID8gb25TdWJtaXQgOiBzdWJtaXRIYW5kbGVyCiAgfSwgcHJvcHMpKTsKfSkpKTsKCmlmIChmYWxzZSkge30KLyoqCiAqIFRoaXMgY29tcG9uZW50IHdpbGwgZW11bGF0ZSB0aGUgYnJvd3NlcidzIHNjcm9sbCByZXN0b3JhdGlvbiBvbiBsb2NhdGlvbgogKiBjaGFuZ2VzLgogKi8KCgpmdW5jdGlvbiBTY3JvbGxSZXN0b3JhdGlvbihfcmVmNykgewogIGxldCB7CiAgICBnZXRLZXksCiAgICBzdG9yYWdlS2V5CiAgfSA9IF9yZWY3OwogIHVzZVNjcm9sbFJlc3RvcmF0aW9uKHsKICAgIGdldEtleSwKICAgIHN0b3JhZ2VLZXkKICB9KTsKICByZXR1cm4gbnVsbDsKfQoKaWYgKGZhbHNlKSB7fSAvLyNlbmRyZWdpb24KLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KLy8jcmVnaW9uIEhvb2tzCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgoKdmFyIGRpc3RfRGF0YVJvdXRlckhvb2s7CgooZnVuY3Rpb24gKERhdGFSb3V0ZXJIb29rKSB7CiAgRGF0YVJvdXRlckhvb2tbIlVzZVNjcm9sbFJlc3RvcmF0aW9uIl0gPSAidXNlU2Nyb2xsUmVzdG9yYXRpb24iOwogIERhdGFSb3V0ZXJIb29rWyJVc2VTdWJtaXRJbXBsIl0gPSAidXNlU3VibWl0SW1wbCI7CiAgRGF0YVJvdXRlckhvb2tbIlVzZUZldGNoZXIiXSA9ICJ1c2VGZXRjaGVyIjsKfSkoZGlzdF9EYXRhUm91dGVySG9vayB8fCAoZGlzdF9EYXRhUm91dGVySG9vayA9IHt9KSk7Cgp2YXIgZGlzdF9EYXRhUm91dGVyU3RhdGVIb29rOwoKKGZ1bmN0aW9uIChEYXRhUm91dGVyU3RhdGVIb29rKSB7CiAgRGF0YVJvdXRlclN0YXRlSG9va1siVXNlRmV0Y2hlcnMiXSA9ICJ1c2VGZXRjaGVycyI7CiAgRGF0YVJvdXRlclN0YXRlSG9va1siVXNlU2Nyb2xsUmVzdG9yYXRpb24iXSA9ICJ1c2VTY3JvbGxSZXN0b3JhdGlvbiI7Cn0pKGRpc3RfRGF0YVJvdXRlclN0YXRlSG9vayB8fCAoZGlzdF9EYXRhUm91dGVyU3RhdGVIb29rID0ge30pKTsKCmZ1bmN0aW9uIGRpc3RfZ2V0RGF0YVJvdXRlckNvbnNvbGVFcnJvcihob29rTmFtZSkgewogIHJldHVybiBob29rTmFtZSArICIgbXVzdCBiZSB1c2VkIHdpdGhpbiBhIGRhdGEgcm91dGVyLiAgU2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL2VuL21haW4vcm91dGVycy9waWNraW5nLWEtcm91dGVyLiI7Cn0KCmZ1bmN0aW9uIGRpc3RfdXNlRGF0YVJvdXRlckNvbnRleHQoaG9va05hbWUpIHsKICBsZXQgY3R4ID0gUmVhY3QudXNlQ29udGV4dChVTlNBRkVfRGF0YVJvdXRlckNvbnRleHQpOwogICFjdHggPyAgZmFsc2UgPyAwIDogaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICByZXR1cm4gY3R4Owp9CgpmdW5jdGlvbiBkaXN0X3VzZURhdGFSb3V0ZXJTdGF0ZShob29rTmFtZSkgewogIGxldCBzdGF0ZSA9IFJlYWN0LnVzZUNvbnRleHQoVU5TQUZFX0RhdGFSb3V0ZXJTdGF0ZUNvbnRleHQpOwogICFzdGF0ZSA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIHJldHVybiBzdGF0ZTsKfQovKioKICogSGFuZGxlcyB0aGUgY2xpY2sgYmVoYXZpb3IgZm9yIHJvdXRlciBgPExpbms+YCBjb21wb25lbnRzLiBUaGlzIGlzIHVzZWZ1bCBpZgogKiB5b3UgbmVlZCB0byBjcmVhdGUgY3VzdG9tIGA8TGluaz5gIGNvbXBvbmVudHMgd2l0aCB0aGUgc2FtZSBjbGljayBiZWhhdmlvciB3ZQogKiB1c2UgaW4gb3VyIGV4cG9ydGVkIGA8TGluaz5gLgogKi8KCgpmdW5jdGlvbiB1c2VMaW5rQ2xpY2tIYW5kbGVyKHRvLCBfdGVtcCkgewogIGxldCB7CiAgICB0YXJnZXQsCiAgICByZXBsYWNlOiByZXBsYWNlUHJvcCwKICAgIHN0YXRlLAogICAgcHJldmVudFNjcm9sbFJlc2V0LAogICAgcmVsYXRpdmUKICB9ID0gX3RlbXAgPT09IHZvaWQgMCA/IHt9IDogX3RlbXA7CiAgbGV0IG5hdmlnYXRlID0gZGlzdF91c2VOYXZpZ2F0ZSgpOwogIGxldCBsb2NhdGlvbiA9IGRpc3RfdXNlTG9jYXRpb24oKTsKICBsZXQgcGF0aCA9IGRpc3RfdXNlUmVzb2x2ZWRQYXRoKHRvLCB7CiAgICByZWxhdGl2ZQogIH0pOwogIHJldHVybiByZWFjdC51c2VDYWxsYmFjayhldmVudCA9PiB7CiAgICBpZiAoc2hvdWxkUHJvY2Vzc0xpbmtDbGljayhldmVudCwgdGFyZ2V0KSkgewogICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyAvLyBJZiB0aGUgVVJMIGhhc24ndCBjaGFuZ2VkLCBhIHJlZ3VsYXIgPGE+IHdpbGwgZG8gYSByZXBsYWNlIGluc3RlYWQgb2YKICAgICAgLy8gYSBwdXNoLCBzbyBkbyB0aGUgc2FtZSBoZXJlIHVubGVzcyB0aGUgcmVwbGFjZSBwcm9wIGlzIGV4cGxpY2l0bHkgc2V0CgogICAgICBsZXQgcmVwbGFjZSA9IHJlcGxhY2VQcm9wICE9PSB1bmRlZmluZWQgPyByZXBsYWNlUHJvcCA6IHJvdXRlcl9jcmVhdGVQYXRoKGxvY2F0aW9uKSA9PT0gcm91dGVyX2NyZWF0ZVBhdGgocGF0aCk7CiAgICAgIG5hdmlnYXRlKHRvLCB7CiAgICAgICAgcmVwbGFjZSwKICAgICAgICBzdGF0ZSwKICAgICAgICBwcmV2ZW50U2Nyb2xsUmVzZXQsCiAgICAgICAgcmVsYXRpdmUKICAgICAgfSk7CiAgICB9CiAgfSwgW2xvY2F0aW9uLCBuYXZpZ2F0ZSwgcGF0aCwgcmVwbGFjZVByb3AsIHN0YXRlLCB0YXJnZXQsIHRvLCBwcmV2ZW50U2Nyb2xsUmVzZXQsIHJlbGF0aXZlXSk7Cn0KLyoqCiAqIEEgY29udmVuaWVudCB3cmFwcGVyIGZvciByZWFkaW5nIGFuZCB3cml0aW5nIHNlYXJjaCBwYXJhbWV0ZXJzIHZpYSB0aGUKICogVVJMU2VhcmNoUGFyYW1zIGludGVyZmFjZS4KICovCgpmdW5jdGlvbiB1c2VTZWFyY2hQYXJhbXMoZGVmYXVsdEluaXQpIHsKICAgZmFsc2UgPyAwIDogdm9pZCAwOwogIGxldCBkZWZhdWx0U2VhcmNoUGFyYW1zUmVmID0gUmVhY3QudXNlUmVmKGNyZWF0ZVNlYXJjaFBhcmFtcyhkZWZhdWx0SW5pdCkpOwogIGxldCBsb2NhdGlvbiA9IHVzZUxvY2F0aW9uKCk7CiAgbGV0IHNlYXJjaFBhcmFtcyA9IFJlYWN0LnVzZU1lbW8oKCkgPT4gZ2V0U2VhcmNoUGFyYW1zRm9yTG9jYXRpb24obG9jYXRpb24uc2VhcmNoLCBkZWZhdWx0U2VhcmNoUGFyYW1zUmVmLmN1cnJlbnQpLCBbbG9jYXRpb24uc2VhcmNoXSk7CiAgbGV0IG5hdmlnYXRlID0gdXNlTmF2aWdhdGUoKTsKICBsZXQgc2V0U2VhcmNoUGFyYW1zID0gUmVhY3QudXNlQ2FsbGJhY2soKG5leHRJbml0LCBuYXZpZ2F0ZU9wdGlvbnMpID0+IHsKICAgIGNvbnN0IG5ld1NlYXJjaFBhcmFtcyA9IGNyZWF0ZVNlYXJjaFBhcmFtcyh0eXBlb2YgbmV4dEluaXQgPT09ICJmdW5jdGlvbiIgPyBuZXh0SW5pdChzZWFyY2hQYXJhbXMpIDogbmV4dEluaXQpOwogICAgbmF2aWdhdGUoIj8iICsgbmV3U2VhcmNoUGFyYW1zLCBuYXZpZ2F0ZU9wdGlvbnMpOwogIH0sIFtuYXZpZ2F0ZSwgc2VhcmNoUGFyYW1zXSk7CiAgcmV0dXJuIFtzZWFyY2hQYXJhbXMsIHNldFNlYXJjaFBhcmFtc107Cn0KLyoqCiAqIFJldHVybnMgYSBmdW5jdGlvbiB0aGF0IG1heSBiZSB1c2VkIHRvIHByb2dyYW1tYXRpY2FsbHkgc3VibWl0IGEgZm9ybSAob3IKICogc29tZSBhcmJpdHJhcnkgZGF0YSkgdG8gdGhlIHNlcnZlci4KICovCgpmdW5jdGlvbiB1c2VTdWJtaXQoKSB7CiAgcmV0dXJuIHVzZVN1Ym1pdEltcGwoKTsKfQoKZnVuY3Rpb24gdXNlU3VibWl0SW1wbChmZXRjaGVyS2V5LCByb3V0ZUlkKSB7CiAgbGV0IHsKICAgIHJvdXRlcgogIH0gPSBkaXN0X3VzZURhdGFSb3V0ZXJDb250ZXh0KGRpc3RfRGF0YVJvdXRlckhvb2suVXNlU3VibWl0SW1wbCk7CiAgbGV0IGRlZmF1bHRBY3Rpb24gPSB1c2VGb3JtQWN0aW9uKCk7CiAgcmV0dXJuIFJlYWN0LnVzZUNhbGxiYWNrKGZ1bmN0aW9uICh0YXJnZXQsIG9wdGlvbnMpIHsKICAgIGlmIChvcHRpb25zID09PSB2b2lkIDApIHsKICAgICAgb3B0aW9ucyA9IHt9OwogICAgfQoKICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgPT09ICJ1bmRlZmluZWQiKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcigiWW91IGFyZSBjYWxsaW5nIHN1Ym1pdCBkdXJpbmcgdGhlIHNlcnZlciByZW5kZXIuICIgKyAiVHJ5IGNhbGxpbmcgc3VibWl0IHdpdGhpbiBhIGB1c2VFZmZlY3RgIG9yIGNhbGxiYWNrIGluc3RlYWQuIik7CiAgICB9CgogICAgbGV0IHsKICAgICAgbWV0aG9kLAogICAgICBlbmNUeXBlLAogICAgICBmb3JtRGF0YSwKICAgICAgdXJsCiAgICB9ID0gZ2V0Rm9ybVN1Ym1pc3Npb25JbmZvKHRhcmdldCwgZGVmYXVsdEFjdGlvbiwgb3B0aW9ucyk7CiAgICBsZXQgaHJlZiA9IHVybC5wYXRobmFtZSArIHVybC5zZWFyY2g7CiAgICBsZXQgb3B0cyA9IHsKICAgICAgcmVwbGFjZTogb3B0aW9ucy5yZXBsYWNlLAogICAgICBmb3JtRGF0YSwKICAgICAgZm9ybU1ldGhvZDogbWV0aG9kLAogICAgICBmb3JtRW5jVHlwZTogZW5jVHlwZQogICAgfTsKCiAgICBpZiAoZmV0Y2hlcktleSkgewogICAgICAhKHJvdXRlSWQgIT0gbnVsbCkgPyAgZmFsc2UgPyAwIDogaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICAgICAgcm91dGVyLmZldGNoKGZldGNoZXJLZXksIHJvdXRlSWQsIGhyZWYsIG9wdHMpOwogICAgfSBlbHNlIHsKICAgICAgcm91dGVyLm5hdmlnYXRlKGhyZWYsIG9wdHMpOwogICAgfQogIH0sIFtkZWZhdWx0QWN0aW9uLCByb3V0ZXIsIGZldGNoZXJLZXksIHJvdXRlSWRdKTsKfQoKZnVuY3Rpb24gdXNlRm9ybUFjdGlvbihhY3Rpb24sIF90ZW1wMikgewogIGxldCB7CiAgICByZWxhdGl2ZQogIH0gPSBfdGVtcDIgPT09IHZvaWQgMCA/IHt9IDogX3RlbXAyOwogIGxldCB7CiAgICBiYXNlbmFtZQogIH0gPSBSZWFjdC51c2VDb250ZXh0KFVOU0FGRV9OYXZpZ2F0aW9uQ29udGV4dCk7CiAgbGV0IHJvdXRlQ29udGV4dCA9IFJlYWN0LnVzZUNvbnRleHQoVU5TQUZFX1JvdXRlQ29udGV4dCk7CiAgIXJvdXRlQ29udGV4dCA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIGxldCBbbWF0Y2hdID0gcm91dGVDb250ZXh0Lm1hdGNoZXMuc2xpY2UoLTEpOwogIGxldCByZXNvbHZlZEFjdGlvbiA9IGFjdGlvbiAhPSBudWxsID8gYWN0aW9uIDogIi4iOyAvLyBTaGFsbG93IGNsb25lIHBhdGggc28gd2UgY2FuIG1vZGlmeSBpdCBiZWxvdywgb3RoZXJ3aXNlIHdlIG1vZGlmeSB0aGUKICAvLyBvYmplY3QgcmVmZXJlbmNlZCBieSB1c2VNZW1vIGluc2lkZSB1c2VSZXNvbHZlZFBhdGgKCiAgbGV0IHBhdGggPSByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcyh7fSwgdXNlUmVzb2x2ZWRQYXRoKHJlc29sdmVkQWN0aW9uLCB7CiAgICByZWxhdGl2ZQogIH0pKTsgLy8gUHJldmlvdXNseSB3ZSBzZXQgdGhlIGRlZmF1bHQgYWN0aW9uIHRvICIuIi4gVGhlIHByb2JsZW0gd2l0aCB0aGlzIGlzIHRoYXQKICAvLyBgdXNlUmVzb2x2ZWRQYXRoKCIuIilgIGV4Y2x1ZGVzIHNlYXJjaCBwYXJhbXMgYW5kIHRoZSBoYXNoIG9mIHRoZSByZXNvbHZlZAogIC8vIFVSTC4gVGhpcyBpcyB0aGUgaW50ZW5kZWQgYmVoYXZpb3Igb2Ygd2hlbiAiLiIgaXMgc3BlY2lmaWNhbGx5IHByb3ZpZGVkIGFzCiAgLy8gdGhlIGZvcm0gYWN0aW9uLCBidXQgaW5jb25zaXN0ZW50IHcvIGJyb3dzZXJzIHdoZW4gdGhlIGFjdGlvbiBpcyBvbWl0dGVkLgogIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9yZW1peC1ydW4vcmVtaXgvaXNzdWVzLzkyNwoKCiAgbGV0IGxvY2F0aW9uID0gdXNlTG9jYXRpb24oKTsKCiAgaWYgKGFjdGlvbiA9PSBudWxsKSB7CiAgICAvLyBTYWZlIHRvIHdyaXRlIHRvIHRoZXNlIGRpcmVjdGx5IGhlcmUgc2luY2UgaWYgYWN0aW9uIHdhcyB1bmRlZmluZWQsIHdlCiAgICAvLyB3b3VsZCBoYXZlIGNhbGxlZCB1c2VSZXNvbHZlZFBhdGgoIi4iKSB3aGljaCB3aWxsIG5ldmVyIGluY2x1ZGUgYSBzZWFyY2gKICAgIC8vIG9yIGhhc2gKICAgIHBhdGguc2VhcmNoID0gbG9jYXRpb24uc2VhcmNoOwogICAgcGF0aC5oYXNoID0gbG9jYXRpb24uaGFzaDsgLy8gV2hlbiBncmFiYmluZyBzZWFyY2ggcGFyYW1zIGZyb20gdGhlIFVSTCwgcmVtb3ZlIHRoZSBhdXRvbWF0aWNhbGx5CiAgICAvLyBpbnNlcnRlZCA/aW5kZXggcGFyYW0gc28gd2UgbWF0Y2ggdGhlIHVzZVJlc29sdmVkUGF0aCBzZWFyY2ggYmVoYXZpb3IKICAgIC8vIHdoaWNoIHdvdWxkIG5vdCBpbmNsdWRlID9pbmRleAoKICAgIGlmIChtYXRjaC5yb3V0ZS5pbmRleCkgewogICAgICBsZXQgcGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhwYXRoLnNlYXJjaCk7CiAgICAgIHBhcmFtcy5kZWxldGUoImluZGV4Iik7CiAgICAgIHBhdGguc2VhcmNoID0gcGFyYW1zLnRvU3RyaW5nKCkgPyAiPyIgKyBwYXJhbXMudG9TdHJpbmcoKSA6ICIiOwogICAgfQogIH0KCiAgaWYgKCghYWN0aW9uIHx8IGFjdGlvbiA9PT0gIi4iKSAmJiBtYXRjaC5yb3V0ZS5pbmRleCkgewogICAgcGF0aC5zZWFyY2ggPSBwYXRoLnNlYXJjaCA/IHBhdGguc2VhcmNoLnJlcGxhY2UoL15cPy8sICI/aW5kZXgmIikgOiAiP2luZGV4IjsKICB9IC8vIElmIHdlJ3JlIG9wZXJhdGluZyB3aXRoaW4gYSBiYXNlbmFtZSwgcHJlcGVuZCBpdCB0byB0aGUgcGF0aG5hbWUgcHJpb3IKICAvLyB0byBjcmVhdGluZyB0aGUgZm9ybSBhY3Rpb24uICBJZiB0aGlzIGlzIGEgcm9vdCBuYXZpZ2F0aW9uLCB0aGVuIGp1c3QgdXNlCiAgLy8gdGhlIHJhdyBiYXNlbmFtZSB3aGljaCBhbGxvd3MgdGhlIGJhc2VuYW1lIHRvIGhhdmUgZnVsbCBjb250cm9sIG92ZXIgdGhlCiAgLy8gcHJlc2VuY2Ugb2YgYSB0cmFpbGluZyBzbGFzaCBvbiByb290IGFjdGlvbnMKCgogIGlmIChiYXNlbmFtZSAhPT0gIi8iKSB7CiAgICBwYXRoLnBhdGhuYW1lID0gcGF0aC5wYXRobmFtZSA9PT0gIi8iID8gYmFzZW5hbWUgOiBqb2luUGF0aHMoW2Jhc2VuYW1lLCBwYXRoLnBhdGhuYW1lXSk7CiAgfQoKICByZXR1cm4gY3JlYXRlUGF0aChwYXRoKTsKfQoKZnVuY3Rpb24gY3JlYXRlRmV0Y2hlckZvcm0oZmV0Y2hlcktleSwgcm91dGVJZCkgewogIGxldCBGZXRjaGVyRm9ybSA9IC8qI19fUFVSRV9fKi9SZWFjdC5mb3J3YXJkUmVmKChwcm9wcywgcmVmKSA9PiB7CiAgICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoRm9ybUltcGwsIHJlYWN0X3JvdXRlcl9kb21fZGlzdF9leHRlbmRzKHt9LCBwcm9wcywgewogICAgICByZWY6IHJlZiwKICAgICAgZmV0Y2hlcktleTogZmV0Y2hlcktleSwKICAgICAgcm91dGVJZDogcm91dGVJZAogICAgfSkpOwogIH0pOwoKICBpZiAoZmFsc2UpIHt9CgogIHJldHVybiBGZXRjaGVyRm9ybTsKfQoKbGV0IGZldGNoZXJJZCA9IDA7Ci8qKgogKiBJbnRlcmFjdHMgd2l0aCByb3V0ZSBsb2FkZXJzIGFuZCBhY3Rpb25zIHdpdGhvdXQgY2F1c2luZyBhIG5hdmlnYXRpb24uIEdyZWF0CiAqIGZvciBhbnkgaW50ZXJhY3Rpb24gdGhhdCBzdGF5cyBvbiB0aGUgc2FtZSBwYWdlLgogKi8KCmZ1bmN0aW9uIHVzZUZldGNoZXIoKSB7CiAgdmFyIF9yb3V0ZSRtYXRjaGVzOwoKICBsZXQgewogICAgcm91dGVyCiAgfSA9IGRpc3RfdXNlRGF0YVJvdXRlckNvbnRleHQoZGlzdF9EYXRhUm91dGVySG9vay5Vc2VGZXRjaGVyKTsKICBsZXQgcm91dGUgPSBSZWFjdC51c2VDb250ZXh0KFVOU0FGRV9Sb3V0ZUNvbnRleHQpOwogICFyb3V0ZSA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogIGxldCByb3V0ZUlkID0gKF9yb3V0ZSRtYXRjaGVzID0gcm91dGUubWF0Y2hlc1tyb3V0ZS5tYXRjaGVzLmxlbmd0aCAtIDFdKSA9PSBudWxsID8gdm9pZCAwIDogX3JvdXRlJG1hdGNoZXMucm91dGUuaWQ7CiAgIShyb3V0ZUlkICE9IG51bGwpID8gIGZhbHNlID8gMCA6IGludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgbGV0IFtmZXRjaGVyS2V5XSA9IFJlYWN0LnVzZVN0YXRlKCgpID0+IFN0cmluZygrK2ZldGNoZXJJZCkpOwogIGxldCBbRm9ybV0gPSBSZWFjdC51c2VTdGF0ZSgoKSA9PiB7CiAgICAhcm91dGVJZCA/ICBmYWxzZSA/IDAgOiBpbnZhcmlhbnQoZmFsc2UpIDogdm9pZCAwOwogICAgcmV0dXJuIGNyZWF0ZUZldGNoZXJGb3JtKGZldGNoZXJLZXksIHJvdXRlSWQpOwogIH0pOwogIGxldCBbbG9hZF0gPSBSZWFjdC51c2VTdGF0ZSgoKSA9PiBocmVmID0+IHsKICAgICFyb3V0ZXIgPyAgZmFsc2UgPyAwIDogaW52YXJpYW50KGZhbHNlKSA6IHZvaWQgMDsKICAgICFyb3V0ZUlkID8gIGZhbHNlID8gMCA6IGludmFyaWFudChmYWxzZSkgOiB2b2lkIDA7CiAgICByb3V0ZXIuZmV0Y2goZmV0Y2hlcktleSwgcm91dGVJZCwgaHJlZik7CiAgfSk7CiAgbGV0IHN1Ym1pdCA9IHVzZVN1Ym1pdEltcGwoZmV0Y2hlcktleSwgcm91dGVJZCk7CiAgbGV0IGZldGNoZXIgPSByb3V0ZXIuZ2V0RmV0Y2hlcihmZXRjaGVyS2V5KTsKICBsZXQgZmV0Y2hlcldpdGhDb21wb25lbnRzID0gUmVhY3QudXNlTWVtbygoKSA9PiByZWFjdF9yb3V0ZXJfZG9tX2Rpc3RfZXh0ZW5kcyh7CiAgICBGb3JtLAogICAgc3VibWl0LAogICAgbG9hZAogIH0sIGZldGNoZXIpLCBbZmV0Y2hlciwgRm9ybSwgc3VibWl0LCBsb2FkXSk7CiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHsKICAgIC8vIElzIHRoaXMgYnVzdGVkIHdoZW4gdGhlIFJlYWN0IHRlYW0gZ2V0cyByZWFsIHdlaXJkIGFuZCBjYWxscyBlZmZlY3RzCiAgICAvLyB0d2ljZSBvbiBtb3VudD8gIFdlIHJlYWxseSBqdXN0IG5lZWQgdG8gZ2FyYmFnZSBjb2xsZWN0IGhlcmUgd2hlbiB0aGlzCiAgICAvLyBmZXRjaGVyIGlzIG5vIGxvbmdlciBhcm91bmQuCiAgICByZXR1cm4gKCkgPT4gewogICAgICBpZiAoIXJvdXRlcikgewogICAgICAgIGNvbnNvbGUud2FybigiTm8gZmV0Y2hlciBhdmFpbGFibGUgdG8gY2xlYW4gdXAgZnJvbSB1c2VGZXRjaGVyKCkiKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIHJvdXRlci5kZWxldGVGZXRjaGVyKGZldGNoZXJLZXkpOwogICAgfTsKICB9LCBbcm91dGVyLCBmZXRjaGVyS2V5XSk7CiAgcmV0dXJuIGZldGNoZXJXaXRoQ29tcG9uZW50czsKfQovKioKICogUHJvdmlkZXMgYWxsIGZldGNoZXJzIGN1cnJlbnRseSBvbiB0aGUgcGFnZS4gVXNlZnVsIGZvciBsYXlvdXRzIGFuZCBwYXJlbnQKICogcm91dGVzIHRoYXQgbmVlZCB0byBwcm92aWRlIHBlbmRpbmcvb3B0aW1pc3RpYyBVSSByZWdhcmRpbmcgdGhlIGZldGNoLgogKi8KCmZ1bmN0aW9uIHVzZUZldGNoZXJzKCkgewogIGxldCBzdGF0ZSA9IGRpc3RfdXNlRGF0YVJvdXRlclN0YXRlKGRpc3RfRGF0YVJvdXRlclN0YXRlSG9vay5Vc2VGZXRjaGVycyk7CiAgcmV0dXJuIFsuLi5zdGF0ZS5mZXRjaGVycy52YWx1ZXMoKV07Cn0KY29uc3QgU0NST0xMX1JFU1RPUkFUSU9OX1NUT1JBR0VfS0VZID0gInJlYWN0LXJvdXRlci1zY3JvbGwtcG9zaXRpb25zIjsKbGV0IHNhdmVkU2Nyb2xsUG9zaXRpb25zID0ge307Ci8qKgogKiBXaGVuIHJlbmRlcmVkIGluc2lkZSBhIFJvdXRlclByb3ZpZGVyLCB3aWxsIHJlc3RvcmUgc2Nyb2xsIHBvc2l0aW9ucyBvbiBuYXZpZ2F0aW9ucwogKi8KCmZ1bmN0aW9uIHVzZVNjcm9sbFJlc3RvcmF0aW9uKF90ZW1wMykgewogIGxldCB7CiAgICBnZXRLZXksCiAgICBzdG9yYWdlS2V5CiAgfSA9IF90ZW1wMyA9PT0gdm9pZCAwID8ge30gOiBfdGVtcDM7CiAgbGV0IHsKICAgIHJvdXRlcgogIH0gPSBkaXN0X3VzZURhdGFSb3V0ZXJDb250ZXh0KGRpc3RfRGF0YVJvdXRlckhvb2suVXNlU2Nyb2xsUmVzdG9yYXRpb24pOwogIGxldCB7CiAgICByZXN0b3JlU2Nyb2xsUG9zaXRpb24sCiAgICBwcmV2ZW50U2Nyb2xsUmVzZXQKICB9ID0gZGlzdF91c2VEYXRhUm91dGVyU3RhdGUoZGlzdF9EYXRhUm91dGVyU3RhdGVIb29rLlVzZVNjcm9sbFJlc3RvcmF0aW9uKTsKICBsZXQgbG9jYXRpb24gPSB1c2VMb2NhdGlvbigpOwogIGxldCBtYXRjaGVzID0gdXNlTWF0Y2hlcygpOwogIGxldCBuYXZpZ2F0aW9uID0gdXNlTmF2aWdhdGlvbigpOyAvLyBUcmlnZ2VyIG1hbnVhbCBzY3JvbGwgcmVzdG9yYXRpb24gd2hpbGUgd2UncmUgYWN0aXZlCgogIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7CiAgICB3aW5kb3cuaGlzdG9yeS5zY3JvbGxSZXN0b3JhdGlvbiA9ICJtYW51YWwiOwogICAgcmV0dXJuICgpID0+IHsKICAgICAgd2luZG93Lmhpc3Rvcnkuc2Nyb2xsUmVzdG9yYXRpb24gPSAiYXV0byI7CiAgICB9OwogIH0sIFtdKTsgLy8gU2F2ZSBwb3NpdGlvbnMgb24gdW5sb2FkCgogIHVzZUJlZm9yZVVubG9hZChSZWFjdC51c2VDYWxsYmFjaygoKSA9PiB7CiAgICBpZiAobmF2aWdhdGlvbi5zdGF0ZSA9PT0gImlkbGUiKSB7CiAgICAgIGxldCBrZXkgPSAoZ2V0S2V5ID8gZ2V0S2V5KGxvY2F0aW9uLCBtYXRjaGVzKSA6IG51bGwpIHx8IGxvY2F0aW9uLmtleTsKICAgICAgc2F2ZWRTY3JvbGxQb3NpdGlvbnNba2V5XSA9IHdpbmRvdy5zY3JvbGxZOwogICAgfQoKICAgIHNlc3Npb25TdG9yYWdlLnNldEl0ZW0oc3RvcmFnZUtleSB8fCBTQ1JPTExfUkVTVE9SQVRJT05fU1RPUkFHRV9LRVksIEpTT04uc3RyaW5naWZ5KHNhdmVkU2Nyb2xsUG9zaXRpb25zKSk7CiAgICB3aW5kb3cuaGlzdG9yeS5zY3JvbGxSZXN0b3JhdGlvbiA9ICJhdXRvIjsKICB9LCBbc3RvcmFnZUtleSwgZ2V0S2V5LCBuYXZpZ2F0aW9uLnN0YXRlLCBsb2NhdGlvbiwgbWF0Y2hlc10pKTsgLy8gUmVhZCBpbiBhbnkgc2F2ZWQgc2Nyb2xsIGxvY2F0aW9ucwoKICBSZWFjdC51c2VMYXlvdXRFZmZlY3QoKCkgPT4gewogICAgdHJ5IHsKICAgICAgbGV0IHNlc3Npb25Qb3NpdGlvbnMgPSBzZXNzaW9uU3RvcmFnZS5nZXRJdGVtKHN0b3JhZ2VLZXkgfHwgU0NST0xMX1JFU1RPUkFUSU9OX1NUT1JBR0VfS0VZKTsKCiAgICAgIGlmIChzZXNzaW9uUG9zaXRpb25zKSB7CiAgICAgICAgc2F2ZWRTY3JvbGxQb3NpdGlvbnMgPSBKU09OLnBhcnNlKHNlc3Npb25Qb3NpdGlvbnMpOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7Ly8gbm8tb3AsIHVzZSBkZWZhdWx0IGVtcHR5IG9iamVjdAogICAgfQogIH0sIFtzdG9yYWdlS2V5XSk7IC8vIEVuYWJsZSBzY3JvbGwgcmVzdG9yYXRpb24gaW4gdGhlIHJvdXRlcgoKICBSZWFjdC51c2VMYXlvdXRFZmZlY3QoKCkgPT4gewogICAgbGV0IGRpc2FibGVTY3JvbGxSZXN0b3JhdGlvbiA9IHJvdXRlciA9PSBudWxsID8gdm9pZCAwIDogcm91dGVyLmVuYWJsZVNjcm9sbFJlc3RvcmF0aW9uKHNhdmVkU2Nyb2xsUG9zaXRpb25zLCAoKSA9PiB3aW5kb3cuc2Nyb2xsWSwgZ2V0S2V5KTsKICAgIHJldHVybiAoKSA9PiBkaXNhYmxlU2Nyb2xsUmVzdG9yYXRpb24gJiYgZGlzYWJsZVNjcm9sbFJlc3RvcmF0aW9uKCk7CiAgfSwgW3JvdXRlciwgZ2V0S2V5XSk7IC8vIFJlc3RvcmUgc2Nyb2xsaW5nIHdoZW4gc3RhdGUucmVzdG9yZVNjcm9sbFBvc2l0aW9uIGNoYW5nZXMKCiAgUmVhY3QudXNlTGF5b3V0RWZmZWN0KCgpID0+IHsKICAgIC8vIEV4cGxpY2l0IGZhbHNlIG1lYW5zIGRvbid0IGRvIGFueXRoaW5nICh1c2VkIGZvciBzdWJtaXNzaW9ucykKICAgIGlmIChyZXN0b3JlU2Nyb2xsUG9zaXRpb24gPT09IGZhbHNlKSB7CiAgICAgIHJldHVybjsKICAgIH0gLy8gYmVlbiBoZXJlIGJlZm9yZSwgc2Nyb2xsIHRvIGl0CgoKICAgIGlmICh0eXBlb2YgcmVzdG9yZVNjcm9sbFBvc2l0aW9uID09PSAibnVtYmVyIikgewogICAgICB3aW5kb3cuc2Nyb2xsVG8oMCwgcmVzdG9yZVNjcm9sbFBvc2l0aW9uKTsKICAgICAgcmV0dXJuOwogICAgfSAvLyB0cnkgdG8gc2Nyb2xsIHRvIHRoZSBoYXNoCgoKICAgIGlmIChsb2NhdGlvbi5oYXNoKSB7CiAgICAgIGxldCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGxvY2F0aW9uLmhhc2guc2xpY2UoMSkpOwoKICAgICAgaWYgKGVsKSB7CiAgICAgICAgZWwuc2Nyb2xsSW50b1ZpZXcoKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgIH0gLy8gT3B0IG91dCBvZiBzY3JvbGwgcmVzZXQgaWYgdGhpcyBsaW5rIHJlcXVlc3RlZCBpdAoKCiAgICBpZiAocHJldmVudFNjcm9sbFJlc2V0ID09PSB0cnVlKSB7CiAgICAgIHJldHVybjsKICAgIH0gLy8gb3RoZXJ3aXNlIGdvIHRvIHRoZSB0b3Agb24gbmV3IGxvY2F0aW9ucwoKCiAgICB3aW5kb3cuc2Nyb2xsVG8oMCwgMCk7CiAgfSwgW2xvY2F0aW9uLCByZXN0b3JlU2Nyb2xsUG9zaXRpb24sIHByZXZlbnRTY3JvbGxSZXNldF0pOwp9CgpmdW5jdGlvbiB1c2VCZWZvcmVVbmxvYWQoY2FsbGJhY2spIHsKICBSZWFjdC51c2VFZmZlY3QoKCkgPT4gewogICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoImJlZm9yZXVubG9hZCIsIGNhbGxiYWNrKTsKICAgIHJldHVybiAoKSA9PiB7CiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCJiZWZvcmV1bmxvYWQiLCBjYWxsYmFjayk7CiAgICB9OwogIH0sIFtjYWxsYmFja10pOwp9IC8vI2VuZHJlZ2lvbgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyNyZWdpb24gVXRpbHMKLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgpmdW5jdGlvbiBkaXN0X3dhcm5pbmcoY29uZCwgbWVzc2FnZSkgewogIGlmICghY29uZCkgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGUKICAgIGlmICh0eXBlb2YgY29uc29sZSAhPT0gInVuZGVmaW5lZCIpIGNvbnNvbGUud2FybihtZXNzYWdlKTsKCiAgICB0cnkgewogICAgICAvLyBXZWxjb21lIHRvIGRlYnVnZ2luZyBSZWFjdCBSb3V0ZXIhCiAgICAgIC8vCiAgICAgIC8vIFRoaXMgZXJyb3IgaXMgdGhyb3duIGFzIGEgY29udmVuaWVuY2Ugc28geW91IGNhbiBtb3JlIGVhc2lseQogICAgICAvLyBmaW5kIHRoZSBzb3VyY2UgZm9yIGEgd2FybmluZyB0aGF0IGFwcGVhcnMgaW4gdGhlIGNvbnNvbGUgYnkKICAgICAgLy8gZW5hYmxpbmcgInBhdXNlIG9uIGV4Y2VwdGlvbnMiIGluIHlvdXIgSmF2YVNjcmlwdCBkZWJ1Z2dlci4KICAgICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpOyAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tZW1wdHkKICAgIH0gY2F0Y2ggKGUpIHt9CiAgfQp9IC8vI2VuZHJlZ2lvbgoKCi8vIyBzb3VyY2VNYXBwaW5nVVJMPWluZGV4LmpzLm1hcAoKOy8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3JlbGF5L2NvbXBvbmVudHMvTGF5b3V0LnRzeAoKCgoKdmFyIExheW91dCA9IGZ1bmN0aW9uIExheW91dChfcmVmKSB7dmFyIHBhZ2VzID0gX3JlZi5wYWdlczsKICB2YXIgb3V0bGV0ID0gLyojX19QVVJFX18qLwogIHJlYWN0LmNyZWF0ZUVsZW1lbnQocmVhY3QuRnJhZ21lbnQsIG51bGwsIC8qI19fUFVSRV9fKi8KICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7IGlkOiAibm90aWZpY2F0aW9uc0NvbnRhaW5lciIgfSksIC8qI19fUFVSRV9fKi8KICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7IGlkOiAicmVsYXlDb250YWluZXIiIH0sIC8qI19fUFVSRV9fKi8KICByZWFjdC5jcmVhdGVFbGVtZW50KE91dGxldCwgbnVsbCkpKTsKCgoKCiAgaWYgKHBhZ2VzLmxlbmd0aCA8PSAxKSB7CiAgICByZXR1cm4gb3V0bGV0OwogIH0KCiAgcmV0dXJuIC8qI19fUFVSRV9fKi8oCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KHJlYWN0LkZyYWdtZW50LCBudWxsLAogICAgIiAiLCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJuYXYiLCBudWxsLCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7IGNsYXNzTmFtZTogInRvcEJhciIgfSwKICAgICIgIiwKICAgIHBhZ2VzLm1hcChmdW5jdGlvbiAocCwgaW5kZXgpIHtyZXR1cm4gLyojX19QVVJFX18qLygKICAgICAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7IGtleTogaW5kZXgsIGNsYXNzTmFtZTogInRhYkVudHJ5IiB9LCAvKiNfX1BVUkVfXyovCiAgICAgICAgcmVhY3QuY3JlYXRlRWxlbWVudChOYXZMaW5rLCB7IHRvOiAiLyIgKyBwLmZpbGUgfSwgcC5wYWdlKSwgIiAiKSk7fSkpKSwKCgoKICAgICIgIiwKICAgIG91dGxldCkpOwoKCn07CgovKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIGNvbnN0IGNvbXBvbmVudHNfTGF5b3V0ID0gKExheW91dCk7CjsvLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZWxheS9hcGkvQXBpUmVxdWVzdC50c3gKZnVuY3Rpb24gX3R5cGVvZihvYmopIHsiQGJhYmVsL2hlbHBlcnMgLSB0eXBlb2YiO3JldHVybiBfdHlwZW9mID0gImZ1bmN0aW9uIiA9PSB0eXBlb2YgU3ltYm9sICYmICJzeW1ib2wiID09IHR5cGVvZiBTeW1ib2wuaXRlcmF0b3IgPyBmdW5jdGlvbiAob2JqKSB7cmV0dXJuIHR5cGVvZiBvYmo7fSA6IGZ1bmN0aW9uIChvYmopIHtyZXR1cm4gb2JqICYmICJmdW5jdGlvbiIgPT0gdHlwZW9mIFN5bWJvbCAmJiBvYmouY29uc3RydWN0b3IgPT09IFN5bWJvbCAmJiBvYmogIT09IFN5bWJvbC5wcm90b3R5cGUgPyAic3ltYm9sIiA6IHR5cGVvZiBvYmo7fSwgX3R5cGVvZihvYmopO31mdW5jdGlvbiBfcmVnZW5lcmF0b3JSdW50aW1lKCkgeyJ1c2Ugc3RyaWN0IjsKLyohIHJlZ2VuZXJhdG9yLXJ1bnRpbWUgLS0gQ29weXJpZ2h0IChjKSAyMDE0LXByZXNlbnQsIEZhY2Vib29rLCBJbmMuIC0tIGxpY2Vuc2UgKE1JVCk6IGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9vay9yZWdlbmVyYXRvci9ibG9iL21haW4vTElDRU5TRSAqL19yZWdlbmVyYXRvclJ1bnRpbWUgPSBmdW5jdGlvbiBfcmVnZW5lcmF0b3JSdW50aW1lKCkge3JldHVybiBleHBvcnRzO307dmFyIGV4cG9ydHMgPSB7fSxPcCA9IE9iamVjdC5wcm90b3R5cGUsaGFzT3duID0gT3AuaGFzT3duUHJvcGVydHksZGVmaW5lUHJvcGVydHkgPSBPYmplY3QuZGVmaW5lUHJvcGVydHkgfHwgZnVuY3Rpb24gKG9iaiwga2V5LCBkZXNjKSB7b2JqW2tleV0gPSBkZXNjLnZhbHVlO30sJFN5bWJvbCA9ICJmdW5jdGlvbiIgPT0gdHlwZW9mIFN5bWJvbCA/IFN5bWJvbCA6IHt9LGl0ZXJhdG9yU3ltYm9sID0gJFN5bWJvbC5pdGVyYXRvciB8fCAiQEBpdGVyYXRvciIsYXN5bmNJdGVyYXRvclN5bWJvbCA9ICRTeW1ib2wuYXN5bmNJdGVyYXRvciB8fCAiQEBhc3luY0l0ZXJhdG9yIix0b1N0cmluZ1RhZ1N5bWJvbCA9ICRTeW1ib2wudG9TdHJpbmdUYWcgfHwgIkBAdG9TdHJpbmdUYWciO2Z1bmN0aW9uIGRlZmluZShvYmosIGtleSwgdmFsdWUpIHtyZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwga2V5LCB7IHZhbHVlOiB2YWx1ZSwgZW51bWVyYWJsZTogITAsIGNvbmZpZ3VyYWJsZTogITAsIHdyaXRhYmxlOiAhMCB9KSwgb2JqW2tleV07fXRyeSB7ZGVmaW5lKHt9LCAiIik7fSBjYXRjaCAoZXJyKSB7ZGVmaW5lID0gZnVuY3Rpb24gZGVmaW5lKG9iaiwga2V5LCB2YWx1ZSkge3JldHVybiBvYmpba2V5XSA9IHZhbHVlO307fWZ1bmN0aW9uIHdyYXAoaW5uZXJGbiwgb3V0ZXJGbiwgc2VsZiwgdHJ5TG9jc0xpc3QpIHt2YXIgcHJvdG9HZW5lcmF0b3IgPSBvdXRlckZuICYmIG91dGVyRm4ucHJvdG90eXBlIGluc3RhbmNlb2YgR2VuZXJhdG9yID8gb3V0ZXJGbiA6IEdlbmVyYXRvcixnZW5lcmF0b3IgPSBPYmplY3QuY3JlYXRlKHByb3RvR2VuZXJhdG9yLnByb3RvdHlwZSksY29udGV4dCA9IG5ldyBDb250ZXh0KHRyeUxvY3NMaXN0IHx8IFtdKTtyZXR1cm4gZGVmaW5lUHJvcGVydHkoZ2VuZXJhdG9yLCAiX2ludm9rZSIsIHsgdmFsdWU6IG1ha2VJbnZva2VNZXRob2QoaW5uZXJGbiwgc2VsZiwgY29udGV4dCkgfSksIGdlbmVyYXRvcjt9ZnVuY3Rpb24gdHJ5Q2F0Y2goZm4sIG9iaiwgYXJnKSB7dHJ5IHtyZXR1cm4geyB0eXBlOiAibm9ybWFsIiwgYXJnOiBmbi5jYWxsKG9iaiwgYXJnKSB9O30gY2F0Y2ggKGVycikge3JldHVybiB7IHR5cGU6ICJ0aHJvdyIsIGFyZzogZXJyIH07fX1leHBvcnRzLndyYXAgPSB3cmFwO3ZhciBDb250aW51ZVNlbnRpbmVsID0ge307ZnVuY3Rpb24gR2VuZXJhdG9yKCkge31mdW5jdGlvbiBHZW5lcmF0b3JGdW5jdGlvbigpIHt9ZnVuY3Rpb24gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUoKSB7fXZhciBJdGVyYXRvclByb3RvdHlwZSA9IHt9O2RlZmluZShJdGVyYXRvclByb3RvdHlwZSwgaXRlcmF0b3JTeW1ib2wsIGZ1bmN0aW9uICgpIHtyZXR1cm4gdGhpczt9KTt2YXIgZ2V0UHJvdG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YsTmF0aXZlSXRlcmF0b3JQcm90b3R5cGUgPSBnZXRQcm90byAmJiBnZXRQcm90byhnZXRQcm90byh2YWx1ZXMoW10pKSk7TmF0aXZlSXRlcmF0b3JQcm90b3R5cGUgJiYgTmF0aXZlSXRlcmF0b3JQcm90b3R5cGUgIT09IE9wICYmIGhhc093bi5jYWxsKE5hdGl2ZUl0ZXJhdG9yUHJvdG90eXBlLCBpdGVyYXRvclN5bWJvbCkgJiYgKEl0ZXJhdG9yUHJvdG90eXBlID0gTmF0aXZlSXRlcmF0b3JQcm90b3R5cGUpO3ZhciBHcCA9IEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlLnByb3RvdHlwZSA9IEdlbmVyYXRvci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKEl0ZXJhdG9yUHJvdG90eXBlKTtmdW5jdGlvbiBkZWZpbmVJdGVyYXRvck1ldGhvZHMocHJvdG90eXBlKSB7WyJuZXh0IiwgInRocm93IiwgInJldHVybiJdLmZvckVhY2goZnVuY3Rpb24gKG1ldGhvZCkge2RlZmluZShwcm90b3R5cGUsIG1ldGhvZCwgZnVuY3Rpb24gKGFyZykge3JldHVybiB0aGlzLl9pbnZva2UobWV0aG9kLCBhcmcpO30pO30pO31mdW5jdGlvbiBBc3luY0l0ZXJhdG9yKGdlbmVyYXRvciwgUHJvbWlzZUltcGwpIHtmdW5jdGlvbiBpbnZva2UobWV0aG9kLCBhcmcsIHJlc29sdmUsIHJlamVjdCkge3ZhciByZWNvcmQgPSB0cnlDYXRjaChnZW5lcmF0b3JbbWV0aG9kXSwgZ2VuZXJhdG9yLCBhcmcpO2lmICgidGhyb3ciICE9PSByZWNvcmQudHlwZSkge3ZhciByZXN1bHQgPSByZWNvcmQuYXJnLHZhbHVlID0gcmVzdWx0LnZhbHVlO3JldHVybiB2YWx1ZSAmJiAib2JqZWN0IiA9PSBfdHlwZW9mKHZhbHVlKSAmJiBoYXNPd24uY2FsbCh2YWx1ZSwgIl9fYXdhaXQiKSA/IFByb21pc2VJbXBsLnJlc29sdmUodmFsdWUuX19hd2FpdCkudGhlbihmdW5jdGlvbiAodmFsdWUpIHtpbnZva2UoIm5leHQiLCB2YWx1ZSwgcmVzb2x2ZSwgcmVqZWN0KTt9LCBmdW5jdGlvbiAoZXJyKSB7aW52b2tlKCJ0aHJvdyIsIGVyciwgcmVzb2x2ZSwgcmVqZWN0KTt9KSA6IFByb21pc2VJbXBsLnJlc29sdmUodmFsdWUpLnRoZW4oZnVuY3Rpb24gKHVud3JhcHBlZCkge3Jlc3VsdC52YWx1ZSA9IHVud3JhcHBlZCwgcmVzb2x2ZShyZXN1bHQpO30sIGZ1bmN0aW9uIChlcnJvcikge3JldHVybiBpbnZva2UoInRocm93IiwgZXJyb3IsIHJlc29sdmUsIHJlamVjdCk7fSk7fXJlamVjdChyZWNvcmQuYXJnKTt9dmFyIHByZXZpb3VzUHJvbWlzZTtkZWZpbmVQcm9wZXJ0eSh0aGlzLCAiX2ludm9rZSIsIHsgdmFsdWU6IGZ1bmN0aW9uIHZhbHVlKG1ldGhvZCwgYXJnKSB7ZnVuY3Rpb24gY2FsbEludm9rZVdpdGhNZXRob2RBbmRBcmcoKSB7cmV0dXJuIG5ldyBQcm9taXNlSW1wbChmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7aW52b2tlKG1ldGhvZCwgYXJnLCByZXNvbHZlLCByZWplY3QpO30pO31yZXR1cm4gcHJldmlvdXNQcm9taXNlID0gcHJldmlvdXNQcm9taXNlID8gcHJldmlvdXNQcm9taXNlLnRoZW4oY2FsbEludm9rZVdpdGhNZXRob2RBbmRBcmcsIGNhbGxJbnZva2VXaXRoTWV0aG9kQW5kQXJnKSA6IGNhbGxJbnZva2VXaXRoTWV0aG9kQW5kQXJnKCk7fSB9KTt9ZnVuY3Rpb24gbWFrZUludm9rZU1ldGhvZChpbm5lckZuLCBzZWxmLCBjb250ZXh0KSB7dmFyIHN0YXRlID0gInN1c3BlbmRlZFN0YXJ0IjtyZXR1cm4gZnVuY3Rpb24gKG1ldGhvZCwgYXJnKSB7aWYgKCJleGVjdXRpbmciID09PSBzdGF0ZSkgdGhyb3cgbmV3IEVycm9yKCJHZW5lcmF0b3IgaXMgYWxyZWFkeSBydW5uaW5nIik7aWYgKCJjb21wbGV0ZWQiID09PSBzdGF0ZSkge2lmICgidGhyb3ciID09PSBtZXRob2QpIHRocm93IGFyZztyZXR1cm4gZG9uZVJlc3VsdCgpO31mb3IgKGNvbnRleHQubWV0aG9kID0gbWV0aG9kLCBjb250ZXh0LmFyZyA9IGFyZzs7KSB7dmFyIGRlbGVnYXRlID0gY29udGV4dC5kZWxlZ2F0ZTtpZiAoZGVsZWdhdGUpIHt2YXIgZGVsZWdhdGVSZXN1bHQgPSBtYXliZUludm9rZURlbGVnYXRlKGRlbGVnYXRlLCBjb250ZXh0KTtpZiAoZGVsZWdhdGVSZXN1bHQpIHtpZiAoZGVsZWdhdGVSZXN1bHQgPT09IENvbnRpbnVlU2VudGluZWwpIGNvbnRpbnVlO3JldHVybiBkZWxlZ2F0ZVJlc3VsdDt9fWlmICgibmV4dCIgPT09IGNvbnRleHQubWV0aG9kKSBjb250ZXh0LnNlbnQgPSBjb250ZXh0Ll9zZW50ID0gY29udGV4dC5hcmc7ZWxzZSBpZiAoInRocm93IiA9PT0gY29udGV4dC5tZXRob2QpIHtpZiAoInN1c3BlbmRlZFN0YXJ0IiA9PT0gc3RhdGUpIHRocm93IHN0YXRlID0gImNvbXBsZXRlZCIsIGNvbnRleHQuYXJnO2NvbnRleHQuZGlzcGF0Y2hFeGNlcHRpb24oY29udGV4dC5hcmcpO30gZWxzZSAicmV0dXJuIiA9PT0gY29udGV4dC5tZXRob2QgJiYgY29udGV4dC5hYnJ1cHQoInJldHVybiIsIGNvbnRleHQuYXJnKTtzdGF0ZSA9ICJleGVjdXRpbmciO3ZhciByZWNvcmQgPSB0cnlDYXRjaChpbm5lckZuLCBzZWxmLCBjb250ZXh0KTtpZiAoIm5vcm1hbCIgPT09IHJlY29yZC50eXBlKSB7aWYgKHN0YXRlID0gY29udGV4dC5kb25lID8gImNvbXBsZXRlZCIgOiAic3VzcGVuZGVkWWllbGQiLCByZWNvcmQuYXJnID09PSBDb250aW51ZVNlbnRpbmVsKSBjb250aW51ZTtyZXR1cm4geyB2YWx1ZTogcmVjb3JkLmFyZywgZG9uZTogY29udGV4dC5kb25lIH07fSJ0aHJvdyIgPT09IHJlY29yZC50eXBlICYmIChzdGF0ZSA9ICJjb21wbGV0ZWQiLCBjb250ZXh0Lm1ldGhvZCA9ICJ0aHJvdyIsIGNvbnRleHQuYXJnID0gcmVjb3JkLmFyZyk7fX07fWZ1bmN0aW9uIG1heWJlSW52b2tlRGVsZWdhdGUoZGVsZWdhdGUsIGNvbnRleHQpIHt2YXIgbWV0aG9kID0gZGVsZWdhdGUuaXRlcmF0b3JbY29udGV4dC5tZXRob2RdO2lmICh1bmRlZmluZWQgPT09IG1ldGhvZCkge2lmIChjb250ZXh0LmRlbGVnYXRlID0gbnVsbCwgInRocm93IiA9PT0gY29udGV4dC5tZXRob2QpIHtpZiAoZGVsZWdhdGUuaXRlcmF0b3JbInJldHVybiJdICYmIChjb250ZXh0Lm1ldGhvZCA9ICJyZXR1cm4iLCBjb250ZXh0LmFyZyA9IHVuZGVmaW5lZCwgbWF5YmVJbnZva2VEZWxlZ2F0ZShkZWxlZ2F0ZSwgY29udGV4dCksICJ0aHJvdyIgPT09IGNvbnRleHQubWV0aG9kKSkgcmV0dXJuIENvbnRpbnVlU2VudGluZWw7Y29udGV4dC5tZXRob2QgPSAidGhyb3ciLCBjb250ZXh0LmFyZyA9IG5ldyBUeXBlRXJyb3IoIlRoZSBpdGVyYXRvciBkb2VzIG5vdCBwcm92aWRlIGEgJ3Rocm93JyBtZXRob2QiKTt9cmV0dXJuIENvbnRpbnVlU2VudGluZWw7fXZhciByZWNvcmQgPSB0cnlDYXRjaChtZXRob2QsIGRlbGVnYXRlLml0ZXJhdG9yLCBjb250ZXh0LmFyZyk7aWYgKCJ0aHJvdyIgPT09IHJlY29yZC50eXBlKSByZXR1cm4gY29udGV4dC5tZXRob2QgPSAidGhyb3ciLCBjb250ZXh0LmFyZyA9IHJlY29yZC5hcmcsIGNvbnRleHQuZGVsZWdhdGUgPSBudWxsLCBDb250aW51ZVNlbnRpbmVsO3ZhciBpbmZvID0gcmVjb3JkLmFyZztyZXR1cm4gaW5mbyA/IGluZm8uZG9uZSA/IChjb250ZXh0W2RlbGVnYXRlLnJlc3VsdE5hbWVdID0gaW5mby52YWx1ZSwgY29udGV4dC5uZXh0ID0gZGVsZWdhdGUubmV4dExvYywgInJldHVybiIgIT09IGNvbnRleHQubWV0aG9kICYmIChjb250ZXh0Lm1ldGhvZCA9ICJuZXh0IiwgY29udGV4dC5hcmcgPSB1bmRlZmluZWQpLCBjb250ZXh0LmRlbGVnYXRlID0gbnVsbCwgQ29udGludWVTZW50aW5lbCkgOiBpbmZvIDogKGNvbnRleHQubWV0aG9kID0gInRocm93IiwgY29udGV4dC5hcmcgPSBuZXcgVHlwZUVycm9yKCJpdGVyYXRvciByZXN1bHQgaXMgbm90IGFuIG9iamVjdCIpLCBjb250ZXh0LmRlbGVnYXRlID0gbnVsbCwgQ29udGludWVTZW50aW5lbCk7fWZ1bmN0aW9uIHB1c2hUcnlFbnRyeShsb2NzKSB7dmFyIGVudHJ5ID0geyB0cnlMb2M6IGxvY3NbMF0gfTsxIGluIGxvY3MgJiYgKGVudHJ5LmNhdGNoTG9jID0gbG9jc1sxXSksIDIgaW4gbG9jcyAmJiAoZW50cnkuZmluYWxseUxvYyA9IGxvY3NbMl0sIGVudHJ5LmFmdGVyTG9jID0gbG9jc1szXSksIHRoaXMudHJ5RW50cmllcy5wdXNoKGVudHJ5KTt9ZnVuY3Rpb24gcmVzZXRUcnlFbnRyeShlbnRyeSkge3ZhciByZWNvcmQgPSBlbnRyeS5jb21wbGV0aW9uIHx8IHt9O3JlY29yZC50eXBlID0gIm5vcm1hbCIsIGRlbGV0ZSByZWNvcmQuYXJnLCBlbnRyeS5jb21wbGV0aW9uID0gcmVjb3JkO31mdW5jdGlvbiBDb250ZXh0KHRyeUxvY3NMaXN0KSB7dGhpcy50cnlFbnRyaWVzID0gW3sgdHJ5TG9jOiAicm9vdCIgfV0sIHRyeUxvY3NMaXN0LmZvckVhY2gocHVzaFRyeUVudHJ5LCB0aGlzKSwgdGhpcy5yZXNldCghMCk7fWZ1bmN0aW9uIHZhbHVlcyhpdGVyYWJsZSkge2lmIChpdGVyYWJsZSkge3ZhciBpdGVyYXRvck1ldGhvZCA9IGl0ZXJhYmxlW2l0ZXJhdG9yU3ltYm9sXTtpZiAoaXRlcmF0b3JNZXRob2QpIHJldHVybiBpdGVyYXRvck1ldGhvZC5jYWxsKGl0ZXJhYmxlKTtpZiAoImZ1bmN0aW9uIiA9PSB0eXBlb2YgaXRlcmFibGUubmV4dCkgcmV0dXJuIGl0ZXJhYmxlO2lmICghaXNOYU4oaXRlcmFibGUubGVuZ3RoKSkge3ZhciBpID0gLTEsbmV4dCA9IGZ1bmN0aW9uIG5leHQoKSB7Zm9yICg7ICsraSA8IGl0ZXJhYmxlLmxlbmd0aDspIHtpZiAoaGFzT3duLmNhbGwoaXRlcmFibGUsIGkpKSByZXR1cm4gbmV4dC52YWx1ZSA9IGl0ZXJhYmxlW2ldLCBuZXh0LmRvbmUgPSAhMSwgbmV4dDt9cmV0dXJuIG5leHQudmFsdWUgPSB1bmRlZmluZWQsIG5leHQuZG9uZSA9ICEwLCBuZXh0O307cmV0dXJuIG5leHQubmV4dCA9IG5leHQ7fX1yZXR1cm4geyBuZXh0OiBkb25lUmVzdWx0IH07fWZ1bmN0aW9uIGRvbmVSZXN1bHQoKSB7cmV0dXJuIHsgdmFsdWU6IHVuZGVmaW5lZCwgZG9uZTogITAgfTt9cmV0dXJuIEdlbmVyYXRvckZ1bmN0aW9uLnByb3RvdHlwZSA9IEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlLCBkZWZpbmVQcm9wZXJ0eShHcCwgImNvbnN0cnVjdG9yIiwgeyB2YWx1ZTogR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUsIGNvbmZpZ3VyYWJsZTogITAgfSksIGRlZmluZVByb3BlcnR5KEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlLCAiY29uc3RydWN0b3IiLCB7IHZhbHVlOiBHZW5lcmF0b3JGdW5jdGlvbiwgY29uZmlndXJhYmxlOiAhMCB9KSwgR2VuZXJhdG9yRnVuY3Rpb24uZGlzcGxheU5hbWUgPSBkZWZpbmUoR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUsIHRvU3RyaW5nVGFnU3ltYm9sLCAiR2VuZXJhdG9yRnVuY3Rpb24iKSwgZXhwb3J0cy5pc0dlbmVyYXRvckZ1bmN0aW9uID0gZnVuY3Rpb24gKGdlbkZ1bikge3ZhciBjdG9yID0gImZ1bmN0aW9uIiA9PSB0eXBlb2YgZ2VuRnVuICYmIGdlbkZ1bi5jb25zdHJ1Y3RvcjtyZXR1cm4gISFjdG9yICYmIChjdG9yID09PSBHZW5lcmF0b3JGdW5jdGlvbiB8fCAiR2VuZXJhdG9yRnVuY3Rpb24iID09PSAoY3Rvci5kaXNwbGF5TmFtZSB8fCBjdG9yLm5hbWUpKTt9LCBleHBvcnRzLm1hcmsgPSBmdW5jdGlvbiAoZ2VuRnVuKSB7cmV0dXJuIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihnZW5GdW4sIEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlKSA6IChnZW5GdW4uX19wcm90b19fID0gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUsIGRlZmluZShnZW5GdW4sIHRvU3RyaW5nVGFnU3ltYm9sLCAiR2VuZXJhdG9yRnVuY3Rpb24iKSksIGdlbkZ1bi5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKEdwKSwgZ2VuRnVuO30sIGV4cG9ydHMuYXdyYXAgPSBmdW5jdGlvbiAoYXJnKSB7cmV0dXJuIHsgX19hd2FpdDogYXJnIH07fSwgZGVmaW5lSXRlcmF0b3JNZXRob2RzKEFzeW5jSXRlcmF0b3IucHJvdG90eXBlKSwgZGVmaW5lKEFzeW5jSXRlcmF0b3IucHJvdG90eXBlLCBhc3luY0l0ZXJhdG9yU3ltYm9sLCBmdW5jdGlvbiAoKSB7cmV0dXJuIHRoaXM7fSksIGV4cG9ydHMuQXN5bmNJdGVyYXRvciA9IEFzeW5jSXRlcmF0b3IsIGV4cG9ydHMuYXN5bmMgPSBmdW5jdGlvbiAoaW5uZXJGbiwgb3V0ZXJGbiwgc2VsZiwgdHJ5TG9jc0xpc3QsIFByb21pc2VJbXBsKSB7dm9pZCAwID09PSBQcm9taXNlSW1wbCAmJiAoUHJvbWlzZUltcGwgPSBQcm9taXNlKTt2YXIgaXRlciA9IG5ldyBBc3luY0l0ZXJhdG9yKHdyYXAoaW5uZXJGbiwgb3V0ZXJGbiwgc2VsZiwgdHJ5TG9jc0xpc3QpLCBQcm9taXNlSW1wbCk7cmV0dXJuIGV4cG9ydHMuaXNHZW5lcmF0b3JGdW5jdGlvbihvdXRlckZuKSA/IGl0ZXIgOiBpdGVyLm5leHQoKS50aGVuKGZ1bmN0aW9uIChyZXN1bHQpIHtyZXR1cm4gcmVzdWx0LmRvbmUgPyByZXN1bHQudmFsdWUgOiBpdGVyLm5leHQoKTt9KTt9LCBkZWZpbmVJdGVyYXRvck1ldGhvZHMoR3ApLCBkZWZpbmUoR3AsIHRvU3RyaW5nVGFnU3ltYm9sLCAiR2VuZXJhdG9yIiksIGRlZmluZShHcCwgaXRlcmF0b3JTeW1ib2wsIGZ1bmN0aW9uICgpIHtyZXR1cm4gdGhpczt9KSwgZGVmaW5lKEdwLCAidG9TdHJpbmciLCBmdW5jdGlvbiAoKSB7cmV0dXJuICJbb2JqZWN0IEdlbmVyYXRvcl0iO30pLCBleHBvcnRzLmtleXMgPSBmdW5jdGlvbiAodmFsKSB7dmFyIG9iamVjdCA9IE9iamVjdCh2YWwpLGtleXMgPSBbXTtmb3IgKHZhciBrZXkgaW4gb2JqZWN0KSB7a2V5cy5wdXNoKGtleSk7fXJldHVybiBrZXlzLnJldmVyc2UoKSwgZnVuY3Rpb24gbmV4dCgpIHtmb3IgKDsga2V5cy5sZW5ndGg7KSB7dmFyIGtleSA9IGtleXMucG9wKCk7aWYgKGtleSBpbiBvYmplY3QpIHJldHVybiBuZXh0LnZhbHVlID0ga2V5LCBuZXh0LmRvbmUgPSAhMSwgbmV4dDt9cmV0dXJuIG5leHQuZG9uZSA9ICEwLCBuZXh0O307fSwgZXhwb3J0cy52YWx1ZXMgPSB2YWx1ZXMsIENvbnRleHQucHJvdG90eXBlID0geyBjb25zdHJ1Y3RvcjogQ29udGV4dCwgcmVzZXQ6IGZ1bmN0aW9uIHJlc2V0KHNraXBUZW1wUmVzZXQpIHtpZiAodGhpcy5wcmV2ID0gMCwgdGhpcy5uZXh0ID0gMCwgdGhpcy5zZW50ID0gdGhpcy5fc2VudCA9IHVuZGVmaW5lZCwgdGhpcy5kb25lID0gITEsIHRoaXMuZGVsZWdhdGUgPSBudWxsLCB0aGlzLm1ldGhvZCA9ICJuZXh0IiwgdGhpcy5hcmcgPSB1bmRlZmluZWQsIHRoaXMudHJ5RW50cmllcy5mb3JFYWNoKHJlc2V0VHJ5RW50cnkpLCAhc2tpcFRlbXBSZXNldCkgZm9yICh2YXIgbmFtZSBpbiB0aGlzKSB7InQiID09PSBuYW1lLmNoYXJBdCgwKSAmJiBoYXNPd24uY2FsbCh0aGlzLCBuYW1lKSAmJiAhaXNOYU4oK25hbWUuc2xpY2UoMSkpICYmICh0aGlzW25hbWVdID0gdW5kZWZpbmVkKTt9fSwgc3RvcDogZnVuY3Rpb24gc3RvcCgpIHt0aGlzLmRvbmUgPSAhMDt2YXIgcm9vdFJlY29yZCA9IHRoaXMudHJ5RW50cmllc1swXS5jb21wbGV0aW9uO2lmICgidGhyb3ciID09PSByb290UmVjb3JkLnR5cGUpIHRocm93IHJvb3RSZWNvcmQuYXJnO3JldHVybiB0aGlzLnJ2YWw7fSwgZGlzcGF0Y2hFeGNlcHRpb246IGZ1bmN0aW9uIGRpc3BhdGNoRXhjZXB0aW9uKGV4Y2VwdGlvbikge2lmICh0aGlzLmRvbmUpIHRocm93IGV4Y2VwdGlvbjt2YXIgY29udGV4dCA9IHRoaXM7ZnVuY3Rpb24gaGFuZGxlKGxvYywgY2F1Z2h0KSB7cmV0dXJuIHJlY29yZC50eXBlID0gInRocm93IiwgcmVjb3JkLmFyZyA9IGV4Y2VwdGlvbiwgY29udGV4dC5uZXh0ID0gbG9jLCBjYXVnaHQgJiYgKGNvbnRleHQubWV0aG9kID0gIm5leHQiLCBjb250ZXh0LmFyZyA9IHVuZGVmaW5lZCksICEhY2F1Z2h0O31mb3IgKHZhciBpID0gdGhpcy50cnlFbnRyaWVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgLS1pKSB7dmFyIGVudHJ5ID0gdGhpcy50cnlFbnRyaWVzW2ldLHJlY29yZCA9IGVudHJ5LmNvbXBsZXRpb247aWYgKCJyb290IiA9PT0gZW50cnkudHJ5TG9jKSByZXR1cm4gaGFuZGxlKCJlbmQiKTtpZiAoZW50cnkudHJ5TG9jIDw9IHRoaXMucHJldikge3ZhciBoYXNDYXRjaCA9IGhhc093bi5jYWxsKGVudHJ5LCAiY2F0Y2hMb2MiKSxoYXNGaW5hbGx5ID0gaGFzT3duLmNhbGwoZW50cnksICJmaW5hbGx5TG9jIik7aWYgKGhhc0NhdGNoICYmIGhhc0ZpbmFsbHkpIHtpZiAodGhpcy5wcmV2IDwgZW50cnkuY2F0Y2hMb2MpIHJldHVybiBoYW5kbGUoZW50cnkuY2F0Y2hMb2MsICEwKTtpZiAodGhpcy5wcmV2IDwgZW50cnkuZmluYWxseUxvYykgcmV0dXJuIGhhbmRsZShlbnRyeS5maW5hbGx5TG9jKTt9IGVsc2UgaWYgKGhhc0NhdGNoKSB7aWYgKHRoaXMucHJldiA8IGVudHJ5LmNhdGNoTG9jKSByZXR1cm4gaGFuZGxlKGVudHJ5LmNhdGNoTG9jLCAhMCk7fSBlbHNlIHtpZiAoIWhhc0ZpbmFsbHkpIHRocm93IG5ldyBFcnJvcigidHJ5IHN0YXRlbWVudCB3aXRob3V0IGNhdGNoIG9yIGZpbmFsbHkiKTtpZiAodGhpcy5wcmV2IDwgZW50cnkuZmluYWxseUxvYykgcmV0dXJuIGhhbmRsZShlbnRyeS5maW5hbGx5TG9jKTt9fX19LCBhYnJ1cHQ6IGZ1bmN0aW9uIGFicnVwdCh0eXBlLCBhcmcpIHtmb3IgKHZhciBpID0gdGhpcy50cnlFbnRyaWVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgLS1pKSB7dmFyIGVudHJ5ID0gdGhpcy50cnlFbnRyaWVzW2ldO2lmIChlbnRyeS50cnlMb2MgPD0gdGhpcy5wcmV2ICYmIGhhc093bi5jYWxsKGVudHJ5LCAiZmluYWxseUxvYyIpICYmIHRoaXMucHJldiA8IGVudHJ5LmZpbmFsbHlMb2MpIHt2YXIgZmluYWxseUVudHJ5ID0gZW50cnk7YnJlYWs7fX1maW5hbGx5RW50cnkgJiYgKCJicmVhayIgPT09IHR5cGUgfHwgImNvbnRpbnVlIiA9PT0gdHlwZSkgJiYgZmluYWxseUVudHJ5LnRyeUxvYyA8PSBhcmcgJiYgYXJnIDw9IGZpbmFsbHlFbnRyeS5maW5hbGx5TG9jICYmIChmaW5hbGx5RW50cnkgPSBudWxsKTt2YXIgcmVjb3JkID0gZmluYWxseUVudHJ5ID8gZmluYWxseUVudHJ5LmNvbXBsZXRpb24gOiB7fTtyZXR1cm4gcmVjb3JkLnR5cGUgPSB0eXBlLCByZWNvcmQuYXJnID0gYXJnLCBmaW5hbGx5RW50cnkgPyAodGhpcy5tZXRob2QgPSAibmV4dCIsIHRoaXMubmV4dCA9IGZpbmFsbHlFbnRyeS5maW5hbGx5TG9jLCBDb250aW51ZVNlbnRpbmVsKSA6IHRoaXMuY29tcGxldGUocmVjb3JkKTt9LCBjb21wbGV0ZTogZnVuY3Rpb24gY29tcGxldGUocmVjb3JkLCBhZnRlckxvYykge2lmICgidGhyb3ciID09PSByZWNvcmQudHlwZSkgdGhyb3cgcmVjb3JkLmFyZztyZXR1cm4gImJyZWFrIiA9PT0gcmVjb3JkLnR5cGUgfHwgImNvbnRpbnVlIiA9PT0gcmVjb3JkLnR5cGUgPyB0aGlzLm5leHQgPSByZWNvcmQuYXJnIDogInJldHVybiIgPT09IHJlY29yZC50eXBlID8gKHRoaXMucnZhbCA9IHRoaXMuYXJnID0gcmVjb3JkLmFyZywgdGhpcy5tZXRob2QgPSAicmV0dXJuIiwgdGhpcy5uZXh0ID0gImVuZCIpIDogIm5vcm1hbCIgPT09IHJlY29yZC50eXBlICYmIGFmdGVyTG9jICYmICh0aGlzLm5leHQgPSBhZnRlckxvYyksIENvbnRpbnVlU2VudGluZWw7fSwgZmluaXNoOiBmdW5jdGlvbiBmaW5pc2goZmluYWxseUxvYykge2ZvciAodmFyIGkgPSB0aGlzLnRyeUVudHJpZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyAtLWkpIHt2YXIgZW50cnkgPSB0aGlzLnRyeUVudHJpZXNbaV07aWYgKGVudHJ5LmZpbmFsbHlMb2MgPT09IGZpbmFsbHlMb2MpIHJldHVybiB0aGlzLmNvbXBsZXRlKGVudHJ5LmNvbXBsZXRpb24sIGVudHJ5LmFmdGVyTG9jKSwgcmVzZXRUcnlFbnRyeShlbnRyeSksIENvbnRpbnVlU2VudGluZWw7fX0sICJjYXRjaCI6IGZ1bmN0aW9uIF9jYXRjaCh0cnlMb2MpIHtmb3IgKHZhciBpID0gdGhpcy50cnlFbnRyaWVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgLS1pKSB7dmFyIGVudHJ5ID0gdGhpcy50cnlFbnRyaWVzW2ldO2lmIChlbnRyeS50cnlMb2MgPT09IHRyeUxvYykge3ZhciByZWNvcmQgPSBlbnRyeS5jb21wbGV0aW9uO2lmICgidGhyb3ciID09PSByZWNvcmQudHlwZSkge3ZhciB0aHJvd24gPSByZWNvcmQuYXJnO3Jlc2V0VHJ5RW50cnkoZW50cnkpO31yZXR1cm4gdGhyb3duO319dGhyb3cgbmV3IEVycm9yKCJpbGxlZ2FsIGNhdGNoIGF0dGVtcHQiKTt9LCBkZWxlZ2F0ZVlpZWxkOiBmdW5jdGlvbiBkZWxlZ2F0ZVlpZWxkKGl0ZXJhYmxlLCByZXN1bHROYW1lLCBuZXh0TG9jKSB7cmV0dXJuIHRoaXMuZGVsZWdhdGUgPSB7IGl0ZXJhdG9yOiB2YWx1ZXMoaXRlcmFibGUpLCByZXN1bHROYW1lOiByZXN1bHROYW1lLCBuZXh0TG9jOiBuZXh0TG9jIH0sICJuZXh0IiA9PT0gdGhpcy5tZXRob2QgJiYgKHRoaXMuYXJnID0gdW5kZWZpbmVkKSwgQ29udGludWVTZW50aW5lbDt9IH0sIGV4cG9ydHM7fWZ1bmN0aW9uIGFzeW5jR2VuZXJhdG9yU3RlcChnZW4sIHJlc29sdmUsIHJlamVjdCwgX25leHQsIF90aHJvdywga2V5LCBhcmcpIHt0cnkge3ZhciBpbmZvID0gZ2VuW2tleV0oYXJnKTt2YXIgdmFsdWUgPSBpbmZvLnZhbHVlO30gY2F0Y2ggKGVycm9yKSB7cmVqZWN0KGVycm9yKTtyZXR1cm47fWlmIChpbmZvLmRvbmUpIHtyZXNvbHZlKHZhbHVlKTt9IGVsc2Uge1Byb21pc2UucmVzb2x2ZSh2YWx1ZSkudGhlbihfbmV4dCwgX3Rocm93KTt9fWZ1bmN0aW9uIF9hc3luY1RvR2VuZXJhdG9yKGZuKSB7cmV0dXJuIGZ1bmN0aW9uICgpIHt2YXIgc2VsZiA9IHRoaXMsYXJncyA9IGFyZ3VtZW50cztyZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge3ZhciBnZW4gPSBmbi5hcHBseShzZWxmLCBhcmdzKTtmdW5jdGlvbiBfbmV4dCh2YWx1ZSkge2FzeW5jR2VuZXJhdG9yU3RlcChnZW4sIHJlc29sdmUsIHJlamVjdCwgX25leHQsIF90aHJvdywgIm5leHQiLCB2YWx1ZSk7fWZ1bmN0aW9uIF90aHJvdyhlcnIpIHthc3luY0dlbmVyYXRvclN0ZXAoZ2VuLCByZXNvbHZlLCByZWplY3QsIF9uZXh0LCBfdGhyb3csICJ0aHJvdyIsIGVycik7fV9uZXh0KHVuZGVmaW5lZCk7fSk7fTt9CmZ1bmN0aW9uIGFkZE5vdGlmaWNhdGlvbihub3RpZmljYXRpb24pIHsKICB2YXIgZWxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7CiAgZWxlLmNsYXNzTmFtZSA9ICJub3RpZmljYXRpb24iOwogIGVsZS5hZGRFdmVudExpc3RlbmVyKCJhbmltYXRpb25lbmQiLCBmdW5jdGlvbiAoKSB7cmV0dXJuIGVsZS5yZW1vdmUoKTt9KTsKICBlbGUuaW5uZXJUZXh0ID0gbm90aWZpY2F0aW9uOwoKICB2YXIgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoIm5vdGlmaWNhdGlvbnNDb250YWluZXIiKTsKCiAgaWYgKCFjb250YWluZXIpIHsKICAgIHJldHVybjsKICB9CgogIGNvbnRhaW5lci5hcHBlbmRDaGlsZChlbGUpOwp9CgpmdW5jdGlvbiBzYXZlU2V0dGluZ3MoCnByb3BlcnRpZXMpCnsKICByZXR1cm4gc2V0UHJvcGVydGllcygKICBwcm9wZXJ0aWVzLgogIGZpbHRlcihmdW5jdGlvbiAocCkgewogICAgaWYgKHAucHJldmlvdXNWYWx1ZSA9PT0gcC52YWx1ZSkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgcC5wcmV2aW91c1ZhbHVlID0gcC52YWx1ZTsKICAgIHJldHVybiB0cnVlOwogIH0pLgogIG1hcChmdW5jdGlvbiAocHJvcCkge3JldHVybiBbcHJvcC5wcmVmZXJlbmNlLCBwcm9wLnZhbHVlLnRyaW0oKV07fSkpOwoKfQoKZnVuY3Rpb24gc2V0UHJvcGVydGllcygKcHJvcGVydGllcykKewogIHJldHVybiBydW5SZWxheSgic2V0UHJvcGVydGllcyIsIEpTT04uc3RyaW5naWZ5KHByb3BlcnRpZXMpKS50aGVuKGZ1bmN0aW9uICh2YWwpIHtyZXR1cm4gKAogICAgICBKU09OLnBhcnNlKHZhbCkpO30pOwoKfWZ1bmN0aW9uCgpydW5SZWxheShfeCwgX3gyKSB7cmV0dXJuIF9ydW5SZWxheS5hcHBseSh0aGlzLCBhcmd1bWVudHMpO31mdW5jdGlvbiBfcnVuUmVsYXkoKSB7X3J1blJlbGF5ID0gX2FzeW5jVG9HZW5lcmF0b3IoIC8qI19fUFVSRV9fKi9fcmVnZW5lcmF0b3JSdW50aW1lKCkubWFyayhmdW5jdGlvbiBfY2FsbGVlKGZvcm1OYW1lLCBwYXJhbSkge3JldHVybiBfcmVnZW5lcmF0b3JSdW50aW1lKCkud3JhcChmdW5jdGlvbiBfY2FsbGVlJChfY29udGV4dCkge3doaWxlICgxKSB7c3dpdGNoIChfY29udGV4dC5wcmV2ID0gX2NvbnRleHQubmV4dCkge2Nhc2UgMDpyZXR1cm4gX2NvbnRleHQuYWJydXB0KCJyZXR1cm4iLAogICAgICAgICAgICBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSkgewogICAgICAgICAgICAgIHZhciB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTsKICAgICAgICAgICAgICB4aHIucmVzcG9uc2VUeXBlID0gInRleHQiOwogICAgICAgICAgICAgIHhoci5vcGVuKCJQT1NUIiwgZG9jdW1lbnQubG9jYXRpb24uaHJlZiwgdHJ1ZSk7CiAgICAgICAgICAgICAgeGhyLnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsICJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiKTsKICAgICAgICAgICAgICB4aHIuc2VuZChmb3JtTmFtZSArICI9IiArIGVuY29kZVVSSUNvbXBvbmVudChwYXJhbSkpOwogICAgICAgICAgICAgIHhoci5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgICAgICBpZiAoeGhyLnJlYWR5U3RhdGUgIT0gNCB8fCB4aHIuc3RhdHVzICE9IDIwMCkgewogICAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgcmVzb2x2ZSh4aHIucmVzcG9uc2VUZXh0KTsKICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9KSk7Y2FzZSAxOmNhc2UgImVuZCI6cmV0dXJuIF9jb250ZXh0LnN0b3AoKTt9fX0sIF9jYWxsZWUpO30pKTtyZXR1cm4gX3J1blJlbGF5LmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7fQo7Ly8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvcmVsYXkvYXBpL1NldHRpbmdWYWxpZGF0b3IudHN4CmZ1bmN0aW9uIF9zbGljZWRUb0FycmF5KGFyciwgaSkge3JldHVybiBfYXJyYXlXaXRoSG9sZXMoYXJyKSB8fCBfaXRlcmFibGVUb0FycmF5TGltaXQoYXJyLCBpKSB8fCBfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkoYXJyLCBpKSB8fCBfbm9uSXRlcmFibGVSZXN0KCk7fWZ1bmN0aW9uIF9ub25JdGVyYWJsZVJlc3QoKSB7dGhyb3cgbmV3IFR5cGVFcnJvcigiSW52YWxpZCBhdHRlbXB0IHRvIGRlc3RydWN0dXJlIG5vbi1pdGVyYWJsZSBpbnN0YW5jZS5cbkluIG9yZGVyIHRvIGJlIGl0ZXJhYmxlLCBub24tYXJyYXkgb2JqZWN0cyBtdXN0IGhhdmUgYSBbU3ltYm9sLml0ZXJhdG9yXSgpIG1ldGhvZC4iKTt9ZnVuY3Rpb24gX2l0ZXJhYmxlVG9BcnJheUxpbWl0KGFyciwgaSkge3ZhciBfaSA9IGFyciA9PSBudWxsID8gbnVsbCA6IHR5cGVvZiBTeW1ib2wgIT09ICJ1bmRlZmluZWQiICYmIGFycltTeW1ib2wuaXRlcmF0b3JdIHx8IGFyclsiQEBpdGVyYXRvciJdO2lmIChfaSA9PSBudWxsKSByZXR1cm47dmFyIF9hcnIgPSBbXTt2YXIgX24gPSB0cnVlO3ZhciBfZCA9IGZhbHNlO3ZhciBfcywgX2U7dHJ5IHtmb3IgKF9pID0gX2kuY2FsbChhcnIpOyAhKF9uID0gKF9zID0gX2kubmV4dCgpKS5kb25lKTsgX24gPSB0cnVlKSB7X2Fyci5wdXNoKF9zLnZhbHVlKTtpZiAoaSAmJiBfYXJyLmxlbmd0aCA9PT0gaSkgYnJlYWs7fX0gY2F0Y2ggKGVycikge19kID0gdHJ1ZTtfZSA9IGVycjt9IGZpbmFsbHkge3RyeSB7aWYgKCFfbiAmJiBfaVsicmV0dXJuIl0gIT0gbnVsbCkgX2lbInJldHVybiJdKCk7fSBmaW5hbGx5IHtpZiAoX2QpIHRocm93IF9lO319cmV0dXJuIF9hcnI7fWZ1bmN0aW9uIF9hcnJheVdpdGhIb2xlcyhhcnIpIHtpZiAoQXJyYXkuaXNBcnJheShhcnIpKSByZXR1cm4gYXJyO31mdW5jdGlvbiBfY3JlYXRlRm9yT2ZJdGVyYXRvckhlbHBlcihvLCBhbGxvd0FycmF5TGlrZSkge3ZhciBpdCA9IHR5cGVvZiBTeW1ib2wgIT09ICJ1bmRlZmluZWQiICYmIG9bU3ltYm9sLml0ZXJhdG9yXSB8fCBvWyJAQGl0ZXJhdG9yIl07aWYgKCFpdCkge2lmIChBcnJheS5pc0FycmF5KG8pIHx8IChpdCA9IF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShvKSkgfHwgYWxsb3dBcnJheUxpa2UgJiYgbyAmJiB0eXBlb2Ygby5sZW5ndGggPT09ICJudW1iZXIiKSB7aWYgKGl0KSBvID0gaXQ7dmFyIGkgPSAwO3ZhciBGID0gZnVuY3Rpb24gRigpIHt9O3JldHVybiB7IHM6IEYsIG46IGZ1bmN0aW9uIG4oKSB7aWYgKGkgPj0gby5sZW5ndGgpIHJldHVybiB7IGRvbmU6IHRydWUgfTtyZXR1cm4geyBkb25lOiBmYWxzZSwgdmFsdWU6IG9baSsrXSB9O30sIGU6IGZ1bmN0aW9uIGUoX2UyKSB7dGhyb3cgX2UyO30sIGY6IEYgfTt9dGhyb3cgbmV3IFR5cGVFcnJvcigiSW52YWxpZCBhdHRlbXB0IHRvIGl0ZXJhdGUgbm9uLWl0ZXJhYmxlIGluc3RhbmNlLlxuSW4gb3JkZXIgdG8gYmUgaXRlcmFibGUsIG5vbi1hcnJheSBvYmplY3RzIG11c3QgaGF2ZSBhIFtTeW1ib2wuaXRlcmF0b3JdKCkgbWV0aG9kLiIpO312YXIgbm9ybWFsQ29tcGxldGlvbiA9IHRydWUsZGlkRXJyID0gZmFsc2UsZXJyO3JldHVybiB7IHM6IGZ1bmN0aW9uIHMoKSB7aXQgPSBpdC5jYWxsKG8pO30sIG46IGZ1bmN0aW9uIG4oKSB7dmFyIHN0ZXAgPSBpdC5uZXh0KCk7bm9ybWFsQ29tcGxldGlvbiA9IHN0ZXAuZG9uZTtyZXR1cm4gc3RlcDt9LCBlOiBmdW5jdGlvbiBlKF9lMykge2RpZEVyciA9IHRydWU7ZXJyID0gX2UzO30sIGY6IGZ1bmN0aW9uIGYoKSB7dHJ5IHtpZiAoIW5vcm1hbENvbXBsZXRpb24gJiYgaXRbInJldHVybiJdICE9IG51bGwpIGl0WyJyZXR1cm4iXSgpO30gZmluYWxseSB7aWYgKGRpZEVycikgdGhyb3cgZXJyO319IH07fWZ1bmN0aW9uIF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShvLCBtaW5MZW4pIHtpZiAoIW8pIHJldHVybjtpZiAodHlwZW9mIG8gPT09ICJzdHJpbmciKSByZXR1cm4gX2FycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTt2YXIgbiA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKS5zbGljZSg4LCAtMSk7aWYgKG4gPT09ICJPYmplY3QiICYmIG8uY29uc3RydWN0b3IpIG4gPSBvLmNvbnN0cnVjdG9yLm5hbWU7aWYgKG4gPT09ICJNYXAiIHx8IG4gPT09ICJTZXQiKSByZXR1cm4gQXJyYXkuZnJvbShvKTtpZiAobiA9PT0gIkFyZ3VtZW50cyIgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QobikpIHJldHVybiBfYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO31mdW5jdGlvbiBfYXJyYXlMaWtlVG9BcnJheShhcnIsIGxlbikge2lmIChsZW4gPT0gbnVsbCB8fCBsZW4gPiBhcnIubGVuZ3RoKSBsZW4gPSBhcnIubGVuZ3RoO2ZvciAodmFyIGkgPSAwLCBhcnIyID0gbmV3IEFycmF5KGxlbik7IGkgPCBsZW47IGkrKykge2FycjJbaV0gPSBhcnJbaV07fXJldHVybiBhcnIyO31mdW5jdGlvbiBfY2xhc3NDYWxsQ2hlY2soaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7aWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHt0aHJvdyBuZXcgVHlwZUVycm9yKCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24iKTt9fWZ1bmN0aW9uIF9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHtmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7dmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTtkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7ZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlO2lmICgidmFsdWUiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTt9fWZ1bmN0aW9uIF9jcmVhdGVDbGFzcyhDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHtpZiAocHJvdG9Qcm9wcykgX2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTtpZiAoc3RhdGljUHJvcHMpIF9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7T2JqZWN0LmRlZmluZVByb3BlcnR5KENvbnN0cnVjdG9yLCAicHJvdG90eXBlIiwgeyB3cml0YWJsZTogZmFsc2UgfSk7cmV0dXJuIENvbnN0cnVjdG9yO31mdW5jdGlvbiBfZGVmaW5lUHJvcGVydHkob2JqLCBrZXksIHZhbHVlKSB7aWYgKGtleSBpbiBvYmopIHtPYmplY3QuZGVmaW5lUHJvcGVydHkob2JqLCBrZXksIHsgdmFsdWU6IHZhbHVlLCBlbnVtZXJhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUsIHdyaXRhYmxlOiB0cnVlIH0pO30gZWxzZSB7b2JqW2tleV0gPSB2YWx1ZTt9cmV0dXJuIG9iajt9dmFyCgoKU2V0dGluZ1ZhbGlkYXRvciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7ZnVuY3Rpb24gU2V0dGluZ1ZhbGlkYXRvcigpIHtfY2xhc3NDYWxsQ2hlY2sodGhpcywgU2V0dGluZ1ZhbGlkYXRvcik7X2RlZmluZVByb3BlcnR5KHRoaXMsICJzZXR0aW5ncyIsCiAgICBbXSk7X2RlZmluZVByb3BlcnR5KHRoaXMsICJvYmplY3QiLAogICAge30pO31fY3JlYXRlQ2xhc3MoU2V0dGluZ1ZhbGlkYXRvciwgW3sga2V5OiAiYWRkU2V0dGluZyIsIHZhbHVlOgoKICAgIGZ1bmN0aW9uIGFkZFNldHRpbmcoc2V0dGluZywgc2V0U3RhdGUpIHsKICAgICAgdGhpcy5zZXR0aW5ncy5wdXNoKFtzZXR0aW5nLCBzZXRTdGF0ZV0pOwogICAgfSB9LCB7IGtleTogInVwZGF0ZVNldHRpbmciLCB2YWx1ZToKCiAgICBmdW5jdGlvbiB1cGRhdGVTZXR0aW5nKHNldHRpbmcpIHsKICAgICAgdGhpcy5vYmplY3Rbc2V0dGluZy5wcmVmZXJlbmNlXSA9IHNldHRpbmcudmFsdWU7CiAgICAgIHRoaXMuZG9WYWxpZGF0ZXMoKTsKICAgIH0gfSwgeyBrZXk6ICJ1cGRhdGVPYmplY3QiLCB2YWx1ZToKCiAgICBmdW5jdGlvbiB1cGRhdGVPYmplY3QoKSB7dmFyIF9pdGVyYXRvciA9IF9jcmVhdGVGb3JPZkl0ZXJhdG9ySGVscGVyKAogICAgICAgIHRoaXMuc2V0dGluZ3MpLF9zdGVwO3RyeSB7Zm9yIChfaXRlcmF0b3IucygpOyAhKF9zdGVwID0gX2l0ZXJhdG9yLm4oKSkuZG9uZTspIHt2YXIgX3N0ZXAkdmFsdWUgPSBfc2xpY2VkVG9BcnJheShfc3RlcC52YWx1ZSwgMSksc2V0dGluZyA9IF9zdGVwJHZhbHVlWzBdOwogICAgICAgICAgdGhpcy5vYmplY3Rbc2V0dGluZy5wcmVmZXJlbmNlXSA9IHNldHRpbmcudmFsdWU7CiAgICAgICAgfX0gY2F0Y2ggKGVycikge19pdGVyYXRvci5lKGVycik7fSBmaW5hbGx5IHtfaXRlcmF0b3IuZigpO30KICAgIH0gfSwgeyBrZXk6ICJkb1ZhbGlkYXRlcyIsIHZhbHVlOgoKICAgIGZ1bmN0aW9uIGRvVmFsaWRhdGVzKCkge3ZhciBfaXRlcmF0b3IyID0gX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIoCiAgICAgICAgdGhpcy5zZXR0aW5ncyksX3N0ZXAyO3RyeSB7Zm9yIChfaXRlcmF0b3IyLnMoKTsgIShfc3RlcDIgPSBfaXRlcmF0b3IyLm4oKSkuZG9uZTspIHt2YXIgX3N0ZXAyJHZhbHVlID0gX3NsaWNlZFRvQXJyYXkoX3N0ZXAyLnZhbHVlLCAyKSxzZXR0aW5nID0gX3N0ZXAyJHZhbHVlWzBdLHNldFZhbGlkID0gX3N0ZXAyJHZhbHVlWzFdOwogICAgICAgICAgc2V0VmFsaWQodGhpcy5pc1ZhbGlkKHNldHRpbmcpKTsKICAgICAgICB9fSBjYXRjaCAoZXJyKSB7X2l0ZXJhdG9yMi5lKGVycik7fSBmaW5hbGx5IHtfaXRlcmF0b3IyLmYoKTt9CiAgICB9IH0sIHsga2V5OiAiaXNWYWxpZCIsIHZhbHVlOgoKICAgIGZ1bmN0aW9uIGlzVmFsaWQoc2V0dGluZykgewogICAgICByZXR1cm4gKAogICAgICAgIHNldHRpbmcudmFsaWRhdGUgPT0gbnVsbCB8fCBzZXR0aW5nLnZhbGlkYXRlKHNldHRpbmcudmFsdWUsIHRoaXMub2JqZWN0KSk7CgogICAgfSB9XSk7cmV0dXJuIFNldHRpbmdWYWxpZGF0b3I7fSgpOwoKCi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gY29uc3QgYXBpX1NldHRpbmdWYWxpZGF0b3IgPSAoU2V0dGluZ1ZhbGlkYXRvcik7CjsvLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZWxheS9jb21wb25lbnRzL0ludGVycnVwdC50c3gKZnVuY3Rpb24gX3RvQ29uc3VtYWJsZUFycmF5KGFycikge3JldHVybiBfYXJyYXlXaXRob3V0SG9sZXMoYXJyKSB8fCBfaXRlcmFibGVUb0FycmF5KGFycikgfHwgSW50ZXJydXB0X3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KGFycikgfHwgX25vbkl0ZXJhYmxlU3ByZWFkKCk7fWZ1bmN0aW9uIF9ub25JdGVyYWJsZVNwcmVhZCgpIHt0aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gc3ByZWFkIG5vbi1pdGVyYWJsZSBpbnN0YW5jZS5cbkluIG9yZGVyIHRvIGJlIGl0ZXJhYmxlLCBub24tYXJyYXkgb2JqZWN0cyBtdXN0IGhhdmUgYSBbU3ltYm9sLml0ZXJhdG9yXSgpIG1ldGhvZC4iKTt9ZnVuY3Rpb24gSW50ZXJydXB0X3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KG8sIG1pbkxlbikge2lmICghbykgcmV0dXJuO2lmICh0eXBlb2YgbyA9PT0gInN0cmluZyIpIHJldHVybiBJbnRlcnJ1cHRfYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO3ZhciBuID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG8pLnNsaWNlKDgsIC0xKTtpZiAobiA9PT0gIk9iamVjdCIgJiYgby5jb25zdHJ1Y3RvcikgbiA9IG8uY29uc3RydWN0b3IubmFtZTtpZiAobiA9PT0gIk1hcCIgfHwgbiA9PT0gIlNldCIpIHJldHVybiBBcnJheS5mcm9tKG8pO2lmIChuID09PSAiQXJndW1lbnRzIiB8fCAvXig/OlVpfEkpbnQoPzo4fDE2fDMyKSg/OkNsYW1wZWQpP0FycmF5JC8udGVzdChuKSkgcmV0dXJuIEludGVycnVwdF9hcnJheUxpa2VUb0FycmF5KG8sIG1pbkxlbik7fWZ1bmN0aW9uIF9pdGVyYWJsZVRvQXJyYXkoaXRlcikge2lmICh0eXBlb2YgU3ltYm9sICE9PSAidW5kZWZpbmVkIiAmJiBpdGVyW1N5bWJvbC5pdGVyYXRvcl0gIT0gbnVsbCB8fCBpdGVyWyJAQGl0ZXJhdG9yIl0gIT0gbnVsbCkgcmV0dXJuIEFycmF5LmZyb20oaXRlcik7fWZ1bmN0aW9uIF9hcnJheVdpdGhvdXRIb2xlcyhhcnIpIHtpZiAoQXJyYXkuaXNBcnJheShhcnIpKSByZXR1cm4gSW50ZXJydXB0X2FycmF5TGlrZVRvQXJyYXkoYXJyKTt9ZnVuY3Rpb24gSW50ZXJydXB0X2FycmF5TGlrZVRvQXJyYXkoYXJyLCBsZW4pIHtpZiAobGVuID09IG51bGwgfHwgbGVuID4gYXJyLmxlbmd0aCkgbGVuID0gYXJyLmxlbmd0aDtmb3IgKHZhciBpID0gMCwgYXJyMiA9IG5ldyBBcnJheShsZW4pOyBpIDwgbGVuOyBpKyspIHthcnIyW2ldID0gYXJyW2ldO31yZXR1cm4gYXJyMjt9ZnVuY3Rpb24gSW50ZXJydXB0X2RlZmluZVByb3BlcnR5KG9iaiwga2V5LCB2YWx1ZSkge2lmIChrZXkgaW4gb2JqKSB7T2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwga2V5LCB7IHZhbHVlOiB2YWx1ZSwgZW51bWVyYWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlLCB3cml0YWJsZTogdHJ1ZSB9KTt9IGVsc2Uge29ialtrZXldID0gdmFsdWU7fXJldHVybiBvYmo7fQoKCgpmdW5jdGlvbiBJbnRlcnJ1cHQoX3JlZikge3ZhciBidXR0b24gPSBfcmVmLmJ1dHRvbjsKICB2YXIgQ1NTc3RyaW5nID0gZnVuY3Rpb24gQ1NTc3RyaW5nKHN0cmluZykgewogICAgdmFyIGNzc19qc29uID0gIntcIiIuY29uY2F0KHN0cmluZy4KICAgIHJlcGxhY2UoLzsgL2csICciLCAiJykuCiAgICByZXBsYWNlKC86IC9nLCAnIjogIicpLgogICAgcmVwbGFjZSgiOyIsICIiKSwgIlwifSIpOwoKICAgIHZhciBvYmogPSBKU09OLnBhcnNlKGNzc19qc29uKTsKCiAgICB2YXIga2V5VmFsdWVzID0gT2JqZWN0LmtleXMob2JqKS5tYXAoZnVuY3Rpb24gKGtleSkgewogICAgICB2YXIgY2FtZWxDYXNlZCA9IGtleS5yZXBsYWNlKC8tW2Etel0vZywgZnVuY3Rpb24gKGcpIHtyZXR1cm4gZ1sxXS50b1VwcGVyQ2FzZSgpO30pOwogICAgICByZXR1cm4gSW50ZXJydXB0X2RlZmluZVByb3BlcnR5KHt9LCBjYW1lbENhc2VkLCBvYmpba2V5XSk7CiAgICB9KTsKCiAgICByZXR1cm4gT2JqZWN0LmFzc2lnbi5hcHBseShPYmplY3QsIFt7fV0uY29uY2F0KF90b0NvbnN1bWFibGVBcnJheShrZXlWYWx1ZXMpKSk7CiAgfTsKCiAgcmV0dXJuIC8qI19fUFVSRV9fKi8oCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJpbnB1dCIsIHsKICAgICAgY2xhc3NOYW1lOiAiaW50ZXJydXB0IiwKICAgICAgc3R5bGU6IGJ1dHRvbi5jc3MgPyBDU1NzdHJpbmcoYnV0dG9uLmNzcykgOiBudWxsLAogICAgICB0eXBlOiAic3VibWl0IiwKICAgICAgdmFsdWU6IGJ1dHRvbi5uYW1lLAogICAgICBvbkNsaWNrOiBmdW5jdGlvbiBvbkNsaWNrKCkgewogICAgICAgIHNldFByb3BlcnRpZXMoCiAgICAgICAgYnV0dG9uLmFjdGlvbnMubWFwKGZ1bmN0aW9uIChfcmVmMykge3ZhciBwcmVmZXJlbmNlID0gX3JlZjMucHJlZmVyZW5jZSx2YWx1ZSA9IF9yZWYzLnZhbHVlO3JldHVybiBbcHJlZmVyZW5jZSwgdmFsdWVdO30pKS4KICAgICAgICB0aGVuKGZ1bmN0aW9uICgpIHtyZXR1cm4gYWRkTm90aWZpY2F0aW9uKGJ1dHRvbi5ub3RpZmljYXRpb24gfHwgIkludGVycnVwdGVkISIpO30pOwogICAgICB9IH0pKTsKCgp9CgovKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIGNvbnN0IGNvbXBvbmVudHNfSW50ZXJydXB0ID0gKEludGVycnVwdCk7CjsvLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZWxheS90eXBlcy9UeXBlcy50c3gKCgp2YXIgUmVsYXlDb21wb25lbnRUeXBlOyhmdW5jdGlvbiAoUmVsYXlDb21wb25lbnRUeXBlKSB7UmVsYXlDb21wb25lbnRUeXBlWyJCT09MRUFOIl0gPSAiYm9vbGVhbiI7UmVsYXlDb21wb25lbnRUeXBlWyJEUk9QRE9XTiJdID0gImRyb3Bkb3duIjtSZWxheUNvbXBvbmVudFR5cGVbIlNUUklORyJdID0gInN0cmluZyI7UmVsYXlDb21wb25lbnRUeXBlWyJIVE1MIl0gPSAiaHRtbCI7UmVsYXlDb21wb25lbnRUeXBlWyJJTlRFUlJVUFQiXSA9ICJpbnRlcnJ1cHQiO30pKFJlbGF5Q29tcG9uZW50VHlwZSB8fCAoUmVsYXlDb21wb25lbnRUeXBlID0ge30pKTsKOy8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3JlbGF5L2NvbXBvbmVudHMvc2V0dGluZ3MvQm9vbGVhbklucHV0LnRzeApmdW5jdGlvbiBCb29sZWFuSW5wdXRfc2xpY2VkVG9BcnJheShhcnIsIGkpIHtyZXR1cm4gQm9vbGVhbklucHV0X2FycmF5V2l0aEhvbGVzKGFycikgfHwgQm9vbGVhbklucHV0X2l0ZXJhYmxlVG9BcnJheUxpbWl0KGFyciwgaSkgfHwgQm9vbGVhbklucHV0X3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KGFyciwgaSkgfHwgQm9vbGVhbklucHV0X25vbkl0ZXJhYmxlUmVzdCgpO31mdW5jdGlvbiBCb29sZWFuSW5wdXRfbm9uSXRlcmFibGVSZXN0KCkge3Rocm93IG5ldyBUeXBlRXJyb3IoIkludmFsaWQgYXR0ZW1wdCB0byBkZXN0cnVjdHVyZSBub24taXRlcmFibGUgaW5zdGFuY2UuXG5JbiBvcmRlciB0byBiZSBpdGVyYWJsZSwgbm9uLWFycmF5IG9iamVjdHMgbXVzdCBoYXZlIGEgW1N5bWJvbC5pdGVyYXRvcl0oKSBtZXRob2QuIik7fWZ1bmN0aW9uIEJvb2xlYW5JbnB1dF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShvLCBtaW5MZW4pIHtpZiAoIW8pIHJldHVybjtpZiAodHlwZW9mIG8gPT09ICJzdHJpbmciKSByZXR1cm4gQm9vbGVhbklucHV0X2FycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTt2YXIgbiA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKS5zbGljZSg4LCAtMSk7aWYgKG4gPT09ICJPYmplY3QiICYmIG8uY29uc3RydWN0b3IpIG4gPSBvLmNvbnN0cnVjdG9yLm5hbWU7aWYgKG4gPT09ICJNYXAiIHx8IG4gPT09ICJTZXQiKSByZXR1cm4gQXJyYXkuZnJvbShvKTtpZiAobiA9PT0gIkFyZ3VtZW50cyIgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QobikpIHJldHVybiBCb29sZWFuSW5wdXRfYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO31mdW5jdGlvbiBCb29sZWFuSW5wdXRfYXJyYXlMaWtlVG9BcnJheShhcnIsIGxlbikge2lmIChsZW4gPT0gbnVsbCB8fCBsZW4gPiBhcnIubGVuZ3RoKSBsZW4gPSBhcnIubGVuZ3RoO2ZvciAodmFyIGkgPSAwLCBhcnIyID0gbmV3IEFycmF5KGxlbik7IGkgPCBsZW47IGkrKykge2FycjJbaV0gPSBhcnJbaV07fXJldHVybiBhcnIyO31mdW5jdGlvbiBCb29sZWFuSW5wdXRfaXRlcmFibGVUb0FycmF5TGltaXQoYXJyLCBpKSB7dmFyIF9pID0gYXJyID09IG51bGwgPyBudWxsIDogdHlwZW9mIFN5bWJvbCAhPT0gInVuZGVmaW5lZCIgJiYgYXJyW1N5bWJvbC5pdGVyYXRvcl0gfHwgYXJyWyJAQGl0ZXJhdG9yIl07aWYgKF9pID09IG51bGwpIHJldHVybjt2YXIgX2FyciA9IFtdO3ZhciBfbiA9IHRydWU7dmFyIF9kID0gZmFsc2U7dmFyIF9zLCBfZTt0cnkge2ZvciAoX2kgPSBfaS5jYWxsKGFycik7ICEoX24gPSAoX3MgPSBfaS5uZXh0KCkpLmRvbmUpOyBfbiA9IHRydWUpIHtfYXJyLnB1c2goX3MudmFsdWUpO2lmIChpICYmIF9hcnIubGVuZ3RoID09PSBpKSBicmVhazt9fSBjYXRjaCAoZXJyKSB7X2QgPSB0cnVlO19lID0gZXJyO30gZmluYWxseSB7dHJ5IHtpZiAoIV9uICYmIF9pWyJyZXR1cm4iXSAhPSBudWxsKSBfaVsicmV0dXJuIl0oKTt9IGZpbmFsbHkge2lmIChfZCkgdGhyb3cgX2U7fX1yZXR1cm4gX2Fycjt9ZnVuY3Rpb24gQm9vbGVhbklucHV0X2FycmF5V2l0aEhvbGVzKGFycikge2lmIChBcnJheS5pc0FycmF5KGFycikpIHJldHVybiBhcnI7fQoKCgpmdW5jdGlvbiBCb29sZWFuSW5wdXQoX3JlZikge3ZhciBidXR0b24gPSBfcmVmLmJ1dHRvbjsKICB2YXIgX3VzZVN0YXRlID0gKDAscmVhY3QudXNlU3RhdGUpKGJ1dHRvbi52YWx1ZSA9PT0gInRydWUiKSxfdXNlU3RhdGUyID0gQm9vbGVhbklucHV0X3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlLCAyKSx2YWx1ZSA9IF91c2VTdGF0ZTJbMF0sc2V0VmFsdWUgPSBfdXNlU3RhdGUyWzFdOwoKICByZXR1cm4gLyojX19QVVJFX18qLygKICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoImxhYmVsIiwgeyBjbGFzc05hbWU6ICJjaGVja2NvbnRhaW5lciIgfSwgLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgiaW5wdXQiLCB7IHR5cGU6ICJoaWRkZW4iLCBuYW1lOiBidXR0b24ubmFtZSwgdmFsdWU6IGJ1dHRvbi52YWx1ZSB9KSwgLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgiZGl2IiwgewogICAgICBjbGFzc05hbWU6ICJ0b2dnbGUtdHJhY2siLAogICAgICBvbkNsaWNrOiBmdW5jdGlvbiBvbkNsaWNrKCkgewogICAgICAgIHNldFZhbHVlKCF2YWx1ZSk7CiAgICAgICAgYnV0dG9uLnNldFZhbHVlKCghdmFsdWUpLnRvU3RyaW5nKCkpOwogICAgICB9IH0sIC8qI19fUFVSRV9fKi8KCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJzcGFuIiwgeyBjbGFzc05hbWU6ICJ0b2dnbGUtaW5kaWNhdG9yIiB9LCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJzcGFuIiwgeyBjbGFzc05hbWU6ICJjaGVja01hcmsiIH0sIC8qI19fUFVSRV9fKi8KICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoInN2ZyIsIHsKICAgICAgdmlld0JveDogIjAgMCAyNCAyNCIsCiAgICAgIGlkOiAiZ2hxLXN2Zy1jaGVjayIsCiAgICAgIHJvbGU6ICJwcmVzZW50YXRpb24iLAogICAgICAiYXJpYS1oaWRkZW4iOiAidHJ1ZSIgfSwgLyojX19QVVJFX18qLwoKICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoInBhdGgiLCB7IGQ6ICJNOS44NiAxOGExIDEgMCAwMS0uNzMtLjMybC00Ljg2LTUuMTdhMS4wMDEgMS4wMDEgMCAwMTEuNDYtMS4zN2w0LjEyIDQuMzkgOC40MS05LjJhMSAxIDAgMTExLjQ4IDEuMzRsLTkuMTQgMTBhMSAxIDAgMDEtLjczLjMzaC0uMDF6IiB9KSkpKSkpKTsKCgoKCgoKfQoKLyoKPGRpdgogIGNsYXNzPSJjaGVja21hcmsiCiAgb25DbGljaz17KCkgPT4gewogICAgc2V0VmFsdWUoIXZhbHVlKTsKICB9fQovPgogKi8KLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBjb25zdCBzZXR0aW5nc19Cb29sZWFuSW5wdXQgPSAoQm9vbGVhbklucHV0KTsKOy8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3JlbGF5L2NvbXBvbmVudHMvc2V0dGluZ3MvRHJvcGRvd25JbnB1dC50c3gKCgoKZnVuY3Rpb24gRHJvcGRvd25JbnB1dChfcmVmKSB7dmFyIGJ1dHRvbiA9IF9yZWYuYnV0dG9uOwogIHJldHVybiAvKiNfX1BVUkVfXyovKAogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgic2VsZWN0IiwgewogICAgICBjbGFzc05hbWU6ICJkcm9wZG93bmNvbnRhaW5lciIsCiAgICAgIG5hbWU6IGJ1dHRvbi5uYW1lLAogICAgICBkZWZhdWx0VmFsdWU6IGJ1dHRvbi52YWx1ZSwKICAgICAgb25DaGFuZ2U6IGZ1bmN0aW9uIG9uQ2hhbmdlKGUpIHtyZXR1cm4gYnV0dG9uLnNldFZhbHVlKGUudGFyZ2V0LnZhbHVlKTt9IH0sCgogICAgYnV0dG9uLmRyb3Bkb3duLm1hcChmdW5jdGlvbiAob3B0aW9uKSB7CiAgICAgIHJldHVybiAvKiNfX1BVUkVfXyovKAogICAgICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoIm9wdGlvbiIsIHsga2V5OiBvcHRpb24udmFsdWUsIHZhbHVlOiBvcHRpb24udmFsdWUgfSwKICAgICAgICBvcHRpb24uZGlzcGxheSB8fCBvcHRpb24udmFsdWUpKTsKCgogICAgfSkpKTsKCgp9CgovKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIGNvbnN0IHNldHRpbmdzX0Ryb3Bkb3duSW5wdXQgPSAoRHJvcGRvd25JbnB1dCk7CjsvLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZWxheS9jb21wb25lbnRzL3NldHRpbmdzL1N0cmluZ0lucHV0LnRzeAoKCgpmdW5jdGlvbiBTdHJpbmdJbnB1dChfcmVmKSB7dmFyIGJ1dHRvbiA9IF9yZWYuYnV0dG9uOwogIHJldHVybiAvKiNfX1BVUkVfXyovKAogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgiaW5wdXQiLCB7CiAgICAgIGNsYXNzTmFtZTogInN0cmluZ2NvbnRhaW5lciIsCiAgICAgIG5hbWU6IGJ1dHRvbi5uYW1lLAogICAgICBkZWZhdWx0VmFsdWU6IGJ1dHRvbi52YWx1ZSwKICAgICAgb25DaGFuZ2U6IGZ1bmN0aW9uIG9uQ2hhbmdlKGUpIHtyZXR1cm4gYnV0dG9uLnNldFZhbHVlKGUudGFyZ2V0LnZhbHVlKTt9IH0pKTsKCgp9CgovKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIGNvbnN0IHNldHRpbmdzX1N0cmluZ0lucHV0ID0gKFN0cmluZ0lucHV0KTsKOy8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3JlbGF5L2NvbXBvbmVudHMvc2V0dGluZ3MvU2V0dGluZy50c3gKZnVuY3Rpb24gU2V0dGluZ19zbGljZWRUb0FycmF5KGFyciwgaSkge3JldHVybiBTZXR0aW5nX2FycmF5V2l0aEhvbGVzKGFycikgfHwgU2V0dGluZ19pdGVyYWJsZVRvQXJyYXlMaW1pdChhcnIsIGkpIHx8IFNldHRpbmdfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkoYXJyLCBpKSB8fCBTZXR0aW5nX25vbkl0ZXJhYmxlUmVzdCgpO31mdW5jdGlvbiBTZXR0aW5nX25vbkl0ZXJhYmxlUmVzdCgpIHt0aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gZGVzdHJ1Y3R1cmUgbm9uLWl0ZXJhYmxlIGluc3RhbmNlLlxuSW4gb3JkZXIgdG8gYmUgaXRlcmFibGUsIG5vbi1hcnJheSBvYmplY3RzIG11c3QgaGF2ZSBhIFtTeW1ib2wuaXRlcmF0b3JdKCkgbWV0aG9kLiIpO31mdW5jdGlvbiBTZXR0aW5nX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KG8sIG1pbkxlbikge2lmICghbykgcmV0dXJuO2lmICh0eXBlb2YgbyA9PT0gInN0cmluZyIpIHJldHVybiBTZXR0aW5nX2FycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTt2YXIgbiA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKS5zbGljZSg4LCAtMSk7aWYgKG4gPT09ICJPYmplY3QiICYmIG8uY29uc3RydWN0b3IpIG4gPSBvLmNvbnN0cnVjdG9yLm5hbWU7aWYgKG4gPT09ICJNYXAiIHx8IG4gPT09ICJTZXQiKSByZXR1cm4gQXJyYXkuZnJvbShvKTtpZiAobiA9PT0gIkFyZ3VtZW50cyIgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QobikpIHJldHVybiBTZXR0aW5nX2FycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTt9ZnVuY3Rpb24gU2V0dGluZ19hcnJheUxpa2VUb0FycmF5KGFyciwgbGVuKSB7aWYgKGxlbiA9PSBudWxsIHx8IGxlbiA+IGFyci5sZW5ndGgpIGxlbiA9IGFyci5sZW5ndGg7Zm9yICh2YXIgaSA9IDAsIGFycjIgPSBuZXcgQXJyYXkobGVuKTsgaSA8IGxlbjsgaSsrKSB7YXJyMltpXSA9IGFycltpXTt9cmV0dXJuIGFycjI7fWZ1bmN0aW9uIFNldHRpbmdfaXRlcmFibGVUb0FycmF5TGltaXQoYXJyLCBpKSB7dmFyIF9pID0gYXJyID09IG51bGwgPyBudWxsIDogdHlwZW9mIFN5bWJvbCAhPT0gInVuZGVmaW5lZCIgJiYgYXJyW1N5bWJvbC5pdGVyYXRvcl0gfHwgYXJyWyJAQGl0ZXJhdG9yIl07aWYgKF9pID09IG51bGwpIHJldHVybjt2YXIgX2FyciA9IFtdO3ZhciBfbiA9IHRydWU7dmFyIF9kID0gZmFsc2U7dmFyIF9zLCBfZTt0cnkge2ZvciAoX2kgPSBfaS5jYWxsKGFycik7ICEoX24gPSAoX3MgPSBfaS5uZXh0KCkpLmRvbmUpOyBfbiA9IHRydWUpIHtfYXJyLnB1c2goX3MudmFsdWUpO2lmIChpICYmIF9hcnIubGVuZ3RoID09PSBpKSBicmVhazt9fSBjYXRjaCAoZXJyKSB7X2QgPSB0cnVlO19lID0gZXJyO30gZmluYWxseSB7dHJ5IHtpZiAoIV9uICYmIF9pWyJyZXR1cm4iXSAhPSBudWxsKSBfaVsicmV0dXJuIl0oKTt9IGZpbmFsbHkge2lmIChfZCkgdGhyb3cgX2U7fX1yZXR1cm4gX2Fycjt9ZnVuY3Rpb24gU2V0dGluZ19hcnJheVdpdGhIb2xlcyhhcnIpIHtpZiAoQXJyYXkuaXNBcnJheShhcnIpKSByZXR1cm4gYXJyO30KCgoKCgoKZnVuY3Rpb24gU2V0dGluZyhfcmVmKQoKCgoKCnt2YXIgYnV0dG9uID0gX3JlZi5idXR0b24sdmFsaWRhdG9yID0gX3JlZi52YWxpZGF0b3I7CiAgdmFyIF91c2VTdGF0ZSA9ICgwLHJlYWN0LnVzZVN0YXRlKSh2YWxpZGF0b3IuaXNWYWxpZChidXR0b24pKSxfdXNlU3RhdGUyID0gU2V0dGluZ19zbGljZWRUb0FycmF5KF91c2VTdGF0ZSwgMiksdmFsaWQgPSBfdXNlU3RhdGUyWzBdLHNldFZhbGlkID0gX3VzZVN0YXRlMlsxXTsKCiAgYnV0dG9uLnNldFZhbHVlID0gZnVuY3Rpb24gKHZhbCkgewogICAgYnV0dG9uLnZhbHVlID0gdmFsOwogICAgdmFsaWRhdG9yLnVwZGF0ZVNldHRpbmcoYnV0dG9uKTsKICB9OwoKICB2YWxpZGF0b3IuYWRkU2V0dGluZyhidXR0b24sIHNldFZhbGlkKTsKCiAgcmV0dXJuIC8qI19fUFVSRV9fKi8oCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJ0ciIsIHsgY2xhc3NOYW1lOiAidXNlclByZWZlcmVuY2UiLCBrZXk6IGJ1dHRvbi5wcmVmZXJlbmNlIH0sIC8qI19fUFVSRV9fKi8KICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoInRkIiwgeyBjbGFzc05hbWU6ICJzZXR0aW5nIiB9LAogICAgYnV0dG9uLm5hbWUsIC8qI19fUFVSRV9fKi8KICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoImRpdiIsIHsgY2xhc3NOYW1lOiAic2V0dGluZ05hbWVIb3ZlciIgfSwgYnV0dG9uLnByZWZlcmVuY2UpKSwgLyojX19QVVJFX18qLwoKICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoInRkIiwgeyBjbGFzc05hbWU6IHZhbGlkID8gInNldHRpbmdJbnB1dCIgOiAic2V0dGluZ0lucHV0IGludmFsaWQtc2V0dGluZyIgfSwKICAgIGJ1dHRvbi50eXBlID09PSBSZWxheUNvbXBvbmVudFR5cGUuQk9PTEVBTiA/IC8qI19fUFVSRV9fKi8KICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoc2V0dGluZ3NfQm9vbGVhbklucHV0LCB7IGJ1dHRvbjogYnV0dG9uIH0pIDoKICAgIGJ1dHRvbi50eXBlID09PSBSZWxheUNvbXBvbmVudFR5cGUuRFJPUERPV04gPyAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KHNldHRpbmdzX0Ryb3Bkb3duSW5wdXQsIHsgYnV0dG9uOiBidXR0b24gfSkgOiAvKiNfX1BVUkVfXyovCgogICAgcmVhY3QuY3JlYXRlRWxlbWVudChzZXR0aW5nc19TdHJpbmdJbnB1dCwgeyBidXR0b246IGJ1dHRvbiB9KSwKCiAgICBidXR0b24uaW52YWxpZFJlYXNvbiAhPSBudWxsID8gLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgiZGl2IiwgeyBjbGFzc05hbWU6ICJpbnZhbGlkLXJlYXNvbiIsIGhpZGRlbjogdmFsaWQgfSwgLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgic21hbGwiLCBudWxsLCBidXR0b24uaW52YWxpZFJlYXNvbikpIDogLyojX19QVVJFX18qLwoKCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KHJlYWN0LkZyYWdtZW50LCBudWxsKSwgLyojX19QVVJFX18qLwoKCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7IGNsYXNzTmFtZTogImhvdmVyQm94IiB9LCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJzbWFsbCIsIHsgY2xhc3NOYW1lOiAic2V0dGluZ0RlZmF1bHRIb3ZlciIgfSwKICAgIGJ1dHRvblsiZGVmYXVsdCJdICE9IG51bGwgPwogICAgIkRlZmF1bHQ6ICIgKyAoCiAgICBidXR0b25bImRlZmF1bHQiXSA9PSAiIiA/ICI8RW1wdHk+IiA6IGJ1dHRvblsiZGVmYXVsdCJdKSA6CiAgICAiRGVmYXVsdCBub3Qgc2V0IikpKSwgLyojX19QVVJFX18qLwoKCgogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgidGQiLCBudWxsLCBidXR0b24uZGVzY3JpcHRpb24pKSk7CgoKfQoKLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBjb25zdCBzZXR0aW5nc19TZXR0aW5nID0gKFNldHRpbmcpOwo7Ly8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvcmVsYXkvcm91dGVzL1JlbGF5UGFnZS50c3gKZnVuY3Rpb24gUmVsYXlQYWdlX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIobywgYWxsb3dBcnJheUxpa2UpIHt2YXIgaXQgPSB0eXBlb2YgU3ltYm9sICE9PSAidW5kZWZpbmVkIiAmJiBvW1N5bWJvbC5pdGVyYXRvcl0gfHwgb1siQEBpdGVyYXRvciJdO2lmICghaXQpIHtpZiAoQXJyYXkuaXNBcnJheShvKSB8fCAoaXQgPSBSZWxheVBhZ2VfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkobykpIHx8IGFsbG93QXJyYXlMaWtlICYmIG8gJiYgdHlwZW9mIG8ubGVuZ3RoID09PSAibnVtYmVyIikge2lmIChpdCkgbyA9IGl0O3ZhciBpID0gMDt2YXIgRiA9IGZ1bmN0aW9uIEYoKSB7fTtyZXR1cm4geyBzOiBGLCBuOiBmdW5jdGlvbiBuKCkge2lmIChpID49IG8ubGVuZ3RoKSByZXR1cm4geyBkb25lOiB0cnVlIH07cmV0dXJuIHsgZG9uZTogZmFsc2UsIHZhbHVlOiBvW2krK10gfTt9LCBlOiBmdW5jdGlvbiBlKF9lKSB7dGhyb3cgX2U7fSwgZjogRiB9O310aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gaXRlcmF0ZSBub24taXRlcmFibGUgaW5zdGFuY2UuXG5JbiBvcmRlciB0byBiZSBpdGVyYWJsZSwgbm9uLWFycmF5IG9iamVjdHMgbXVzdCBoYXZlIGEgW1N5bWJvbC5pdGVyYXRvcl0oKSBtZXRob2QuIik7fXZhciBub3JtYWxDb21wbGV0aW9uID0gdHJ1ZSxkaWRFcnIgPSBmYWxzZSxlcnI7cmV0dXJuIHsgczogZnVuY3Rpb24gcygpIHtpdCA9IGl0LmNhbGwobyk7fSwgbjogZnVuY3Rpb24gbigpIHt2YXIgc3RlcCA9IGl0Lm5leHQoKTtub3JtYWxDb21wbGV0aW9uID0gc3RlcC5kb25lO3JldHVybiBzdGVwO30sIGU6IGZ1bmN0aW9uIGUoX2UyKSB7ZGlkRXJyID0gdHJ1ZTtlcnIgPSBfZTI7fSwgZjogZnVuY3Rpb24gZigpIHt0cnkge2lmICghbm9ybWFsQ29tcGxldGlvbiAmJiBpdFsicmV0dXJuIl0gIT0gbnVsbCkgaXRbInJldHVybiJdKCk7fSBmaW5hbGx5IHtpZiAoZGlkRXJyKSB0aHJvdyBlcnI7fX0gfTt9ZnVuY3Rpb24gUmVsYXlQYWdlX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KG8sIG1pbkxlbikge2lmICghbykgcmV0dXJuO2lmICh0eXBlb2YgbyA9PT0gInN0cmluZyIpIHJldHVybiBSZWxheVBhZ2VfYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO3ZhciBuID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG8pLnNsaWNlKDgsIC0xKTtpZiAobiA9PT0gIk9iamVjdCIgJiYgby5jb25zdHJ1Y3RvcikgbiA9IG8uY29uc3RydWN0b3IubmFtZTtpZiAobiA9PT0gIk1hcCIgfHwgbiA9PT0gIlNldCIpIHJldHVybiBBcnJheS5mcm9tKG8pO2lmIChuID09PSAiQXJndW1lbnRzIiB8fCAvXig/OlVpfEkpbnQoPzo4fDE2fDMyKSg/OkNsYW1wZWQpP0FycmF5JC8udGVzdChuKSkgcmV0dXJuIFJlbGF5UGFnZV9hcnJheUxpa2VUb0FycmF5KG8sIG1pbkxlbik7fWZ1bmN0aW9uIFJlbGF5UGFnZV9hcnJheUxpa2VUb0FycmF5KGFyciwgbGVuKSB7aWYgKGxlbiA9PSBudWxsIHx8IGxlbiA+IGFyci5sZW5ndGgpIGxlbiA9IGFyci5sZW5ndGg7Zm9yICh2YXIgaSA9IDAsIGFycjIgPSBuZXcgQXJyYXkobGVuKTsgaSA8IGxlbjsgaSsrKSB7YXJyMltpXSA9IGFycltpXTt9cmV0dXJuIGFycjI7fQoKCgoKCgpmdW5jdGlvbiBSZWxheVBhZ2UoX3JlZikge3ZhciBwYWdlID0gX3JlZi5wYWdlOwogIHZhciBncm91cHMgPSBbXTsKICB2YXIgY3VycmVudEdyb3VwID0gbnVsbDt2YXIgX2l0ZXJhdG9yID0gUmVsYXlQYWdlX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIoCgogICAgcGFnZS5jb21wb25lbnRzKSxfc3RlcDt0cnkge2ZvciAoX2l0ZXJhdG9yLnMoKTsgIShfc3RlcCA9IF9pdGVyYXRvci5uKCkpLmRvbmU7KSB7dmFyIGNvbXBvbmVudCA9IF9zdGVwLnZhbHVlOwogICAgICBpZiAoCiAgICAgIGNvbXBvbmVudC50eXBlID09IFJlbGF5Q29tcG9uZW50VHlwZS5IVE1MIHx8CiAgICAgIGNvbXBvbmVudC50eXBlID09IFJlbGF5Q29tcG9uZW50VHlwZS5JTlRFUlJVUFQpCiAgICAgIHsKICAgICAgICBncm91cHMucHVzaChjb21wb25lbnQpOwogICAgICAgIGN1cnJlbnRHcm91cCA9IG51bGw7CiAgICAgICAgY29udGludWU7CiAgICAgIH0KCiAgICAgIGlmIChjdXJyZW50R3JvdXAgPT0gbnVsbCkgewogICAgICAgIGN1cnJlbnRHcm91cCA9IFtdOwogICAgICAgIGdyb3Vwcy5wdXNoKGN1cnJlbnRHcm91cCk7CiAgICAgIH0KCiAgICAgIGN1cnJlbnRHcm91cC5wdXNoKGNvbXBvbmVudCk7CiAgICB9fSBjYXRjaCAoZXJyKSB7X2l0ZXJhdG9yLmUoZXJyKTt9IGZpbmFsbHkge19pdGVyYXRvci5mKCk7fQoKICB2YXIgZWxlbWVudHMgPSBbXTsKICB2YXIgdmFsaWRhdG9yID0gbmV3IGFwaV9TZXR0aW5nVmFsaWRhdG9yKCk7CgogIGdyb3Vwcy5mb3JFYWNoKGZ1bmN0aW9uIChjb21wb25lbnRzKSB7CiAgICBpZiAoY29tcG9uZW50cy50eXBlID09IFJlbGF5Q29tcG9uZW50VHlwZS5IVE1MKSB7CiAgICAgIHZhciBodG1sID0gY29tcG9uZW50czsKCiAgICAgIGlmIChodG1sLmRhdGEgPT0gbnVsbCkgewogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgZWxlbWVudHMucHVzaCggLyojX19QVVJFX18qLwogICAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJkaXYiLCB7CiAgICAgICAgZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUw6IHsKICAgICAgICAgIF9faHRtbDogY29tcG9uZW50cy5kYXRhCiAgICAgICAgfSB9KSk7CgoKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmIChjb21wb25lbnRzLnR5cGUgPT0gUmVsYXlDb21wb25lbnRUeXBlLklOVEVSUlVQVCkgewogICAgICBlbGVtZW50cy5wdXNoKCAvKiNfX1BVUkVfXyovCiAgICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoY29tcG9uZW50c19JbnRlcnJ1cHQsIHsgYnV0dG9uOiBjb21wb25lbnRzIH0pKTsKCiAgICAgIHJldHVybjsKICAgIH0KCiAgICB2YXIgYnV0dG9ucyA9IGNvbXBvbmVudHM7CgogICAgZWxlbWVudHMucHVzaCggLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudCgidGFibGUiLCBudWxsLCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJ0Ym9keSIsIG51bGwsCiAgICBidXR0b25zLm1hcChmdW5jdGlvbiAoc2V0dGluZywgaW5kZXgpIHtyZXR1cm4gLyojX19QVVJFX18qLygKICAgICAgICByZWFjdC5jcmVhdGVFbGVtZW50KHNldHRpbmdzX1NldHRpbmcsIHsga2V5OiBpbmRleCwgYnV0dG9uOiBzZXR0aW5nLCB2YWxpZGF0b3I6IHZhbGlkYXRvciB9KSk7fSkpKSk7CgoKCgogIH0pOwoKICB2YWxpZGF0b3IudXBkYXRlT2JqZWN0KCk7CgogIC8vIElmIHdlIGhhdmUgbm8gc2V0dGluZ3MgaW4gd2hpY2ggd2UnZCBzYXZlIHN0dWZmLCBkb24ndCBib3RoZXIgcmVuZGVyaW5nIHNhdmUgYnV0dG9uCiAgaWYgKAogIHBhZ2UuY29tcG9uZW50cy5maW5kKGZ1bmN0aW9uIChjKSB7cmV0dXJuIGMucHJlZmVyZW5jZSAhPSBudWxsO30pID09CiAgbnVsbCkKICB7CiAgICByZXR1cm4gLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQocmVhY3QuRnJhZ21lbnQsIG51bGwsIGVsZW1lbnRzKTsKICB9CgogIHJldHVybiAvKiNfX1BVUkVfXyovKAogICAgcmVhY3QuY3JlYXRlRWxlbWVudChyZWFjdC5GcmFnbWVudCwgbnVsbCwKICAgICIgIiwKICAgIGVsZW1lbnRzLCAiICIsIC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KCJiciIsIG51bGwpLCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KCJpbnB1dCIsIHsKICAgICAgY2xhc3NOYW1lOiAic2F2ZSIsCiAgICAgIG9uQ2xpY2s6IGZ1bmN0aW9uIG9uQ2xpY2soKSB7cmV0dXJuICgKICAgICAgICAgIHNhdmVTZXR0aW5ncygKICAgICAgICAgIHBhZ2UuY29tcG9uZW50cy5maWx0ZXIoCiAgICAgICAgICBmdW5jdGlvbiAoYikge3JldHVybiBiLnByZWZlcmVuY2UgIT0gbnVsbDt9KSkuCgogICAgICAgICAgdGhlbihmdW5jdGlvbiAobm90aWZzKSB7dmFyIF9pdGVyYXRvcjIgPSBSZWxheVBhZ2VfY3JlYXRlRm9yT2ZJdGVyYXRvckhlbHBlcigKICAgICAgICAgICAgICBub3RpZnMpLF9zdGVwMjt0cnkge2ZvciAoX2l0ZXJhdG9yMi5zKCk7ICEoX3N0ZXAyID0gX2l0ZXJhdG9yMi5uKCkpLmRvbmU7KSB7dmFyIG5vdGlmID0gX3N0ZXAyLnZhbHVlOwogICAgICAgICAgICAgICAgYWRkTm90aWZpY2F0aW9uKG5vdGlmKTsKICAgICAgICAgICAgICB9fSBjYXRjaCAoZXJyKSB7X2l0ZXJhdG9yMi5lKGVycik7fSBmaW5hbGx5IHtfaXRlcmF0b3IyLmYoKTt9CiAgICAgICAgICB9KSk7fSwKCiAgICAgIHR5cGU6ICJzdWJtaXQiLAogICAgICB2YWx1ZTogIlNhdmUgQ2hhbmdlcyIgfSkpKTsKCgoKfQoKLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBjb25zdCByb3V0ZXNfUmVsYXlQYWdlID0gKFJlbGF5UGFnZSk7CjsvLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZWxheS9BcHAudHN4CgoKCgoKCmZ1bmN0aW9uIEFwcChfcmVmKSB7dmFyIHBhZ2VzID0gX3JlZi5wYWdlczsKICByZXR1cm4gLyojX19QVVJFX18qLygKICAgIHJlYWN0LmNyZWF0ZUVsZW1lbnQoTWVtb3J5Um91dGVyLCBudWxsLAogICAgIiAiLCAvKiNfX1BVUkVfXyovCiAgICByZWFjdC5jcmVhdGVFbGVtZW50KFJvdXRlcywgbnVsbCwKICAgICIgIiwgLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudChSb3V0ZSwgeyBlbGVtZW50OiAvKiNfX1BVUkVfXyovcmVhY3QuY3JlYXRlRWxlbWVudChjb21wb25lbnRzX0xheW91dCwgeyBwYWdlczogcGFnZXMgfSkgfSwKICAgICIgIiwgLyojX19QVVJFX18qLwogICAgcmVhY3QuY3JlYXRlRWxlbWVudChSb3V0ZSwgewogICAgICBwYXRoOiAiLyIsCiAgICAgIGVsZW1lbnQ6IC8qI19fUFVSRV9fKi9yZWFjdC5jcmVhdGVFbGVtZW50KE5hdmlnYXRlLCB7IHRvOiAiLyIgKyBwYWdlc1swXS5maWxlLCByZXBsYWNlOiB0cnVlIH0pIH0pLAogICAgIiAiLAogICAgcGFnZXMubWFwKGZ1bmN0aW9uIChwLCBpbmRleCkgewogICAgICByZXR1cm4gLyojX19QVVJFX18qLygKICAgICAgICByZWFjdC5jcmVhdGVFbGVtZW50KFJvdXRlLCB7CiAgICAgICAgICBrZXk6IGluZGV4LAogICAgICAgICAgcGF0aDogIi8iICsgcC5maWxlLAogICAgICAgICAgZWxlbWVudDogLyojX19QVVJFX18qL3JlYWN0LmNyZWF0ZUVsZW1lbnQocm91dGVzX1JlbGF5UGFnZSwgeyBwYWdlOiBwIH0pIH0pKTsKCgogICAgfSksICIgIiksCiAgICAiICIpLAogICAgIiAiKSk7CgoKfQoKLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBjb25zdCByZWxheV9BcHAgPSAoQXBwKTsKCi8qKiovIH0pLAoKLyoqKi8gNDQ4OgovKioqLyAoKF9fdW51c2VkX3dlYnBhY2tfbW9kdWxlLCBleHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSA9PiB7CgovKioKICogQGxpY2Vuc2UgUmVhY3QKICogcmVhY3QtZG9tLnByb2R1Y3Rpb24ubWluLmpzCiAqCiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLgogKgogKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGUKICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLgogKi8KLyoKIE1vZGVybml6ciAzLjAuMHByZSAoQ3VzdG9tIEJ1aWxkKSB8IE1JVAoqLwp2YXIgYWE9X193ZWJwYWNrX3JlcXVpcmVfXygyOTQpLGNhPV9fd2VicGFja19yZXF1aXJlX18oODQwKTtmdW5jdGlvbiBwKGEpe2Zvcih2YXIgYj0iaHR0cHM6Ly9yZWFjdGpzLm9yZy9kb2NzL2Vycm9yLWRlY29kZXIuaHRtbD9pbnZhcmlhbnQ9IithLGM9MTtjPGFyZ3VtZW50cy5sZW5ndGg7YysrKWIrPSImYXJnc1tdPSIrZW5jb2RlVVJJQ29tcG9uZW50KGFyZ3VtZW50c1tjXSk7cmV0dXJuIk1pbmlmaWVkIFJlYWN0IGVycm9yICMiK2ErIjsgdmlzaXQgIitiKyIgZm9yIHRoZSBmdWxsIG1lc3NhZ2Ugb3IgdXNlIHRoZSBub24tbWluaWZpZWQgZGV2IGVudmlyb25tZW50IGZvciBmdWxsIGVycm9ycyBhbmQgYWRkaXRpb25hbCBoZWxwZnVsIHdhcm5pbmdzLiJ9dmFyIGRhPW5ldyBTZXQsZWE9e307ZnVuY3Rpb24gZmEoYSxiKXtoYShhLGIpO2hhKGErIkNhcHR1cmUiLGIpfQpmdW5jdGlvbiBoYShhLGIpe2VhW2FdPWI7Zm9yKGE9MDthPGIubGVuZ3RoO2ErKylkYS5hZGQoYlthXSl9CnZhciBpYT0hKCJ1bmRlZmluZWQiPT09dHlwZW9mIHdpbmRvd3x8InVuZGVmaW5lZCI9PT10eXBlb2Ygd2luZG93LmRvY3VtZW50fHwidW5kZWZpbmVkIj09PXR5cGVvZiB3aW5kb3cuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCksamE9T2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eSxrYT0vXls6QS1aX2Etelx1MDBDMC1cdTAwRDZcdTAwRDgtXHUwMEY2XHUwMEY4LVx1MDJGRlx1MDM3MC1cdTAzN0RcdTAzN0YtXHUxRkZGXHUyMDBDLVx1MjAwRFx1MjA3MC1cdTIxOEZcdTJDMDAtXHUyRkVGXHUzMDAxLVx1RDdGRlx1RjkwMC1cdUZEQ0ZcdUZERjAtXHVGRkZEXVs6QS1aX2Etelx1MDBDMC1cdTAwRDZcdTAwRDgtXHUwMEY2XHUwMEY4LVx1MDJGRlx1MDM3MC1cdTAzN0RcdTAzN0YtXHUxRkZGXHUyMDBDLVx1MjAwRFx1MjA3MC1cdTIxOEZcdTJDMDAtXHUyRkVGXHUzMDAxLVx1RDdGRlx1RjkwMC1cdUZEQ0ZcdUZERjAtXHVGRkZEXC0uMC05XHUwMEI3XHUwMzAwLVx1MDM2Rlx1MjAzRi1cdTIwNDBdKiQvLGxhPQp7fSxtYT17fTtmdW5jdGlvbiBvYShhKXtpZihqYS5jYWxsKG1hLGEpKXJldHVybiEwO2lmKGphLmNhbGwobGEsYSkpcmV0dXJuITE7aWYoa2EudGVzdChhKSlyZXR1cm4gbWFbYV09ITA7bGFbYV09ITA7cmV0dXJuITF9ZnVuY3Rpb24gcGEoYSxiLGMsZCl7aWYobnVsbCE9PWMmJjA9PT1jLnR5cGUpcmV0dXJuITE7c3dpdGNoKHR5cGVvZiBiKXtjYXNlICJmdW5jdGlvbiI6Y2FzZSAic3ltYm9sIjpyZXR1cm4hMDtjYXNlICJib29sZWFuIjppZihkKXJldHVybiExO2lmKG51bGwhPT1jKXJldHVybiFjLmFjY2VwdHNCb29sZWFuczthPWEudG9Mb3dlckNhc2UoKS5zbGljZSgwLDUpO3JldHVybiJkYXRhLSIhPT1hJiYiYXJpYS0iIT09YTtkZWZhdWx0OnJldHVybiExfX0KZnVuY3Rpb24gcWEoYSxiLGMsZCl7aWYobnVsbD09PWJ8fCJ1bmRlZmluZWQiPT09dHlwZW9mIGJ8fHBhKGEsYixjLGQpKXJldHVybiEwO2lmKGQpcmV0dXJuITE7aWYobnVsbCE9PWMpc3dpdGNoKGMudHlwZSl7Y2FzZSAzOnJldHVybiFiO2Nhc2UgNDpyZXR1cm4hMT09PWI7Y2FzZSA1OnJldHVybiBpc05hTihiKTtjYXNlIDY6cmV0dXJuIGlzTmFOKGIpfHwxPmJ9cmV0dXJuITF9ZnVuY3Rpb24gdihhLGIsYyxkLGUsZixnKXt0aGlzLmFjY2VwdHNCb29sZWFucz0yPT09Ynx8Mz09PWJ8fDQ9PT1iO3RoaXMuYXR0cmlidXRlTmFtZT1kO3RoaXMuYXR0cmlidXRlTmFtZXNwYWNlPWU7dGhpcy5tdXN0VXNlUHJvcGVydHk9Yzt0aGlzLnByb3BlcnR5TmFtZT1hO3RoaXMudHlwZT1iO3RoaXMuc2FuaXRpemVVUkw9Zjt0aGlzLnJlbW92ZUVtcHR5U3RyaW5nPWd9dmFyIHo9e307CiJjaGlsZHJlbiBkYW5nZXJvdXNseVNldElubmVySFRNTCBkZWZhdWx0VmFsdWUgZGVmYXVsdENoZWNrZWQgaW5uZXJIVE1MIHN1cHByZXNzQ29udGVudEVkaXRhYmxlV2FybmluZyBzdXBwcmVzc0h5ZHJhdGlvbldhcm5pbmcgc3R5bGUiLnNwbGl0KCIgIikuZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMCwhMSxhLG51bGwsITEsITEpfSk7W1siYWNjZXB0Q2hhcnNldCIsImFjY2VwdC1jaGFyc2V0Il0sWyJjbGFzc05hbWUiLCJjbGFzcyJdLFsiaHRtbEZvciIsImZvciJdLFsiaHR0cEVxdWl2IiwiaHR0cC1lcXVpdiJdXS5mb3JFYWNoKGZ1bmN0aW9uKGEpe3ZhciBiPWFbMF07eltiXT1uZXcgdihiLDEsITEsYVsxXSxudWxsLCExLCExKX0pO1siY29udGVudEVkaXRhYmxlIiwiZHJhZ2dhYmxlIiwic3BlbGxDaGVjayIsInZhbHVlIl0uZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMiwhMSxhLnRvTG93ZXJDYXNlKCksbnVsbCwhMSwhMSl9KTsKWyJhdXRvUmV2ZXJzZSIsImV4dGVybmFsUmVzb3VyY2VzUmVxdWlyZWQiLCJmb2N1c2FibGUiLCJwcmVzZXJ2ZUFscGhhIl0uZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMiwhMSxhLG51bGwsITEsITEpfSk7ImFsbG93RnVsbFNjcmVlbiBhc3luYyBhdXRvRm9jdXMgYXV0b1BsYXkgY29udHJvbHMgZGVmYXVsdCBkZWZlciBkaXNhYmxlZCBkaXNhYmxlUGljdHVyZUluUGljdHVyZSBkaXNhYmxlUmVtb3RlUGxheWJhY2sgZm9ybU5vVmFsaWRhdGUgaGlkZGVuIGxvb3Agbm9Nb2R1bGUgbm9WYWxpZGF0ZSBvcGVuIHBsYXlzSW5saW5lIHJlYWRPbmx5IHJlcXVpcmVkIHJldmVyc2VkIHNjb3BlZCBzZWFtbGVzcyBpdGVtU2NvcGUiLnNwbGl0KCIgIikuZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMywhMSxhLnRvTG93ZXJDYXNlKCksbnVsbCwhMSwhMSl9KTsKWyJjaGVja2VkIiwibXVsdGlwbGUiLCJtdXRlZCIsInNlbGVjdGVkIl0uZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMywhMCxhLG51bGwsITEsITEpfSk7WyJjYXB0dXJlIiwiZG93bmxvYWQiXS5mb3JFYWNoKGZ1bmN0aW9uKGEpe3pbYV09bmV3IHYoYSw0LCExLGEsbnVsbCwhMSwhMSl9KTtbImNvbHMiLCJyb3dzIiwic2l6ZSIsInNwYW4iXS5mb3JFYWNoKGZ1bmN0aW9uKGEpe3pbYV09bmV3IHYoYSw2LCExLGEsbnVsbCwhMSwhMSl9KTtbInJvd1NwYW4iLCJzdGFydCJdLmZvckVhY2goZnVuY3Rpb24oYSl7elthXT1uZXcgdihhLDUsITEsYS50b0xvd2VyQ2FzZSgpLG51bGwsITEsITEpfSk7dmFyIHJhPS9bXC06XShbYS16XSkvZztmdW5jdGlvbiBzYShhKXtyZXR1cm4gYVsxXS50b1VwcGVyQ2FzZSgpfQoiYWNjZW50LWhlaWdodCBhbGlnbm1lbnQtYmFzZWxpbmUgYXJhYmljLWZvcm0gYmFzZWxpbmUtc2hpZnQgY2FwLWhlaWdodCBjbGlwLXBhdGggY2xpcC1ydWxlIGNvbG9yLWludGVycG9sYXRpb24gY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzIGNvbG9yLXByb2ZpbGUgY29sb3ItcmVuZGVyaW5nIGRvbWluYW50LWJhc2VsaW5lIGVuYWJsZS1iYWNrZ3JvdW5kIGZpbGwtb3BhY2l0eSBmaWxsLXJ1bGUgZmxvb2QtY29sb3IgZmxvb2Qtb3BhY2l0eSBmb250LWZhbWlseSBmb250LXNpemUgZm9udC1zaXplLWFkanVzdCBmb250LXN0cmV0Y2ggZm9udC1zdHlsZSBmb250LXZhcmlhbnQgZm9udC13ZWlnaHQgZ2x5cGgtbmFtZSBnbHlwaC1vcmllbnRhdGlvbi1ob3Jpem9udGFsIGdseXBoLW9yaWVudGF0aW9uLXZlcnRpY2FsIGhvcml6LWFkdi14IGhvcml6LW9yaWdpbi14IGltYWdlLXJlbmRlcmluZyBsZXR0ZXItc3BhY2luZyBsaWdodGluZy1jb2xvciBtYXJrZXItZW5kIG1hcmtlci1taWQgbWFya2VyLXN0YXJ0IG92ZXJsaW5lLXBvc2l0aW9uIG92ZXJsaW5lLXRoaWNrbmVzcyBwYWludC1vcmRlciBwYW5vc2UtMSBwb2ludGVyLWV2ZW50cyByZW5kZXJpbmctaW50ZW50IHNoYXBlLXJlbmRlcmluZyBzdG9wLWNvbG9yIHN0b3Atb3BhY2l0eSBzdHJpa2V0aHJvdWdoLXBvc2l0aW9uIHN0cmlrZXRocm91Z2gtdGhpY2tuZXNzIHN0cm9rZS1kYXNoYXJyYXkgc3Ryb2tlLWRhc2hvZmZzZXQgc3Ryb2tlLWxpbmVjYXAgc3Ryb2tlLWxpbmVqb2luIHN0cm9rZS1taXRlcmxpbWl0IHN0cm9rZS1vcGFjaXR5IHN0cm9rZS13aWR0aCB0ZXh0LWFuY2hvciB0ZXh0LWRlY29yYXRpb24gdGV4dC1yZW5kZXJpbmcgdW5kZXJsaW5lLXBvc2l0aW9uIHVuZGVybGluZS10aGlja25lc3MgdW5pY29kZS1iaWRpIHVuaWNvZGUtcmFuZ2UgdW5pdHMtcGVyLWVtIHYtYWxwaGFiZXRpYyB2LWhhbmdpbmcgdi1pZGVvZ3JhcGhpYyB2LW1hdGhlbWF0aWNhbCB2ZWN0b3ItZWZmZWN0IHZlcnQtYWR2LXkgdmVydC1vcmlnaW4teCB2ZXJ0LW9yaWdpbi15IHdvcmQtc3BhY2luZyB3cml0aW5nLW1vZGUgeG1sbnM6eGxpbmsgeC1oZWlnaHQiLnNwbGl0KCIgIikuZm9yRWFjaChmdW5jdGlvbihhKXt2YXIgYj1hLnJlcGxhY2UocmEsCnNhKTt6W2JdPW5ldyB2KGIsMSwhMSxhLG51bGwsITEsITEpfSk7InhsaW5rOmFjdHVhdGUgeGxpbms6YXJjcm9sZSB4bGluazpyb2xlIHhsaW5rOnNob3cgeGxpbms6dGl0bGUgeGxpbms6dHlwZSIuc3BsaXQoIiAiKS5mb3JFYWNoKGZ1bmN0aW9uKGEpe3ZhciBiPWEucmVwbGFjZShyYSxzYSk7eltiXT1uZXcgdihiLDEsITEsYSwiaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIsITEsITEpfSk7WyJ4bWw6YmFzZSIsInhtbDpsYW5nIiwieG1sOnNwYWNlIl0uZm9yRWFjaChmdW5jdGlvbihhKXt2YXIgYj1hLnJlcGxhY2UocmEsc2EpO3pbYl09bmV3IHYoYiwxLCExLGEsImh0dHA6Ly93d3cudzMub3JnL1hNTC8xOTk4L25hbWVzcGFjZSIsITEsITEpfSk7WyJ0YWJJbmRleCIsImNyb3NzT3JpZ2luIl0uZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMSwhMSxhLnRvTG93ZXJDYXNlKCksbnVsbCwhMSwhMSl9KTsKei54bGlua0hyZWY9bmV3IHYoInhsaW5rSHJlZiIsMSwhMSwieGxpbms6aHJlZiIsImh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiLCEwLCExKTtbInNyYyIsImhyZWYiLCJhY3Rpb24iLCJmb3JtQWN0aW9uIl0uZm9yRWFjaChmdW5jdGlvbihhKXt6W2FdPW5ldyB2KGEsMSwhMSxhLnRvTG93ZXJDYXNlKCksbnVsbCwhMCwhMCl9KTsKZnVuY3Rpb24gdGEoYSxiLGMsZCl7dmFyIGU9ei5oYXNPd25Qcm9wZXJ0eShiKT96W2JdOm51bGw7aWYobnVsbCE9PWU/MCE9PWUudHlwZTpkfHwhKDI8Yi5sZW5ndGgpfHwibyIhPT1iWzBdJiYiTyIhPT1iWzBdfHwibiIhPT1iWzFdJiYiTiIhPT1iWzFdKXFhKGIsYyxlLGQpJiYoYz1udWxsKSxkfHxudWxsPT09ZT9vYShiKSYmKG51bGw9PT1jP2EucmVtb3ZlQXR0cmlidXRlKGIpOmEuc2V0QXR0cmlidXRlKGIsIiIrYykpOmUubXVzdFVzZVByb3BlcnR5P2FbZS5wcm9wZXJ0eU5hbWVdPW51bGw9PT1jPzM9PT1lLnR5cGU/ITE6IiI6YzooYj1lLmF0dHJpYnV0ZU5hbWUsZD1lLmF0dHJpYnV0ZU5hbWVzcGFjZSxudWxsPT09Yz9hLnJlbW92ZUF0dHJpYnV0ZShiKTooZT1lLnR5cGUsYz0zPT09ZXx8ND09PWUmJiEwPT09Yz8iIjoiIitjLGQ/YS5zZXRBdHRyaWJ1dGVOUyhkLGIsYyk6YS5zZXRBdHRyaWJ1dGUoYixjKSkpfQp2YXIgdWE9YWEuX19TRUNSRVRfSU5URVJOQUxTX0RPX05PVF9VU0VfT1JfWU9VX1dJTExfQkVfRklSRUQsdmE9U3ltYm9sLmZvcigicmVhY3QuZWxlbWVudCIpLHdhPVN5bWJvbC5mb3IoInJlYWN0LnBvcnRhbCIpLHlhPVN5bWJvbC5mb3IoInJlYWN0LmZyYWdtZW50IiksemE9U3ltYm9sLmZvcigicmVhY3Quc3RyaWN0X21vZGUiKSxBYT1TeW1ib2wuZm9yKCJyZWFjdC5wcm9maWxlciIpLEJhPVN5bWJvbC5mb3IoInJlYWN0LnByb3ZpZGVyIiksQ2E9U3ltYm9sLmZvcigicmVhY3QuY29udGV4dCIpLERhPVN5bWJvbC5mb3IoInJlYWN0LmZvcndhcmRfcmVmIiksRWE9U3ltYm9sLmZvcigicmVhY3Quc3VzcGVuc2UiKSxGYT1TeW1ib2wuZm9yKCJyZWFjdC5zdXNwZW5zZV9saXN0IiksR2E9U3ltYm9sLmZvcigicmVhY3QubWVtbyIpLEhhPVN5bWJvbC5mb3IoInJlYWN0LmxhenkiKTtTeW1ib2wuZm9yKCJyZWFjdC5zY29wZSIpO1N5bWJvbC5mb3IoInJlYWN0LmRlYnVnX3RyYWNlX21vZGUiKTsKdmFyIElhPVN5bWJvbC5mb3IoInJlYWN0Lm9mZnNjcmVlbiIpO1N5bWJvbC5mb3IoInJlYWN0LmxlZ2FjeV9oaWRkZW4iKTtTeW1ib2wuZm9yKCJyZWFjdC5jYWNoZSIpO1N5bWJvbC5mb3IoInJlYWN0LnRyYWNpbmdfbWFya2VyIik7dmFyIEphPVN5bWJvbC5pdGVyYXRvcjtmdW5jdGlvbiBLYShhKXtpZihudWxsPT09YXx8Im9iamVjdCIhPT10eXBlb2YgYSlyZXR1cm4gbnVsbDthPUphJiZhW0phXXx8YVsiQEBpdGVyYXRvciJdO3JldHVybiJmdW5jdGlvbiI9PT10eXBlb2YgYT9hOm51bGx9dmFyIEE9T2JqZWN0LmFzc2lnbixMYTtmdW5jdGlvbiBNYShhKXtpZih2b2lkIDA9PT1MYSl0cnl7dGhyb3cgRXJyb3IoKTt9Y2F0Y2goYyl7dmFyIGI9Yy5zdGFjay50cmltKCkubWF0Y2goL1xuKCAqKGF0ICk/KS8pO0xhPWImJmJbMV18fCIifXJldHVybiJcbiIrTGErYX12YXIgTmE9ITE7CmZ1bmN0aW9uIE9hKGEsYil7aWYoIWF8fE5hKXJldHVybiIiO05hPSEwO3ZhciBjPUVycm9yLnByZXBhcmVTdGFja1RyYWNlO0Vycm9yLnByZXBhcmVTdGFja1RyYWNlPXZvaWQgMDt0cnl7aWYoYilpZihiPWZ1bmN0aW9uKCl7dGhyb3cgRXJyb3IoKTt9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShiLnByb3RvdHlwZSwicHJvcHMiLHtzZXQ6ZnVuY3Rpb24oKXt0aHJvdyBFcnJvcigpO319KSwib2JqZWN0Ij09PXR5cGVvZiBSZWZsZWN0JiZSZWZsZWN0LmNvbnN0cnVjdCl7dHJ5e1JlZmxlY3QuY29uc3RydWN0KGIsW10pfWNhdGNoKGwpe3ZhciBkPWx9UmVmbGVjdC5jb25zdHJ1Y3QoYSxbXSxiKX1lbHNle3RyeXtiLmNhbGwoKX1jYXRjaChsKXtkPWx9YS5jYWxsKGIucHJvdG90eXBlKX1lbHNle3RyeXt0aHJvdyBFcnJvcigpO31jYXRjaChsKXtkPWx9YSgpfX1jYXRjaChsKXtpZihsJiZkJiYic3RyaW5nIj09PXR5cGVvZiBsLnN0YWNrKXtmb3IodmFyIGU9bC5zdGFjay5zcGxpdCgiXG4iKSwKZj1kLnN0YWNrLnNwbGl0KCJcbiIpLGc9ZS5sZW5ndGgtMSxoPWYubGVuZ3RoLTE7MTw9ZyYmMDw9aCYmZVtnXSE9PWZbaF07KWgtLTtmb3IoOzE8PWcmJjA8PWg7Zy0tLGgtLSlpZihlW2ddIT09ZltoXSl7aWYoMSE9PWd8fDEhPT1oKXtkbyBpZihnLS0saC0tLDA+aHx8ZVtnXSE9PWZbaF0pe3ZhciBrPSJcbiIrZVtnXS5yZXBsYWNlKCIgYXQgbmV3ICIsIiBhdCAiKTthLmRpc3BsYXlOYW1lJiZrLmluY2x1ZGVzKCI8YW5vbnltb3VzPiIpJiYoaz1rLnJlcGxhY2UoIjxhbm9ueW1vdXM+IixhLmRpc3BsYXlOYW1lKSk7cmV0dXJuIGt9d2hpbGUoMTw9ZyYmMDw9aCl9YnJlYWt9fX1maW5hbGx5e05hPSExLEVycm9yLnByZXBhcmVTdGFja1RyYWNlPWN9cmV0dXJuKGE9YT9hLmRpc3BsYXlOYW1lfHxhLm5hbWU6IiIpP01hKGEpOiIifQpmdW5jdGlvbiBQYShhKXtzd2l0Y2goYS50YWcpe2Nhc2UgNTpyZXR1cm4gTWEoYS50eXBlKTtjYXNlIDE2OnJldHVybiBNYSgiTGF6eSIpO2Nhc2UgMTM6cmV0dXJuIE1hKCJTdXNwZW5zZSIpO2Nhc2UgMTk6cmV0dXJuIE1hKCJTdXNwZW5zZUxpc3QiKTtjYXNlIDA6Y2FzZSAyOmNhc2UgMTU6cmV0dXJuIGE9T2EoYS50eXBlLCExKSxhO2Nhc2UgMTE6cmV0dXJuIGE9T2EoYS50eXBlLnJlbmRlciwhMSksYTtjYXNlIDE6cmV0dXJuIGE9T2EoYS50eXBlLCEwKSxhO2RlZmF1bHQ6cmV0dXJuIiJ9fQpmdW5jdGlvbiBRYShhKXtpZihudWxsPT1hKXJldHVybiBudWxsO2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgYSlyZXR1cm4gYS5kaXNwbGF5TmFtZXx8YS5uYW1lfHxudWxsO2lmKCJzdHJpbmciPT09dHlwZW9mIGEpcmV0dXJuIGE7c3dpdGNoKGEpe2Nhc2UgeWE6cmV0dXJuIkZyYWdtZW50IjtjYXNlIHdhOnJldHVybiJQb3J0YWwiO2Nhc2UgQWE6cmV0dXJuIlByb2ZpbGVyIjtjYXNlIHphOnJldHVybiJTdHJpY3RNb2RlIjtjYXNlIEVhOnJldHVybiJTdXNwZW5zZSI7Y2FzZSBGYTpyZXR1cm4iU3VzcGVuc2VMaXN0In1pZigib2JqZWN0Ij09PXR5cGVvZiBhKXN3aXRjaChhLiQkdHlwZW9mKXtjYXNlIENhOnJldHVybihhLmRpc3BsYXlOYW1lfHwiQ29udGV4dCIpKyIuQ29uc3VtZXIiO2Nhc2UgQmE6cmV0dXJuKGEuX2NvbnRleHQuZGlzcGxheU5hbWV8fCJDb250ZXh0IikrIi5Qcm92aWRlciI7Y2FzZSBEYTp2YXIgYj1hLnJlbmRlcjthPWEuZGlzcGxheU5hbWU7YXx8KGE9Yi5kaXNwbGF5TmFtZXx8CmIubmFtZXx8IiIsYT0iIiE9PWE/IkZvcndhcmRSZWYoIithKyIpIjoiRm9yd2FyZFJlZiIpO3JldHVybiBhO2Nhc2UgR2E6cmV0dXJuIGI9YS5kaXNwbGF5TmFtZXx8bnVsbCxudWxsIT09Yj9iOlFhKGEudHlwZSl8fCJNZW1vIjtjYXNlIEhhOmI9YS5fcGF5bG9hZDthPWEuX2luaXQ7dHJ5e3JldHVybiBRYShhKGIpKX1jYXRjaChjKXt9fXJldHVybiBudWxsfQpmdW5jdGlvbiBSYShhKXt2YXIgYj1hLnR5cGU7c3dpdGNoKGEudGFnKXtjYXNlIDI0OnJldHVybiJDYWNoZSI7Y2FzZSA5OnJldHVybihiLmRpc3BsYXlOYW1lfHwiQ29udGV4dCIpKyIuQ29uc3VtZXIiO2Nhc2UgMTA6cmV0dXJuKGIuX2NvbnRleHQuZGlzcGxheU5hbWV8fCJDb250ZXh0IikrIi5Qcm92aWRlciI7Y2FzZSAxODpyZXR1cm4iRGVoeWRyYXRlZEZyYWdtZW50IjtjYXNlIDExOnJldHVybiBhPWIucmVuZGVyLGE9YS5kaXNwbGF5TmFtZXx8YS5uYW1lfHwiIixiLmRpc3BsYXlOYW1lfHwoIiIhPT1hPyJGb3J3YXJkUmVmKCIrYSsiKSI6IkZvcndhcmRSZWYiKTtjYXNlIDc6cmV0dXJuIkZyYWdtZW50IjtjYXNlIDU6cmV0dXJuIGI7Y2FzZSA0OnJldHVybiJQb3J0YWwiO2Nhc2UgMzpyZXR1cm4iUm9vdCI7Y2FzZSA2OnJldHVybiJUZXh0IjtjYXNlIDE2OnJldHVybiBRYShiKTtjYXNlIDg6cmV0dXJuIGI9PT16YT8iU3RyaWN0TW9kZSI6Ik1vZGUiO2Nhc2UgMjI6cmV0dXJuIk9mZnNjcmVlbiI7CmNhc2UgMTI6cmV0dXJuIlByb2ZpbGVyIjtjYXNlIDIxOnJldHVybiJTY29wZSI7Y2FzZSAxMzpyZXR1cm4iU3VzcGVuc2UiO2Nhc2UgMTk6cmV0dXJuIlN1c3BlbnNlTGlzdCI7Y2FzZSAyNTpyZXR1cm4iVHJhY2luZ01hcmtlciI7Y2FzZSAxOmNhc2UgMDpjYXNlIDE3OmNhc2UgMjpjYXNlIDE0OmNhc2UgMTU6aWYoImZ1bmN0aW9uIj09PXR5cGVvZiBiKXJldHVybiBiLmRpc3BsYXlOYW1lfHxiLm5hbWV8fG51bGw7aWYoInN0cmluZyI9PT10eXBlb2YgYilyZXR1cm4gYn1yZXR1cm4gbnVsbH1mdW5jdGlvbiBTYShhKXtzd2l0Y2godHlwZW9mIGEpe2Nhc2UgImJvb2xlYW4iOmNhc2UgIm51bWJlciI6Y2FzZSAic3RyaW5nIjpjYXNlICJ1bmRlZmluZWQiOnJldHVybiBhO2Nhc2UgIm9iamVjdCI6cmV0dXJuIGE7ZGVmYXVsdDpyZXR1cm4iIn19CmZ1bmN0aW9uIFRhKGEpe3ZhciBiPWEudHlwZTtyZXR1cm4oYT1hLm5vZGVOYW1lKSYmImlucHV0Ij09PWEudG9Mb3dlckNhc2UoKSYmKCJjaGVja2JveCI9PT1ifHwicmFkaW8iPT09Yil9CmZ1bmN0aW9uIFVhKGEpe3ZhciBiPVRhKGEpPyJjaGVja2VkIjoidmFsdWUiLGM9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihhLmNvbnN0cnVjdG9yLnByb3RvdHlwZSxiKSxkPSIiK2FbYl07aWYoIWEuaGFzT3duUHJvcGVydHkoYikmJiJ1bmRlZmluZWQiIT09dHlwZW9mIGMmJiJmdW5jdGlvbiI9PT10eXBlb2YgYy5nZXQmJiJmdW5jdGlvbiI9PT10eXBlb2YgYy5zZXQpe3ZhciBlPWMuZ2V0LGY9Yy5zZXQ7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsYix7Y29uZmlndXJhYmxlOiEwLGdldDpmdW5jdGlvbigpe3JldHVybiBlLmNhbGwodGhpcyl9LHNldDpmdW5jdGlvbihhKXtkPSIiK2E7Zi5jYWxsKHRoaXMsYSl9fSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsYix7ZW51bWVyYWJsZTpjLmVudW1lcmFibGV9KTtyZXR1cm57Z2V0VmFsdWU6ZnVuY3Rpb24oKXtyZXR1cm4gZH0sc2V0VmFsdWU6ZnVuY3Rpb24oYSl7ZD0iIithfSxzdG9wVHJhY2tpbmc6ZnVuY3Rpb24oKXthLl92YWx1ZVRyYWNrZXI9Cm51bGw7ZGVsZXRlIGFbYl19fX19ZnVuY3Rpb24gVmEoYSl7YS5fdmFsdWVUcmFja2VyfHwoYS5fdmFsdWVUcmFja2VyPVVhKGEpKX1mdW5jdGlvbiBXYShhKXtpZighYSlyZXR1cm4hMTt2YXIgYj1hLl92YWx1ZVRyYWNrZXI7aWYoIWIpcmV0dXJuITA7dmFyIGM9Yi5nZXRWYWx1ZSgpO3ZhciBkPSIiO2EmJihkPVRhKGEpP2EuY2hlY2tlZD8idHJ1ZSI6ImZhbHNlIjphLnZhbHVlKTthPWQ7cmV0dXJuIGEhPT1jPyhiLnNldFZhbHVlKGEpLCEwKTohMX1mdW5jdGlvbiBYYShhKXthPWF8fCgidW5kZWZpbmVkIiE9PXR5cGVvZiBkb2N1bWVudD9kb2N1bWVudDp2b2lkIDApO2lmKCJ1bmRlZmluZWQiPT09dHlwZW9mIGEpcmV0dXJuIG51bGw7dHJ5e3JldHVybiBhLmFjdGl2ZUVsZW1lbnR8fGEuYm9keX1jYXRjaChiKXtyZXR1cm4gYS5ib2R5fX0KZnVuY3Rpb24gWWEoYSxiKXt2YXIgYz1iLmNoZWNrZWQ7cmV0dXJuIEEoe30sYix7ZGVmYXVsdENoZWNrZWQ6dm9pZCAwLGRlZmF1bHRWYWx1ZTp2b2lkIDAsdmFsdWU6dm9pZCAwLGNoZWNrZWQ6bnVsbCE9Yz9jOmEuX3dyYXBwZXJTdGF0ZS5pbml0aWFsQ2hlY2tlZH0pfWZ1bmN0aW9uIFphKGEsYil7dmFyIGM9bnVsbD09Yi5kZWZhdWx0VmFsdWU/IiI6Yi5kZWZhdWx0VmFsdWUsZD1udWxsIT1iLmNoZWNrZWQ/Yi5jaGVja2VkOmIuZGVmYXVsdENoZWNrZWQ7Yz1TYShudWxsIT1iLnZhbHVlP2IudmFsdWU6Yyk7YS5fd3JhcHBlclN0YXRlPXtpbml0aWFsQ2hlY2tlZDpkLGluaXRpYWxWYWx1ZTpjLGNvbnRyb2xsZWQ6ImNoZWNrYm94Ij09PWIudHlwZXx8InJhZGlvIj09PWIudHlwZT9udWxsIT1iLmNoZWNrZWQ6bnVsbCE9Yi52YWx1ZX19ZnVuY3Rpb24gYWIoYSxiKXtiPWIuY2hlY2tlZDtudWxsIT1iJiZ0YShhLCJjaGVja2VkIixiLCExKX0KZnVuY3Rpb24gYmIoYSxiKXthYihhLGIpO3ZhciBjPVNhKGIudmFsdWUpLGQ9Yi50eXBlO2lmKG51bGwhPWMpaWYoIm51bWJlciI9PT1kKXtpZigwPT09YyYmIiI9PT1hLnZhbHVlfHxhLnZhbHVlIT1jKWEudmFsdWU9IiIrY31lbHNlIGEudmFsdWUhPT0iIitjJiYoYS52YWx1ZT0iIitjKTtlbHNlIGlmKCJzdWJtaXQiPT09ZHx8InJlc2V0Ij09PWQpe2EucmVtb3ZlQXR0cmlidXRlKCJ2YWx1ZSIpO3JldHVybn1iLmhhc093blByb3BlcnR5KCJ2YWx1ZSIpP2NiKGEsYi50eXBlLGMpOmIuaGFzT3duUHJvcGVydHkoImRlZmF1bHRWYWx1ZSIpJiZjYihhLGIudHlwZSxTYShiLmRlZmF1bHRWYWx1ZSkpO251bGw9PWIuY2hlY2tlZCYmbnVsbCE9Yi5kZWZhdWx0Q2hlY2tlZCYmKGEuZGVmYXVsdENoZWNrZWQ9ISFiLmRlZmF1bHRDaGVja2VkKX0KZnVuY3Rpb24gZGIoYSxiLGMpe2lmKGIuaGFzT3duUHJvcGVydHkoInZhbHVlIil8fGIuaGFzT3duUHJvcGVydHkoImRlZmF1bHRWYWx1ZSIpKXt2YXIgZD1iLnR5cGU7aWYoISgic3VibWl0IiE9PWQmJiJyZXNldCIhPT1kfHx2b2lkIDAhPT1iLnZhbHVlJiZudWxsIT09Yi52YWx1ZSkpcmV0dXJuO2I9IiIrYS5fd3JhcHBlclN0YXRlLmluaXRpYWxWYWx1ZTtjfHxiPT09YS52YWx1ZXx8KGEudmFsdWU9Yik7YS5kZWZhdWx0VmFsdWU9Yn1jPWEubmFtZTsiIiE9PWMmJihhLm5hbWU9IiIpO2EuZGVmYXVsdENoZWNrZWQ9ISFhLl93cmFwcGVyU3RhdGUuaW5pdGlhbENoZWNrZWQ7IiIhPT1jJiYoYS5uYW1lPWMpfQpmdW5jdGlvbiBjYihhLGIsYyl7aWYoIm51bWJlciIhPT1ifHxYYShhLm93bmVyRG9jdW1lbnQpIT09YSludWxsPT1jP2EuZGVmYXVsdFZhbHVlPSIiK2EuX3dyYXBwZXJTdGF0ZS5pbml0aWFsVmFsdWU6YS5kZWZhdWx0VmFsdWUhPT0iIitjJiYoYS5kZWZhdWx0VmFsdWU9IiIrYyl9dmFyIGViPUFycmF5LmlzQXJyYXk7CmZ1bmN0aW9uIGZiKGEsYixjLGQpe2E9YS5vcHRpb25zO2lmKGIpe2I9e307Zm9yKHZhciBlPTA7ZTxjLmxlbmd0aDtlKyspYlsiJCIrY1tlXV09ITA7Zm9yKGM9MDtjPGEubGVuZ3RoO2MrKyllPWIuaGFzT3duUHJvcGVydHkoIiQiK2FbY10udmFsdWUpLGFbY10uc2VsZWN0ZWQhPT1lJiYoYVtjXS5zZWxlY3RlZD1lKSxlJiZkJiYoYVtjXS5kZWZhdWx0U2VsZWN0ZWQ9ITApfWVsc2V7Yz0iIitTYShjKTtiPW51bGw7Zm9yKGU9MDtlPGEubGVuZ3RoO2UrKyl7aWYoYVtlXS52YWx1ZT09PWMpe2FbZV0uc2VsZWN0ZWQ9ITA7ZCYmKGFbZV0uZGVmYXVsdFNlbGVjdGVkPSEwKTtyZXR1cm59bnVsbCE9PWJ8fGFbZV0uZGlzYWJsZWR8fChiPWFbZV0pfW51bGwhPT1iJiYoYi5zZWxlY3RlZD0hMCl9fQpmdW5jdGlvbiBnYihhLGIpe2lmKG51bGwhPWIuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwpdGhyb3cgRXJyb3IocCg5MSkpO3JldHVybiBBKHt9LGIse3ZhbHVlOnZvaWQgMCxkZWZhdWx0VmFsdWU6dm9pZCAwLGNoaWxkcmVuOiIiK2EuX3dyYXBwZXJTdGF0ZS5pbml0aWFsVmFsdWV9KX1mdW5jdGlvbiBoYihhLGIpe3ZhciBjPWIudmFsdWU7aWYobnVsbD09Yyl7Yz1iLmNoaWxkcmVuO2I9Yi5kZWZhdWx0VmFsdWU7aWYobnVsbCE9Yyl7aWYobnVsbCE9Yil0aHJvdyBFcnJvcihwKDkyKSk7aWYoZWIoYykpe2lmKDE8Yy5sZW5ndGgpdGhyb3cgRXJyb3IocCg5MykpO2M9Y1swXX1iPWN9bnVsbD09YiYmKGI9IiIpO2M9Yn1hLl93cmFwcGVyU3RhdGU9e2luaXRpYWxWYWx1ZTpTYShjKX19CmZ1bmN0aW9uIGliKGEsYil7dmFyIGM9U2EoYi52YWx1ZSksZD1TYShiLmRlZmF1bHRWYWx1ZSk7bnVsbCE9YyYmKGM9IiIrYyxjIT09YS52YWx1ZSYmKGEudmFsdWU9YyksbnVsbD09Yi5kZWZhdWx0VmFsdWUmJmEuZGVmYXVsdFZhbHVlIT09YyYmKGEuZGVmYXVsdFZhbHVlPWMpKTtudWxsIT1kJiYoYS5kZWZhdWx0VmFsdWU9IiIrZCl9ZnVuY3Rpb24gamIoYSl7dmFyIGI9YS50ZXh0Q29udGVudDtiPT09YS5fd3JhcHBlclN0YXRlLmluaXRpYWxWYWx1ZSYmIiIhPT1iJiZudWxsIT09YiYmKGEudmFsdWU9Yil9ZnVuY3Rpb24ga2IoYSl7c3dpdGNoKGEpe2Nhc2UgInN2ZyI6cmV0dXJuImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIjtjYXNlICJtYXRoIjpyZXR1cm4iaHR0cDovL3d3dy53My5vcmcvMTk5OC9NYXRoL01hdGhNTCI7ZGVmYXVsdDpyZXR1cm4iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCJ9fQpmdW5jdGlvbiBsYihhLGIpe3JldHVybiBudWxsPT1hfHwiaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCI9PT1hP2tiKGIpOiJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI9PT1hJiYiZm9yZWlnbk9iamVjdCI9PT1iPyJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIjphfQp2YXIgbWIsbmI9ZnVuY3Rpb24oYSl7cmV0dXJuInVuZGVmaW5lZCIhPT10eXBlb2YgTVNBcHAmJk1TQXBwLmV4ZWNVbnNhZmVMb2NhbEZ1bmN0aW9uP2Z1bmN0aW9uKGIsYyxkLGUpe01TQXBwLmV4ZWNVbnNhZmVMb2NhbEZ1bmN0aW9uKGZ1bmN0aW9uKCl7cmV0dXJuIGEoYixjLGQsZSl9KX06YX0oZnVuY3Rpb24oYSxiKXtpZigiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIT09YS5uYW1lc3BhY2VVUkl8fCJpbm5lckhUTUwiaW4gYSlhLmlubmVySFRNTD1iO2Vsc2V7bWI9bWJ8fGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpO21iLmlubmVySFRNTD0iPHN2Zz4iK2IudmFsdWVPZigpLnRvU3RyaW5nKCkrIjwvc3ZnPiI7Zm9yKGI9bWIuZmlyc3RDaGlsZDthLmZpcnN0Q2hpbGQ7KWEucmVtb3ZlQ2hpbGQoYS5maXJzdENoaWxkKTtmb3IoO2IuZmlyc3RDaGlsZDspYS5hcHBlbmRDaGlsZChiLmZpcnN0Q2hpbGQpfX0pOwpmdW5jdGlvbiBvYihhLGIpe2lmKGIpe3ZhciBjPWEuZmlyc3RDaGlsZDtpZihjJiZjPT09YS5sYXN0Q2hpbGQmJjM9PT1jLm5vZGVUeXBlKXtjLm5vZGVWYWx1ZT1iO3JldHVybn19YS50ZXh0Q29udGVudD1ifQp2YXIgcGI9e2FuaW1hdGlvbkl0ZXJhdGlvbkNvdW50OiEwLGFzcGVjdFJhdGlvOiEwLGJvcmRlckltYWdlT3V0c2V0OiEwLGJvcmRlckltYWdlU2xpY2U6ITAsYm9yZGVySW1hZ2VXaWR0aDohMCxib3hGbGV4OiEwLGJveEZsZXhHcm91cDohMCxib3hPcmRpbmFsR3JvdXA6ITAsY29sdW1uQ291bnQ6ITAsY29sdW1uczohMCxmbGV4OiEwLGZsZXhHcm93OiEwLGZsZXhQb3NpdGl2ZTohMCxmbGV4U2hyaW5rOiEwLGZsZXhOZWdhdGl2ZTohMCxmbGV4T3JkZXI6ITAsZ3JpZEFyZWE6ITAsZ3JpZFJvdzohMCxncmlkUm93RW5kOiEwLGdyaWRSb3dTcGFuOiEwLGdyaWRSb3dTdGFydDohMCxncmlkQ29sdW1uOiEwLGdyaWRDb2x1bW5FbmQ6ITAsZ3JpZENvbHVtblNwYW46ITAsZ3JpZENvbHVtblN0YXJ0OiEwLGZvbnRXZWlnaHQ6ITAsbGluZUNsYW1wOiEwLGxpbmVIZWlnaHQ6ITAsb3BhY2l0eTohMCxvcmRlcjohMCxvcnBoYW5zOiEwLHRhYlNpemU6ITAsd2lkb3dzOiEwLHpJbmRleDohMCwKem9vbTohMCxmaWxsT3BhY2l0eTohMCxmbG9vZE9wYWNpdHk6ITAsc3RvcE9wYWNpdHk6ITAsc3Ryb2tlRGFzaGFycmF5OiEwLHN0cm9rZURhc2hvZmZzZXQ6ITAsc3Ryb2tlTWl0ZXJsaW1pdDohMCxzdHJva2VPcGFjaXR5OiEwLHN0cm9rZVdpZHRoOiEwfSxxYj1bIldlYmtpdCIsIm1zIiwiTW96IiwiTyJdO09iamVjdC5rZXlzKHBiKS5mb3JFYWNoKGZ1bmN0aW9uKGEpe3FiLmZvckVhY2goZnVuY3Rpb24oYil7Yj1iK2EuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkrYS5zdWJzdHJpbmcoMSk7cGJbYl09cGJbYV19KX0pO2Z1bmN0aW9uIHJiKGEsYixjKXtyZXR1cm4gbnVsbD09Ynx8ImJvb2xlYW4iPT09dHlwZW9mIGJ8fCIiPT09Yj8iIjpjfHwibnVtYmVyIiE9PXR5cGVvZiBifHwwPT09Ynx8cGIuaGFzT3duUHJvcGVydHkoYSkmJnBiW2FdPygiIitiKS50cmltKCk6YisicHgifQpmdW5jdGlvbiBzYihhLGIpe2E9YS5zdHlsZTtmb3IodmFyIGMgaW4gYilpZihiLmhhc093blByb3BlcnR5KGMpKXt2YXIgZD0wPT09Yy5pbmRleE9mKCItLSIpLGU9cmIoYyxiW2NdLGQpOyJmbG9hdCI9PT1jJiYoYz0iY3NzRmxvYXQiKTtkP2Euc2V0UHJvcGVydHkoYyxlKTphW2NdPWV9fXZhciB0Yj1BKHttZW51aXRlbTohMH0se2FyZWE6ITAsYmFzZTohMCxicjohMCxjb2w6ITAsZW1iZWQ6ITAsaHI6ITAsaW1nOiEwLGlucHV0OiEwLGtleWdlbjohMCxsaW5rOiEwLG1ldGE6ITAscGFyYW06ITAsc291cmNlOiEwLHRyYWNrOiEwLHdicjohMH0pOwpmdW5jdGlvbiB1YihhLGIpe2lmKGIpe2lmKHRiW2FdJiYobnVsbCE9Yi5jaGlsZHJlbnx8bnVsbCE9Yi5kYW5nZXJvdXNseVNldElubmVySFRNTCkpdGhyb3cgRXJyb3IocCgxMzcsYSkpO2lmKG51bGwhPWIuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwpe2lmKG51bGwhPWIuY2hpbGRyZW4pdGhyb3cgRXJyb3IocCg2MCkpO2lmKCJvYmplY3QiIT09dHlwZW9mIGIuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUx8fCEoIl9faHRtbCJpbiBiLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MKSl0aHJvdyBFcnJvcihwKDYxKSk7fWlmKG51bGwhPWIuc3R5bGUmJiJvYmplY3QiIT09dHlwZW9mIGIuc3R5bGUpdGhyb3cgRXJyb3IocCg2MikpO319CmZ1bmN0aW9uIHZiKGEsYil7aWYoLTE9PT1hLmluZGV4T2YoIi0iKSlyZXR1cm4ic3RyaW5nIj09PXR5cGVvZiBiLmlzO3N3aXRjaChhKXtjYXNlICJhbm5vdGF0aW9uLXhtbCI6Y2FzZSAiY29sb3ItcHJvZmlsZSI6Y2FzZSAiZm9udC1mYWNlIjpjYXNlICJmb250LWZhY2Utc3JjIjpjYXNlICJmb250LWZhY2UtdXJpIjpjYXNlICJmb250LWZhY2UtZm9ybWF0IjpjYXNlICJmb250LWZhY2UtbmFtZSI6Y2FzZSAibWlzc2luZy1nbHlwaCI6cmV0dXJuITE7ZGVmYXVsdDpyZXR1cm4hMH19dmFyIHdiPW51bGw7ZnVuY3Rpb24geGIoYSl7YT1hLnRhcmdldHx8YS5zcmNFbGVtZW50fHx3aW5kb3c7YS5jb3JyZXNwb25kaW5nVXNlRWxlbWVudCYmKGE9YS5jb3JyZXNwb25kaW5nVXNlRWxlbWVudCk7cmV0dXJuIDM9PT1hLm5vZGVUeXBlP2EucGFyZW50Tm9kZTphfXZhciB5Yj1udWxsLHpiPW51bGwsQWI9bnVsbDsKZnVuY3Rpb24gQmIoYSl7aWYoYT1DYihhKSl7aWYoImZ1bmN0aW9uIiE9PXR5cGVvZiB5Yil0aHJvdyBFcnJvcihwKDI4MCkpO3ZhciBiPWEuc3RhdGVOb2RlO2ImJihiPURiKGIpLHliKGEuc3RhdGVOb2RlLGEudHlwZSxiKSl9fWZ1bmN0aW9uIEViKGEpe3piP0FiP0FiLnB1c2goYSk6QWI9W2FdOnpiPWF9ZnVuY3Rpb24gRmIoKXtpZih6Yil7dmFyIGE9emIsYj1BYjtBYj16Yj1udWxsO0JiKGEpO2lmKGIpZm9yKGE9MDthPGIubGVuZ3RoO2ErKylCYihiW2FdKX19ZnVuY3Rpb24gR2IoYSxiKXtyZXR1cm4gYShiKX1mdW5jdGlvbiBIYigpe312YXIgSWI9ITE7ZnVuY3Rpb24gSmIoYSxiLGMpe2lmKEliKXJldHVybiBhKGIsYyk7SWI9ITA7dHJ5e3JldHVybiBHYihhLGIsYyl9ZmluYWxseXtpZihJYj0hMSxudWxsIT09emJ8fG51bGwhPT1BYilIYigpLEZiKCl9fQpmdW5jdGlvbiBLYihhLGIpe3ZhciBjPWEuc3RhdGVOb2RlO2lmKG51bGw9PT1jKXJldHVybiBudWxsO3ZhciBkPURiKGMpO2lmKG51bGw9PT1kKXJldHVybiBudWxsO2M9ZFtiXTthOnN3aXRjaChiKXtjYXNlICJvbkNsaWNrIjpjYXNlICJvbkNsaWNrQ2FwdHVyZSI6Y2FzZSAib25Eb3VibGVDbGljayI6Y2FzZSAib25Eb3VibGVDbGlja0NhcHR1cmUiOmNhc2UgIm9uTW91c2VEb3duIjpjYXNlICJvbk1vdXNlRG93bkNhcHR1cmUiOmNhc2UgIm9uTW91c2VNb3ZlIjpjYXNlICJvbk1vdXNlTW92ZUNhcHR1cmUiOmNhc2UgIm9uTW91c2VVcCI6Y2FzZSAib25Nb3VzZVVwQ2FwdHVyZSI6Y2FzZSAib25Nb3VzZUVudGVyIjooZD0hZC5kaXNhYmxlZCl8fChhPWEudHlwZSxkPSEoImJ1dHRvbiI9PT1hfHwiaW5wdXQiPT09YXx8InNlbGVjdCI9PT1hfHwidGV4dGFyZWEiPT09YSkpO2E9IWQ7YnJlYWsgYTtkZWZhdWx0OmE9ITF9aWYoYSlyZXR1cm4gbnVsbDtpZihjJiYiZnVuY3Rpb24iIT09CnR5cGVvZiBjKXRocm93IEVycm9yKHAoMjMxLGIsdHlwZW9mIGMpKTtyZXR1cm4gY312YXIgTGI9ITE7aWYoaWEpdHJ5e3ZhciBNYj17fTtPYmplY3QuZGVmaW5lUHJvcGVydHkoTWIsInBhc3NpdmUiLHtnZXQ6ZnVuY3Rpb24oKXtMYj0hMH19KTt3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigidGVzdCIsTWIsTWIpO3dpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCJ0ZXN0IixNYixNYil9Y2F0Y2goYSl7TGI9ITF9ZnVuY3Rpb24gTmIoYSxiLGMsZCxlLGYsZyxoLGspe3ZhciBsPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywzKTt0cnl7Yi5hcHBseShjLGwpfWNhdGNoKG0pe3RoaXMub25FcnJvcihtKX19dmFyIE9iPSExLFBiPW51bGwsUWI9ITEsUmI9bnVsbCxTYj17b25FcnJvcjpmdW5jdGlvbihhKXtPYj0hMDtQYj1hfX07ZnVuY3Rpb24gVGIoYSxiLGMsZCxlLGYsZyxoLGspe09iPSExO1BiPW51bGw7TmIuYXBwbHkoU2IsYXJndW1lbnRzKX0KZnVuY3Rpb24gVWIoYSxiLGMsZCxlLGYsZyxoLGspe1RiLmFwcGx5KHRoaXMsYXJndW1lbnRzKTtpZihPYil7aWYoT2Ipe3ZhciBsPVBiO09iPSExO1BiPW51bGx9ZWxzZSB0aHJvdyBFcnJvcihwKDE5OCkpO1FifHwoUWI9ITAsUmI9bCl9fWZ1bmN0aW9uIFZiKGEpe3ZhciBiPWEsYz1hO2lmKGEuYWx0ZXJuYXRlKWZvcig7Yi5yZXR1cm47KWI9Yi5yZXR1cm47ZWxzZXthPWI7ZG8gYj1hLDAhPT0oYi5mbGFncyY0MDk4KSYmKGM9Yi5yZXR1cm4pLGE9Yi5yZXR1cm47d2hpbGUoYSl9cmV0dXJuIDM9PT1iLnRhZz9jOm51bGx9ZnVuY3Rpb24gV2IoYSl7aWYoMTM9PT1hLnRhZyl7dmFyIGI9YS5tZW1vaXplZFN0YXRlO251bGw9PT1iJiYoYT1hLmFsdGVybmF0ZSxudWxsIT09YSYmKGI9YS5tZW1vaXplZFN0YXRlKSk7aWYobnVsbCE9PWIpcmV0dXJuIGIuZGVoeWRyYXRlZH1yZXR1cm4gbnVsbH1mdW5jdGlvbiBYYihhKXtpZihWYihhKSE9PWEpdGhyb3cgRXJyb3IocCgxODgpKTt9CmZ1bmN0aW9uIFliKGEpe3ZhciBiPWEuYWx0ZXJuYXRlO2lmKCFiKXtiPVZiKGEpO2lmKG51bGw9PT1iKXRocm93IEVycm9yKHAoMTg4KSk7cmV0dXJuIGIhPT1hP251bGw6YX1mb3IodmFyIGM9YSxkPWI7Oyl7dmFyIGU9Yy5yZXR1cm47aWYobnVsbD09PWUpYnJlYWs7dmFyIGY9ZS5hbHRlcm5hdGU7aWYobnVsbD09PWYpe2Q9ZS5yZXR1cm47aWYobnVsbCE9PWQpe2M9ZDtjb250aW51ZX1icmVha31pZihlLmNoaWxkPT09Zi5jaGlsZCl7Zm9yKGY9ZS5jaGlsZDtmOyl7aWYoZj09PWMpcmV0dXJuIFhiKGUpLGE7aWYoZj09PWQpcmV0dXJuIFhiKGUpLGI7Zj1mLnNpYmxpbmd9dGhyb3cgRXJyb3IocCgxODgpKTt9aWYoYy5yZXR1cm4hPT1kLnJldHVybiljPWUsZD1mO2Vsc2V7Zm9yKHZhciBnPSExLGg9ZS5jaGlsZDtoOyl7aWYoaD09PWMpe2c9ITA7Yz1lO2Q9ZjticmVha31pZihoPT09ZCl7Zz0hMDtkPWU7Yz1mO2JyZWFrfWg9aC5zaWJsaW5nfWlmKCFnKXtmb3IoaD1mLmNoaWxkO2g7KXtpZihoPT09CmMpe2c9ITA7Yz1mO2Q9ZTticmVha31pZihoPT09ZCl7Zz0hMDtkPWY7Yz1lO2JyZWFrfWg9aC5zaWJsaW5nfWlmKCFnKXRocm93IEVycm9yKHAoMTg5KSk7fX1pZihjLmFsdGVybmF0ZSE9PWQpdGhyb3cgRXJyb3IocCgxOTApKTt9aWYoMyE9PWMudGFnKXRocm93IEVycm9yKHAoMTg4KSk7cmV0dXJuIGMuc3RhdGVOb2RlLmN1cnJlbnQ9PT1jP2E6Yn1mdW5jdGlvbiBaYihhKXthPVliKGEpO3JldHVybiBudWxsIT09YT8kYihhKTpudWxsfWZ1bmN0aW9uICRiKGEpe2lmKDU9PT1hLnRhZ3x8Nj09PWEudGFnKXJldHVybiBhO2ZvcihhPWEuY2hpbGQ7bnVsbCE9PWE7KXt2YXIgYj0kYihhKTtpZihudWxsIT09YilyZXR1cm4gYjthPWEuc2libGluZ31yZXR1cm4gbnVsbH0KdmFyIGFjPWNhLnVuc3RhYmxlX3NjaGVkdWxlQ2FsbGJhY2ssYmM9Y2EudW5zdGFibGVfY2FuY2VsQ2FsbGJhY2ssY2M9Y2EudW5zdGFibGVfc2hvdWxkWWllbGQsZGM9Y2EudW5zdGFibGVfcmVxdWVzdFBhaW50LEI9Y2EudW5zdGFibGVfbm93LGVjPWNhLnVuc3RhYmxlX2dldEN1cnJlbnRQcmlvcml0eUxldmVsLGZjPWNhLnVuc3RhYmxlX0ltbWVkaWF0ZVByaW9yaXR5LGdjPWNhLnVuc3RhYmxlX1VzZXJCbG9ja2luZ1ByaW9yaXR5LGhjPWNhLnVuc3RhYmxlX05vcm1hbFByaW9yaXR5LGljPWNhLnVuc3RhYmxlX0xvd1ByaW9yaXR5LGpjPWNhLnVuc3RhYmxlX0lkbGVQcmlvcml0eSxrYz1udWxsLGxjPW51bGw7ZnVuY3Rpb24gbWMoYSl7aWYobGMmJiJmdW5jdGlvbiI9PT10eXBlb2YgbGMub25Db21taXRGaWJlclJvb3QpdHJ5e2xjLm9uQ29tbWl0RmliZXJSb290KGtjLGEsdm9pZCAwLDEyOD09PShhLmN1cnJlbnQuZmxhZ3MmMTI4KSl9Y2F0Y2goYil7fX0KdmFyIG9jPU1hdGguY2x6MzI/TWF0aC5jbHozMjpuYyxwYz1NYXRoLmxvZyxxYz1NYXRoLkxOMjtmdW5jdGlvbiBuYyhhKXthPj4+PTA7cmV0dXJuIDA9PT1hPzMyOjMxLShwYyhhKS9xY3wwKXwwfXZhciByYz02NCxzYz00MTk0MzA0OwpmdW5jdGlvbiB0YyhhKXtzd2l0Y2goYSYtYSl7Y2FzZSAxOnJldHVybiAxO2Nhc2UgMjpyZXR1cm4gMjtjYXNlIDQ6cmV0dXJuIDQ7Y2FzZSA4OnJldHVybiA4O2Nhc2UgMTY6cmV0dXJuIDE2O2Nhc2UgMzI6cmV0dXJuIDMyO2Nhc2UgNjQ6Y2FzZSAxMjg6Y2FzZSAyNTY6Y2FzZSA1MTI6Y2FzZSAxMDI0OmNhc2UgMjA0ODpjYXNlIDQwOTY6Y2FzZSA4MTkyOmNhc2UgMTYzODQ6Y2FzZSAzMjc2ODpjYXNlIDY1NTM2OmNhc2UgMTMxMDcyOmNhc2UgMjYyMTQ0OmNhc2UgNTI0Mjg4OmNhc2UgMTA0ODU3NjpjYXNlIDIwOTcxNTI6cmV0dXJuIGEmNDE5NDI0MDtjYXNlIDQxOTQzMDQ6Y2FzZSA4Mzg4NjA4OmNhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpjYXNlIDY3MTA4ODY0OnJldHVybiBhJjEzMDAyMzQyNDtjYXNlIDEzNDIxNzcyODpyZXR1cm4gMTM0MjE3NzI4O2Nhc2UgMjY4NDM1NDU2OnJldHVybiAyNjg0MzU0NTY7Y2FzZSA1MzY4NzA5MTI6cmV0dXJuIDUzNjg3MDkxMjtjYXNlIDEwNzM3NDE4MjQ6cmV0dXJuIDEwNzM3NDE4MjQ7CmRlZmF1bHQ6cmV0dXJuIGF9fWZ1bmN0aW9uIHVjKGEsYil7dmFyIGM9YS5wZW5kaW5nTGFuZXM7aWYoMD09PWMpcmV0dXJuIDA7dmFyIGQ9MCxlPWEuc3VzcGVuZGVkTGFuZXMsZj1hLnBpbmdlZExhbmVzLGc9YyYyNjg0MzU0NTU7aWYoMCE9PWcpe3ZhciBoPWcmfmU7MCE9PWg/ZD10YyhoKTooZiY9ZywwIT09ZiYmKGQ9dGMoZikpKX1lbHNlIGc9YyZ+ZSwwIT09Zz9kPXRjKGcpOjAhPT1mJiYoZD10YyhmKSk7aWYoMD09PWQpcmV0dXJuIDA7aWYoMCE9PWImJmIhPT1kJiYwPT09KGImZSkmJihlPWQmLWQsZj1iJi1iLGU+PWZ8fDE2PT09ZSYmMCE9PShmJjQxOTQyNDApKSlyZXR1cm4gYjswIT09KGQmNCkmJihkfD1jJjE2KTtiPWEuZW50YW5nbGVkTGFuZXM7aWYoMCE9PWIpZm9yKGE9YS5lbnRhbmdsZW1lbnRzLGImPWQ7MDxiOyljPTMxLW9jKGIpLGU9MTw8YyxkfD1hW2NdLGImPX5lO3JldHVybiBkfQpmdW5jdGlvbiB2YyhhLGIpe3N3aXRjaChhKXtjYXNlIDE6Y2FzZSAyOmNhc2UgNDpyZXR1cm4gYisyNTA7Y2FzZSA4OmNhc2UgMTY6Y2FzZSAzMjpjYXNlIDY0OmNhc2UgMTI4OmNhc2UgMjU2OmNhc2UgNTEyOmNhc2UgMTAyNDpjYXNlIDIwNDg6Y2FzZSA0MDk2OmNhc2UgODE5MjpjYXNlIDE2Mzg0OmNhc2UgMzI3Njg6Y2FzZSA2NTUzNjpjYXNlIDEzMTA3MjpjYXNlIDI2MjE0NDpjYXNlIDUyNDI4ODpjYXNlIDEwNDg1NzY6Y2FzZSAyMDk3MTUyOnJldHVybiBiKzVFMztjYXNlIDQxOTQzMDQ6Y2FzZSA4Mzg4NjA4OmNhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpjYXNlIDY3MTA4ODY0OnJldHVybi0xO2Nhc2UgMTM0MjE3NzI4OmNhc2UgMjY4NDM1NDU2OmNhc2UgNTM2ODcwOTEyOmNhc2UgMTA3Mzc0MTgyNDpyZXR1cm4tMTtkZWZhdWx0OnJldHVybi0xfX0KZnVuY3Rpb24gd2MoYSxiKXtmb3IodmFyIGM9YS5zdXNwZW5kZWRMYW5lcyxkPWEucGluZ2VkTGFuZXMsZT1hLmV4cGlyYXRpb25UaW1lcyxmPWEucGVuZGluZ0xhbmVzOzA8Zjspe3ZhciBnPTMxLW9jKGYpLGg9MTw8ZyxrPWVbZ107aWYoLTE9PT1rKXtpZigwPT09KGgmYyl8fDAhPT0oaCZkKSllW2ddPXZjKGgsYil9ZWxzZSBrPD1iJiYoYS5leHBpcmVkTGFuZXN8PWgpO2YmPX5ofX1mdW5jdGlvbiB4YyhhKXthPWEucGVuZGluZ0xhbmVzJi0xMDczNzQxODI1O3JldHVybiAwIT09YT9hOmEmMTA3Mzc0MTgyND8xMDczNzQxODI0OjB9ZnVuY3Rpb24geWMoKXt2YXIgYT1yYztyYzw8PTE7MD09PShyYyY0MTk0MjQwKSYmKHJjPTY0KTtyZXR1cm4gYX1mdW5jdGlvbiB6YyhhKXtmb3IodmFyIGI9W10sYz0wOzMxPmM7YysrKWIucHVzaChhKTtyZXR1cm4gYn0KZnVuY3Rpb24gQWMoYSxiLGMpe2EucGVuZGluZ0xhbmVzfD1iOzUzNjg3MDkxMiE9PWImJihhLnN1c3BlbmRlZExhbmVzPTAsYS5waW5nZWRMYW5lcz0wKTthPWEuZXZlbnRUaW1lcztiPTMxLW9jKGIpO2FbYl09Y31mdW5jdGlvbiBCYyhhLGIpe3ZhciBjPWEucGVuZGluZ0xhbmVzJn5iO2EucGVuZGluZ0xhbmVzPWI7YS5zdXNwZW5kZWRMYW5lcz0wO2EucGluZ2VkTGFuZXM9MDthLmV4cGlyZWRMYW5lcyY9YjthLm11dGFibGVSZWFkTGFuZXMmPWI7YS5lbnRhbmdsZWRMYW5lcyY9YjtiPWEuZW50YW5nbGVtZW50czt2YXIgZD1hLmV2ZW50VGltZXM7Zm9yKGE9YS5leHBpcmF0aW9uVGltZXM7MDxjOyl7dmFyIGU9MzEtb2MoYyksZj0xPDxlO2JbZV09MDtkW2VdPS0xO2FbZV09LTE7YyY9fmZ9fQpmdW5jdGlvbiBDYyhhLGIpe3ZhciBjPWEuZW50YW5nbGVkTGFuZXN8PWI7Zm9yKGE9YS5lbnRhbmdsZW1lbnRzO2M7KXt2YXIgZD0zMS1vYyhjKSxlPTE8PGQ7ZSZifGFbZF0mYiYmKGFbZF18PWIpO2MmPX5lfX12YXIgQz0wO2Z1bmN0aW9uIERjKGEpe2EmPS1hO3JldHVybiAxPGE/NDxhPzAhPT0oYSYyNjg0MzU0NTUpPzE2OjUzNjg3MDkxMjo0OjF9dmFyIEVjLEZjLEdjLEhjLEljLEpjPSExLEtjPVtdLExjPW51bGwsTWM9bnVsbCxOYz1udWxsLE9jPW5ldyBNYXAsUGM9bmV3IE1hcCxRYz1bXSxSYz0ibW91c2Vkb3duIG1vdXNldXAgdG91Y2hjYW5jZWwgdG91Y2hlbmQgdG91Y2hzdGFydCBhdXhjbGljayBkYmxjbGljayBwb2ludGVyY2FuY2VsIHBvaW50ZXJkb3duIHBvaW50ZXJ1cCBkcmFnZW5kIGRyYWdzdGFydCBkcm9wIGNvbXBvc2l0aW9uZW5kIGNvbXBvc2l0aW9uc3RhcnQga2V5ZG93biBrZXlwcmVzcyBrZXl1cCBpbnB1dCB0ZXh0SW5wdXQgY29weSBjdXQgcGFzdGUgY2xpY2sgY2hhbmdlIGNvbnRleHRtZW51IHJlc2V0IHN1Ym1pdCIuc3BsaXQoIiAiKTsKZnVuY3Rpb24gU2MoYSxiKXtzd2l0Y2goYSl7Y2FzZSAiZm9jdXNpbiI6Y2FzZSAiZm9jdXNvdXQiOkxjPW51bGw7YnJlYWs7Y2FzZSAiZHJhZ2VudGVyIjpjYXNlICJkcmFnbGVhdmUiOk1jPW51bGw7YnJlYWs7Y2FzZSAibW91c2VvdmVyIjpjYXNlICJtb3VzZW91dCI6TmM9bnVsbDticmVhaztjYXNlICJwb2ludGVyb3ZlciI6Y2FzZSAicG9pbnRlcm91dCI6T2MuZGVsZXRlKGIucG9pbnRlcklkKTticmVhaztjYXNlICJnb3Rwb2ludGVyY2FwdHVyZSI6Y2FzZSAibG9zdHBvaW50ZXJjYXB0dXJlIjpQYy5kZWxldGUoYi5wb2ludGVySWQpfX0KZnVuY3Rpb24gVGMoYSxiLGMsZCxlLGYpe2lmKG51bGw9PT1hfHxhLm5hdGl2ZUV2ZW50IT09ZilyZXR1cm4gYT17YmxvY2tlZE9uOmIsZG9tRXZlbnROYW1lOmMsZXZlbnRTeXN0ZW1GbGFnczpkLG5hdGl2ZUV2ZW50OmYsdGFyZ2V0Q29udGFpbmVyczpbZV19LG51bGwhPT1iJiYoYj1DYihiKSxudWxsIT09YiYmRmMoYikpLGE7YS5ldmVudFN5c3RlbUZsYWdzfD1kO2I9YS50YXJnZXRDb250YWluZXJzO251bGwhPT1lJiYtMT09PWIuaW5kZXhPZihlKSYmYi5wdXNoKGUpO3JldHVybiBhfQpmdW5jdGlvbiBVYyhhLGIsYyxkLGUpe3N3aXRjaChiKXtjYXNlICJmb2N1c2luIjpyZXR1cm4gTGM9VGMoTGMsYSxiLGMsZCxlKSwhMDtjYXNlICJkcmFnZW50ZXIiOnJldHVybiBNYz1UYyhNYyxhLGIsYyxkLGUpLCEwO2Nhc2UgIm1vdXNlb3ZlciI6cmV0dXJuIE5jPVRjKE5jLGEsYixjLGQsZSksITA7Y2FzZSAicG9pbnRlcm92ZXIiOnZhciBmPWUucG9pbnRlcklkO09jLnNldChmLFRjKE9jLmdldChmKXx8bnVsbCxhLGIsYyxkLGUpKTtyZXR1cm4hMDtjYXNlICJnb3Rwb2ludGVyY2FwdHVyZSI6cmV0dXJuIGY9ZS5wb2ludGVySWQsUGMuc2V0KGYsVGMoUGMuZ2V0KGYpfHxudWxsLGEsYixjLGQsZSkpLCEwfXJldHVybiExfQpmdW5jdGlvbiBWYyhhKXt2YXIgYj1XYyhhLnRhcmdldCk7aWYobnVsbCE9PWIpe3ZhciBjPVZiKGIpO2lmKG51bGwhPT1jKWlmKGI9Yy50YWcsMTM9PT1iKXtpZihiPVdiKGMpLG51bGwhPT1iKXthLmJsb2NrZWRPbj1iO0ljKGEucHJpb3JpdHksZnVuY3Rpb24oKXtHYyhjKX0pO3JldHVybn19ZWxzZSBpZigzPT09YiYmYy5zdGF0ZU5vZGUuY3VycmVudC5tZW1vaXplZFN0YXRlLmlzRGVoeWRyYXRlZCl7YS5ibG9ja2VkT249Mz09PWMudGFnP2Muc3RhdGVOb2RlLmNvbnRhaW5lckluZm86bnVsbDtyZXR1cm59fWEuYmxvY2tlZE9uPW51bGx9CmZ1bmN0aW9uIFhjKGEpe2lmKG51bGwhPT1hLmJsb2NrZWRPbilyZXR1cm4hMTtmb3IodmFyIGI9YS50YXJnZXRDb250YWluZXJzOzA8Yi5sZW5ndGg7KXt2YXIgYz1ZYyhhLmRvbUV2ZW50TmFtZSxhLmV2ZW50U3lzdGVtRmxhZ3MsYlswXSxhLm5hdGl2ZUV2ZW50KTtpZihudWxsPT09Yyl7Yz1hLm5hdGl2ZUV2ZW50O3ZhciBkPW5ldyBjLmNvbnN0cnVjdG9yKGMudHlwZSxjKTt3Yj1kO2MudGFyZ2V0LmRpc3BhdGNoRXZlbnQoZCk7d2I9bnVsbH1lbHNlIHJldHVybiBiPUNiKGMpLG51bGwhPT1iJiZGYyhiKSxhLmJsb2NrZWRPbj1jLCExO2Iuc2hpZnQoKX1yZXR1cm4hMH1mdW5jdGlvbiBaYyhhLGIsYyl7WGMoYSkmJmMuZGVsZXRlKGIpfWZ1bmN0aW9uICRjKCl7SmM9ITE7bnVsbCE9PUxjJiZYYyhMYykmJihMYz1udWxsKTtudWxsIT09TWMmJlhjKE1jKSYmKE1jPW51bGwpO251bGwhPT1OYyYmWGMoTmMpJiYoTmM9bnVsbCk7T2MuZm9yRWFjaChaYyk7UGMuZm9yRWFjaChaYyl9CmZ1bmN0aW9uIGFkKGEsYil7YS5ibG9ja2VkT249PT1iJiYoYS5ibG9ja2VkT249bnVsbCxKY3x8KEpjPSEwLGNhLnVuc3RhYmxlX3NjaGVkdWxlQ2FsbGJhY2soY2EudW5zdGFibGVfTm9ybWFsUHJpb3JpdHksJGMpKSl9CmZ1bmN0aW9uIGJkKGEpe2Z1bmN0aW9uIGIoYil7cmV0dXJuIGFkKGIsYSl9aWYoMDxLYy5sZW5ndGgpe2FkKEtjWzBdLGEpO2Zvcih2YXIgYz0xO2M8S2MubGVuZ3RoO2MrKyl7dmFyIGQ9S2NbY107ZC5ibG9ja2VkT249PT1hJiYoZC5ibG9ja2VkT249bnVsbCl9fW51bGwhPT1MYyYmYWQoTGMsYSk7bnVsbCE9PU1jJiZhZChNYyxhKTtudWxsIT09TmMmJmFkKE5jLGEpO09jLmZvckVhY2goYik7UGMuZm9yRWFjaChiKTtmb3IoYz0wO2M8UWMubGVuZ3RoO2MrKylkPVFjW2NdLGQuYmxvY2tlZE9uPT09YSYmKGQuYmxvY2tlZE9uPW51bGwpO2Zvcig7MDxRYy5sZW5ndGgmJihjPVFjWzBdLG51bGw9PT1jLmJsb2NrZWRPbik7KVZjKGMpLG51bGw9PT1jLmJsb2NrZWRPbiYmUWMuc2hpZnQoKX12YXIgY2Q9dWEuUmVhY3RDdXJyZW50QmF0Y2hDb25maWcsZGQ9ITA7CmZ1bmN0aW9uIGVkKGEsYixjLGQpe3ZhciBlPUMsZj1jZC50cmFuc2l0aW9uO2NkLnRyYW5zaXRpb249bnVsbDt0cnl7Qz0xLGZkKGEsYixjLGQpfWZpbmFsbHl7Qz1lLGNkLnRyYW5zaXRpb249Zn19ZnVuY3Rpb24gZ2QoYSxiLGMsZCl7dmFyIGU9QyxmPWNkLnRyYW5zaXRpb247Y2QudHJhbnNpdGlvbj1udWxsO3RyeXtDPTQsZmQoYSxiLGMsZCl9ZmluYWxseXtDPWUsY2QudHJhbnNpdGlvbj1mfX0KZnVuY3Rpb24gZmQoYSxiLGMsZCl7aWYoZGQpe3ZhciBlPVljKGEsYixjLGQpO2lmKG51bGw9PT1lKWhkKGEsYixkLGlkLGMpLFNjKGEsZCk7ZWxzZSBpZihVYyhlLGEsYixjLGQpKWQuc3RvcFByb3BhZ2F0aW9uKCk7ZWxzZSBpZihTYyhhLGQpLGImNCYmLTE8UmMuaW5kZXhPZihhKSl7Zm9yKDtudWxsIT09ZTspe3ZhciBmPUNiKGUpO251bGwhPT1mJiZFYyhmKTtmPVljKGEsYixjLGQpO251bGw9PT1mJiZoZChhLGIsZCxpZCxjKTtpZihmPT09ZSlicmVhaztlPWZ9bnVsbCE9PWUmJmQuc3RvcFByb3BhZ2F0aW9uKCl9ZWxzZSBoZChhLGIsZCxudWxsLGMpfX12YXIgaWQ9bnVsbDsKZnVuY3Rpb24gWWMoYSxiLGMsZCl7aWQ9bnVsbDthPXhiKGQpO2E9V2MoYSk7aWYobnVsbCE9PWEpaWYoYj1WYihhKSxudWxsPT09YilhPW51bGw7ZWxzZSBpZihjPWIudGFnLDEzPT09Yyl7YT1XYihiKTtpZihudWxsIT09YSlyZXR1cm4gYTthPW51bGx9ZWxzZSBpZigzPT09Yyl7aWYoYi5zdGF0ZU5vZGUuY3VycmVudC5tZW1vaXplZFN0YXRlLmlzRGVoeWRyYXRlZClyZXR1cm4gMz09PWIudGFnP2Iuc3RhdGVOb2RlLmNvbnRhaW5lckluZm86bnVsbDthPW51bGx9ZWxzZSBiIT09YSYmKGE9bnVsbCk7aWQ9YTtyZXR1cm4gbnVsbH0KZnVuY3Rpb24gamQoYSl7c3dpdGNoKGEpe2Nhc2UgImNhbmNlbCI6Y2FzZSAiY2xpY2siOmNhc2UgImNsb3NlIjpjYXNlICJjb250ZXh0bWVudSI6Y2FzZSAiY29weSI6Y2FzZSAiY3V0IjpjYXNlICJhdXhjbGljayI6Y2FzZSAiZGJsY2xpY2siOmNhc2UgImRyYWdlbmQiOmNhc2UgImRyYWdzdGFydCI6Y2FzZSAiZHJvcCI6Y2FzZSAiZm9jdXNpbiI6Y2FzZSAiZm9jdXNvdXQiOmNhc2UgImlucHV0IjpjYXNlICJpbnZhbGlkIjpjYXNlICJrZXlkb3duIjpjYXNlICJrZXlwcmVzcyI6Y2FzZSAia2V5dXAiOmNhc2UgIm1vdXNlZG93biI6Y2FzZSAibW91c2V1cCI6Y2FzZSAicGFzdGUiOmNhc2UgInBhdXNlIjpjYXNlICJwbGF5IjpjYXNlICJwb2ludGVyY2FuY2VsIjpjYXNlICJwb2ludGVyZG93biI6Y2FzZSAicG9pbnRlcnVwIjpjYXNlICJyYXRlY2hhbmdlIjpjYXNlICJyZXNldCI6Y2FzZSAicmVzaXplIjpjYXNlICJzZWVrZWQiOmNhc2UgInN1Ym1pdCI6Y2FzZSAidG91Y2hjYW5jZWwiOmNhc2UgInRvdWNoZW5kIjpjYXNlICJ0b3VjaHN0YXJ0IjpjYXNlICJ2b2x1bWVjaGFuZ2UiOmNhc2UgImNoYW5nZSI6Y2FzZSAic2VsZWN0aW9uY2hhbmdlIjpjYXNlICJ0ZXh0SW5wdXQiOmNhc2UgImNvbXBvc2l0aW9uc3RhcnQiOmNhc2UgImNvbXBvc2l0aW9uZW5kIjpjYXNlICJjb21wb3NpdGlvbnVwZGF0ZSI6Y2FzZSAiYmVmb3JlYmx1ciI6Y2FzZSAiYWZ0ZXJibHVyIjpjYXNlICJiZWZvcmVpbnB1dCI6Y2FzZSAiYmx1ciI6Y2FzZSAiZnVsbHNjcmVlbmNoYW5nZSI6Y2FzZSAiZm9jdXMiOmNhc2UgImhhc2hjaGFuZ2UiOmNhc2UgInBvcHN0YXRlIjpjYXNlICJzZWxlY3QiOmNhc2UgInNlbGVjdHN0YXJ0IjpyZXR1cm4gMTtjYXNlICJkcmFnIjpjYXNlICJkcmFnZW50ZXIiOmNhc2UgImRyYWdleGl0IjpjYXNlICJkcmFnbGVhdmUiOmNhc2UgImRyYWdvdmVyIjpjYXNlICJtb3VzZW1vdmUiOmNhc2UgIm1vdXNlb3V0IjpjYXNlICJtb3VzZW92ZXIiOmNhc2UgInBvaW50ZXJtb3ZlIjpjYXNlICJwb2ludGVyb3V0IjpjYXNlICJwb2ludGVyb3ZlciI6Y2FzZSAic2Nyb2xsIjpjYXNlICJ0b2dnbGUiOmNhc2UgInRvdWNobW92ZSI6Y2FzZSAid2hlZWwiOmNhc2UgIm1vdXNlZW50ZXIiOmNhc2UgIm1vdXNlbGVhdmUiOmNhc2UgInBvaW50ZXJlbnRlciI6Y2FzZSAicG9pbnRlcmxlYXZlIjpyZXR1cm4gNDsKY2FzZSAibWVzc2FnZSI6c3dpdGNoKGVjKCkpe2Nhc2UgZmM6cmV0dXJuIDE7Y2FzZSBnYzpyZXR1cm4gNDtjYXNlIGhjOmNhc2UgaWM6cmV0dXJuIDE2O2Nhc2UgamM6cmV0dXJuIDUzNjg3MDkxMjtkZWZhdWx0OnJldHVybiAxNn1kZWZhdWx0OnJldHVybiAxNn19dmFyIGtkPW51bGwsbGQ9bnVsbCxtZD1udWxsO2Z1bmN0aW9uIG5kKCl7aWYobWQpcmV0dXJuIG1kO3ZhciBhLGI9bGQsYz1iLmxlbmd0aCxkLGU9InZhbHVlImluIGtkP2tkLnZhbHVlOmtkLnRleHRDb250ZW50LGY9ZS5sZW5ndGg7Zm9yKGE9MDthPGMmJmJbYV09PT1lW2FdO2ErKyk7dmFyIGc9Yy1hO2ZvcihkPTE7ZDw9ZyYmYltjLWRdPT09ZVtmLWRdO2QrKyk7cmV0dXJuIG1kPWUuc2xpY2UoYSwxPGQ/MS1kOnZvaWQgMCl9CmZ1bmN0aW9uIG9kKGEpe3ZhciBiPWEua2V5Q29kZTsiY2hhckNvZGUiaW4gYT8oYT1hLmNoYXJDb2RlLDA9PT1hJiYxMz09PWImJihhPTEzKSk6YT1iOzEwPT09YSYmKGE9MTMpO3JldHVybiAzMjw9YXx8MTM9PT1hP2E6MH1mdW5jdGlvbiBwZCgpe3JldHVybiEwfWZ1bmN0aW9uIHFkKCl7cmV0dXJuITF9CmZ1bmN0aW9uIHJkKGEpe2Z1bmN0aW9uIGIoYixkLGUsZixnKXt0aGlzLl9yZWFjdE5hbWU9Yjt0aGlzLl90YXJnZXRJbnN0PWU7dGhpcy50eXBlPWQ7dGhpcy5uYXRpdmVFdmVudD1mO3RoaXMudGFyZ2V0PWc7dGhpcy5jdXJyZW50VGFyZ2V0PW51bGw7Zm9yKHZhciBjIGluIGEpYS5oYXNPd25Qcm9wZXJ0eShjKSYmKGI9YVtjXSx0aGlzW2NdPWI/YihmKTpmW2NdKTt0aGlzLmlzRGVmYXVsdFByZXZlbnRlZD0obnVsbCE9Zi5kZWZhdWx0UHJldmVudGVkP2YuZGVmYXVsdFByZXZlbnRlZDohMT09PWYucmV0dXJuVmFsdWUpP3BkOnFkO3RoaXMuaXNQcm9wYWdhdGlvblN0b3BwZWQ9cWQ7cmV0dXJuIHRoaXN9QShiLnByb3RvdHlwZSx7cHJldmVudERlZmF1bHQ6ZnVuY3Rpb24oKXt0aGlzLmRlZmF1bHRQcmV2ZW50ZWQ9ITA7dmFyIGE9dGhpcy5uYXRpdmVFdmVudDthJiYoYS5wcmV2ZW50RGVmYXVsdD9hLnByZXZlbnREZWZhdWx0KCk6InVua25vd24iIT09dHlwZW9mIGEucmV0dXJuVmFsdWUmJgooYS5yZXR1cm5WYWx1ZT0hMSksdGhpcy5pc0RlZmF1bHRQcmV2ZW50ZWQ9cGQpfSxzdG9wUHJvcGFnYXRpb246ZnVuY3Rpb24oKXt2YXIgYT10aGlzLm5hdGl2ZUV2ZW50O2EmJihhLnN0b3BQcm9wYWdhdGlvbj9hLnN0b3BQcm9wYWdhdGlvbigpOiJ1bmtub3duIiE9PXR5cGVvZiBhLmNhbmNlbEJ1YmJsZSYmKGEuY2FuY2VsQnViYmxlPSEwKSx0aGlzLmlzUHJvcGFnYXRpb25TdG9wcGVkPXBkKX0scGVyc2lzdDpmdW5jdGlvbigpe30saXNQZXJzaXN0ZW50OnBkfSk7cmV0dXJuIGJ9CnZhciBzZD17ZXZlbnRQaGFzZTowLGJ1YmJsZXM6MCxjYW5jZWxhYmxlOjAsdGltZVN0YW1wOmZ1bmN0aW9uKGEpe3JldHVybiBhLnRpbWVTdGFtcHx8RGF0ZS5ub3coKX0sZGVmYXVsdFByZXZlbnRlZDowLGlzVHJ1c3RlZDowfSx0ZD1yZChzZCksdWQ9QSh7fSxzZCx7dmlldzowLGRldGFpbDowfSksdmQ9cmQodWQpLHdkLHhkLHlkLEFkPUEoe30sdWQse3NjcmVlblg6MCxzY3JlZW5ZOjAsY2xpZW50WDowLGNsaWVudFk6MCxwYWdlWDowLHBhZ2VZOjAsY3RybEtleTowLHNoaWZ0S2V5OjAsYWx0S2V5OjAsbWV0YUtleTowLGdldE1vZGlmaWVyU3RhdGU6emQsYnV0dG9uOjAsYnV0dG9uczowLHJlbGF0ZWRUYXJnZXQ6ZnVuY3Rpb24oYSl7cmV0dXJuIHZvaWQgMD09PWEucmVsYXRlZFRhcmdldD9hLmZyb21FbGVtZW50PT09YS5zcmNFbGVtZW50P2EudG9FbGVtZW50OmEuZnJvbUVsZW1lbnQ6YS5yZWxhdGVkVGFyZ2V0fSxtb3ZlbWVudFg6ZnVuY3Rpb24oYSl7aWYoIm1vdmVtZW50WCJpbgphKXJldHVybiBhLm1vdmVtZW50WDthIT09eWQmJih5ZCYmIm1vdXNlbW92ZSI9PT1hLnR5cGU/KHdkPWEuc2NyZWVuWC15ZC5zY3JlZW5YLHhkPWEuc2NyZWVuWS15ZC5zY3JlZW5ZKTp4ZD13ZD0wLHlkPWEpO3JldHVybiB3ZH0sbW92ZW1lbnRZOmZ1bmN0aW9uKGEpe3JldHVybiJtb3ZlbWVudFkiaW4gYT9hLm1vdmVtZW50WTp4ZH19KSxCZD1yZChBZCksQ2Q9QSh7fSxBZCx7ZGF0YVRyYW5zZmVyOjB9KSxEZD1yZChDZCksRWQ9QSh7fSx1ZCx7cmVsYXRlZFRhcmdldDowfSksRmQ9cmQoRWQpLEdkPUEoe30sc2Qse2FuaW1hdGlvbk5hbWU6MCxlbGFwc2VkVGltZTowLHBzZXVkb0VsZW1lbnQ6MH0pLEhkPXJkKEdkKSxJZD1BKHt9LHNkLHtjbGlwYm9hcmREYXRhOmZ1bmN0aW9uKGEpe3JldHVybiJjbGlwYm9hcmREYXRhImluIGE/YS5jbGlwYm9hcmREYXRhOndpbmRvdy5jbGlwYm9hcmREYXRhfX0pLEpkPXJkKElkKSxLZD1BKHt9LHNkLHtkYXRhOjB9KSxMZD1yZChLZCksTWQ9e0VzYzoiRXNjYXBlIiwKU3BhY2ViYXI6IiAiLExlZnQ6IkFycm93TGVmdCIsVXA6IkFycm93VXAiLFJpZ2h0OiJBcnJvd1JpZ2h0IixEb3duOiJBcnJvd0Rvd24iLERlbDoiRGVsZXRlIixXaW46Ik9TIixNZW51OiJDb250ZXh0TWVudSIsQXBwczoiQ29udGV4dE1lbnUiLFNjcm9sbDoiU2Nyb2xsTG9jayIsTW96UHJpbnRhYmxlS2V5OiJVbmlkZW50aWZpZWQifSxOZD17ODoiQmFja3NwYWNlIiw5OiJUYWIiLDEyOiJDbGVhciIsMTM6IkVudGVyIiwxNjoiU2hpZnQiLDE3OiJDb250cm9sIiwxODoiQWx0IiwxOToiUGF1c2UiLDIwOiJDYXBzTG9jayIsMjc6IkVzY2FwZSIsMzI6IiAiLDMzOiJQYWdlVXAiLDM0OiJQYWdlRG93biIsMzU6IkVuZCIsMzY6IkhvbWUiLDM3OiJBcnJvd0xlZnQiLDM4OiJBcnJvd1VwIiwzOToiQXJyb3dSaWdodCIsNDA6IkFycm93RG93biIsNDU6Ikluc2VydCIsNDY6IkRlbGV0ZSIsMTEyOiJGMSIsMTEzOiJGMiIsMTE0OiJGMyIsMTE1OiJGNCIsMTE2OiJGNSIsMTE3OiJGNiIsMTE4OiJGNyIsCjExOToiRjgiLDEyMDoiRjkiLDEyMToiRjEwIiwxMjI6IkYxMSIsMTIzOiJGMTIiLDE0NDoiTnVtTG9jayIsMTQ1OiJTY3JvbGxMb2NrIiwyMjQ6Ik1ldGEifSxPZD17QWx0OiJhbHRLZXkiLENvbnRyb2w6ImN0cmxLZXkiLE1ldGE6Im1ldGFLZXkiLFNoaWZ0OiJzaGlmdEtleSJ9O2Z1bmN0aW9uIFBkKGEpe3ZhciBiPXRoaXMubmF0aXZlRXZlbnQ7cmV0dXJuIGIuZ2V0TW9kaWZpZXJTdGF0ZT9iLmdldE1vZGlmaWVyU3RhdGUoYSk6KGE9T2RbYV0pPyEhYlthXTohMX1mdW5jdGlvbiB6ZCgpe3JldHVybiBQZH0KdmFyIFFkPUEoe30sdWQse2tleTpmdW5jdGlvbihhKXtpZihhLmtleSl7dmFyIGI9TWRbYS5rZXldfHxhLmtleTtpZigiVW5pZGVudGlmaWVkIiE9PWIpcmV0dXJuIGJ9cmV0dXJuImtleXByZXNzIj09PWEudHlwZT8oYT1vZChhKSwxMz09PWE/IkVudGVyIjpTdHJpbmcuZnJvbUNoYXJDb2RlKGEpKToia2V5ZG93biI9PT1hLnR5cGV8fCJrZXl1cCI9PT1hLnR5cGU/TmRbYS5rZXlDb2RlXXx8IlVuaWRlbnRpZmllZCI6IiJ9LGNvZGU6MCxsb2NhdGlvbjowLGN0cmxLZXk6MCxzaGlmdEtleTowLGFsdEtleTowLG1ldGFLZXk6MCxyZXBlYXQ6MCxsb2NhbGU6MCxnZXRNb2RpZmllclN0YXRlOnpkLGNoYXJDb2RlOmZ1bmN0aW9uKGEpe3JldHVybiJrZXlwcmVzcyI9PT1hLnR5cGU/b2QoYSk6MH0sa2V5Q29kZTpmdW5jdGlvbihhKXtyZXR1cm4ia2V5ZG93biI9PT1hLnR5cGV8fCJrZXl1cCI9PT1hLnR5cGU/YS5rZXlDb2RlOjB9LHdoaWNoOmZ1bmN0aW9uKGEpe3JldHVybiJrZXlwcmVzcyI9PT0KYS50eXBlP29kKGEpOiJrZXlkb3duIj09PWEudHlwZXx8ImtleXVwIj09PWEudHlwZT9hLmtleUNvZGU6MH19KSxSZD1yZChRZCksU2Q9QSh7fSxBZCx7cG9pbnRlcklkOjAsd2lkdGg6MCxoZWlnaHQ6MCxwcmVzc3VyZTowLHRhbmdlbnRpYWxQcmVzc3VyZTowLHRpbHRYOjAsdGlsdFk6MCx0d2lzdDowLHBvaW50ZXJUeXBlOjAsaXNQcmltYXJ5OjB9KSxUZD1yZChTZCksVWQ9QSh7fSx1ZCx7dG91Y2hlczowLHRhcmdldFRvdWNoZXM6MCxjaGFuZ2VkVG91Y2hlczowLGFsdEtleTowLG1ldGFLZXk6MCxjdHJsS2V5OjAsc2hpZnRLZXk6MCxnZXRNb2RpZmllclN0YXRlOnpkfSksVmQ9cmQoVWQpLFdkPUEoe30sc2Qse3Byb3BlcnR5TmFtZTowLGVsYXBzZWRUaW1lOjAscHNldWRvRWxlbWVudDowfSksWGQ9cmQoV2QpLFlkPUEoe30sQWQse2RlbHRhWDpmdW5jdGlvbihhKXtyZXR1cm4iZGVsdGFYImluIGE/YS5kZWx0YVg6IndoZWVsRGVsdGFYImluIGE/LWEud2hlZWxEZWx0YVg6MH0sCmRlbHRhWTpmdW5jdGlvbihhKXtyZXR1cm4iZGVsdGFZImluIGE/YS5kZWx0YVk6IndoZWVsRGVsdGFZImluIGE/LWEud2hlZWxEZWx0YVk6IndoZWVsRGVsdGEiaW4gYT8tYS53aGVlbERlbHRhOjB9LGRlbHRhWjowLGRlbHRhTW9kZTowfSksWmQ9cmQoWWQpLCRkPVs5LDEzLDI3LDMyXSxhZT1pYSYmIkNvbXBvc2l0aW9uRXZlbnQiaW4gd2luZG93LGJlPW51bGw7aWEmJiJkb2N1bWVudE1vZGUiaW4gZG9jdW1lbnQmJihiZT1kb2N1bWVudC5kb2N1bWVudE1vZGUpO3ZhciBjZT1pYSYmIlRleHRFdmVudCJpbiB3aW5kb3cmJiFiZSxkZT1pYSYmKCFhZXx8YmUmJjg8YmUmJjExPj1iZSksZWU9U3RyaW5nLmZyb21DaGFyQ29kZSgzMiksZmU9ITE7CmZ1bmN0aW9uIGdlKGEsYil7c3dpdGNoKGEpe2Nhc2UgImtleXVwIjpyZXR1cm4tMSE9PSRkLmluZGV4T2YoYi5rZXlDb2RlKTtjYXNlICJrZXlkb3duIjpyZXR1cm4gMjI5IT09Yi5rZXlDb2RlO2Nhc2UgImtleXByZXNzIjpjYXNlICJtb3VzZWRvd24iOmNhc2UgImZvY3Vzb3V0IjpyZXR1cm4hMDtkZWZhdWx0OnJldHVybiExfX1mdW5jdGlvbiBoZShhKXthPWEuZGV0YWlsO3JldHVybiJvYmplY3QiPT09dHlwZW9mIGEmJiJkYXRhImluIGE/YS5kYXRhOm51bGx9dmFyIGllPSExO2Z1bmN0aW9uIGplKGEsYil7c3dpdGNoKGEpe2Nhc2UgImNvbXBvc2l0aW9uZW5kIjpyZXR1cm4gaGUoYik7Y2FzZSAia2V5cHJlc3MiOmlmKDMyIT09Yi53aGljaClyZXR1cm4gbnVsbDtmZT0hMDtyZXR1cm4gZWU7Y2FzZSAidGV4dElucHV0IjpyZXR1cm4gYT1iLmRhdGEsYT09PWVlJiZmZT9udWxsOmE7ZGVmYXVsdDpyZXR1cm4gbnVsbH19CmZ1bmN0aW9uIGtlKGEsYil7aWYoaWUpcmV0dXJuImNvbXBvc2l0aW9uZW5kIj09PWF8fCFhZSYmZ2UoYSxiKT8oYT1uZCgpLG1kPWxkPWtkPW51bGwsaWU9ITEsYSk6bnVsbDtzd2l0Y2goYSl7Y2FzZSAicGFzdGUiOnJldHVybiBudWxsO2Nhc2UgImtleXByZXNzIjppZighKGIuY3RybEtleXx8Yi5hbHRLZXl8fGIubWV0YUtleSl8fGIuY3RybEtleSYmYi5hbHRLZXkpe2lmKGIuY2hhciYmMTxiLmNoYXIubGVuZ3RoKXJldHVybiBiLmNoYXI7aWYoYi53aGljaClyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZShiLndoaWNoKX1yZXR1cm4gbnVsbDtjYXNlICJjb21wb3NpdGlvbmVuZCI6cmV0dXJuIGRlJiYia28iIT09Yi5sb2NhbGU/bnVsbDpiLmRhdGE7ZGVmYXVsdDpyZXR1cm4gbnVsbH19CnZhciBsZT17Y29sb3I6ITAsZGF0ZTohMCxkYXRldGltZTohMCwiZGF0ZXRpbWUtbG9jYWwiOiEwLGVtYWlsOiEwLG1vbnRoOiEwLG51bWJlcjohMCxwYXNzd29yZDohMCxyYW5nZTohMCxzZWFyY2g6ITAsdGVsOiEwLHRleHQ6ITAsdGltZTohMCx1cmw6ITAsd2VlazohMH07ZnVuY3Rpb24gbWUoYSl7dmFyIGI9YSYmYS5ub2RlTmFtZSYmYS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO3JldHVybiJpbnB1dCI9PT1iPyEhbGVbYS50eXBlXToidGV4dGFyZWEiPT09Yj8hMDohMX1mdW5jdGlvbiBuZShhLGIsYyxkKXtFYihkKTtiPW9lKGIsIm9uQ2hhbmdlIik7MDxiLmxlbmd0aCYmKGM9bmV3IHRkKCJvbkNoYW5nZSIsImNoYW5nZSIsbnVsbCxjLGQpLGEucHVzaCh7ZXZlbnQ6YyxsaXN0ZW5lcnM6Yn0pKX12YXIgcGU9bnVsbCxxZT1udWxsO2Z1bmN0aW9uIHJlKGEpe3NlKGEsMCl9ZnVuY3Rpb24gdGUoYSl7dmFyIGI9dWUoYSk7aWYoV2EoYikpcmV0dXJuIGF9CmZ1bmN0aW9uIHZlKGEsYil7aWYoImNoYW5nZSI9PT1hKXJldHVybiBifXZhciB3ZT0hMTtpZihpYSl7dmFyIHhlO2lmKGlhKXt2YXIgeWU9Im9uaW5wdXQiaW4gZG9jdW1lbnQ7aWYoIXllKXt2YXIgemU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7emUuc2V0QXR0cmlidXRlKCJvbmlucHV0IiwicmV0dXJuOyIpO3llPSJmdW5jdGlvbiI9PT10eXBlb2YgemUub25pbnB1dH14ZT15ZX1lbHNlIHhlPSExO3dlPXhlJiYoIWRvY3VtZW50LmRvY3VtZW50TW9kZXx8OTxkb2N1bWVudC5kb2N1bWVudE1vZGUpfWZ1bmN0aW9uIEFlKCl7cGUmJihwZS5kZXRhY2hFdmVudCgib25wcm9wZXJ0eWNoYW5nZSIsQmUpLHFlPXBlPW51bGwpfWZ1bmN0aW9uIEJlKGEpe2lmKCJ2YWx1ZSI9PT1hLnByb3BlcnR5TmFtZSYmdGUocWUpKXt2YXIgYj1bXTtuZShiLHFlLGEseGIoYSkpO0piKHJlLGIpfX0KZnVuY3Rpb24gQ2UoYSxiLGMpeyJmb2N1c2luIj09PWE/KEFlKCkscGU9YixxZT1jLHBlLmF0dGFjaEV2ZW50KCJvbnByb3BlcnR5Y2hhbmdlIixCZSkpOiJmb2N1c291dCI9PT1hJiZBZSgpfWZ1bmN0aW9uIERlKGEpe2lmKCJzZWxlY3Rpb25jaGFuZ2UiPT09YXx8ImtleXVwIj09PWF8fCJrZXlkb3duIj09PWEpcmV0dXJuIHRlKHFlKX1mdW5jdGlvbiBFZShhLGIpe2lmKCJjbGljayI9PT1hKXJldHVybiB0ZShiKX1mdW5jdGlvbiBGZShhLGIpe2lmKCJpbnB1dCI9PT1hfHwiY2hhbmdlIj09PWEpcmV0dXJuIHRlKGIpfWZ1bmN0aW9uIEdlKGEsYil7cmV0dXJuIGE9PT1iJiYoMCE9PWF8fDEvYT09PTEvYil8fGEhPT1hJiZiIT09Yn12YXIgSGU9ImZ1bmN0aW9uIj09PXR5cGVvZiBPYmplY3QuaXM/T2JqZWN0LmlzOkdlOwpmdW5jdGlvbiBJZShhLGIpe2lmKEhlKGEsYikpcmV0dXJuITA7aWYoIm9iamVjdCIhPT10eXBlb2YgYXx8bnVsbD09PWF8fCJvYmplY3QiIT09dHlwZW9mIGJ8fG51bGw9PT1iKXJldHVybiExO3ZhciBjPU9iamVjdC5rZXlzKGEpLGQ9T2JqZWN0LmtleXMoYik7aWYoYy5sZW5ndGghPT1kLmxlbmd0aClyZXR1cm4hMTtmb3IoZD0wO2Q8Yy5sZW5ndGg7ZCsrKXt2YXIgZT1jW2RdO2lmKCFqYS5jYWxsKGIsZSl8fCFIZShhW2VdLGJbZV0pKXJldHVybiExfXJldHVybiEwfWZ1bmN0aW9uIEplKGEpe2Zvcig7YSYmYS5maXJzdENoaWxkOylhPWEuZmlyc3RDaGlsZDtyZXR1cm4gYX0KZnVuY3Rpb24gS2UoYSxiKXt2YXIgYz1KZShhKTthPTA7Zm9yKHZhciBkO2M7KXtpZigzPT09Yy5ub2RlVHlwZSl7ZD1hK2MudGV4dENvbnRlbnQubGVuZ3RoO2lmKGE8PWImJmQ+PWIpcmV0dXJue25vZGU6YyxvZmZzZXQ6Yi1hfTthPWR9YTp7Zm9yKDtjOyl7aWYoYy5uZXh0U2libGluZyl7Yz1jLm5leHRTaWJsaW5nO2JyZWFrIGF9Yz1jLnBhcmVudE5vZGV9Yz12b2lkIDB9Yz1KZShjKX19ZnVuY3Rpb24gTGUoYSxiKXtyZXR1cm4gYSYmYj9hPT09Yj8hMDphJiYzPT09YS5ub2RlVHlwZT8hMTpiJiYzPT09Yi5ub2RlVHlwZT9MZShhLGIucGFyZW50Tm9kZSk6ImNvbnRhaW5zImluIGE/YS5jb250YWlucyhiKTphLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uPyEhKGEuY29tcGFyZURvY3VtZW50UG9zaXRpb24oYikmMTYpOiExOiExfQpmdW5jdGlvbiBNZSgpe2Zvcih2YXIgYT13aW5kb3csYj1YYSgpO2IgaW5zdGFuY2VvZiBhLkhUTUxJRnJhbWVFbGVtZW50Oyl7dHJ5e3ZhciBjPSJzdHJpbmciPT09dHlwZW9mIGIuY29udGVudFdpbmRvdy5sb2NhdGlvbi5ocmVmfWNhdGNoKGQpe2M9ITF9aWYoYylhPWIuY29udGVudFdpbmRvdztlbHNlIGJyZWFrO2I9WGEoYS5kb2N1bWVudCl9cmV0dXJuIGJ9ZnVuY3Rpb24gTmUoYSl7dmFyIGI9YSYmYS5ub2RlTmFtZSYmYS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO3JldHVybiBiJiYoImlucHV0Ij09PWImJigidGV4dCI9PT1hLnR5cGV8fCJzZWFyY2giPT09YS50eXBlfHwidGVsIj09PWEudHlwZXx8InVybCI9PT1hLnR5cGV8fCJwYXNzd29yZCI9PT1hLnR5cGUpfHwidGV4dGFyZWEiPT09Ynx8InRydWUiPT09YS5jb250ZW50RWRpdGFibGUpfQpmdW5jdGlvbiBPZShhKXt2YXIgYj1NZSgpLGM9YS5mb2N1c2VkRWxlbSxkPWEuc2VsZWN0aW9uUmFuZ2U7aWYoYiE9PWMmJmMmJmMub3duZXJEb2N1bWVudCYmTGUoYy5vd25lckRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxjKSl7aWYobnVsbCE9PWQmJk5lKGMpKWlmKGI9ZC5zdGFydCxhPWQuZW5kLHZvaWQgMD09PWEmJihhPWIpLCJzZWxlY3Rpb25TdGFydCJpbiBjKWMuc2VsZWN0aW9uU3RhcnQ9YixjLnNlbGVjdGlvbkVuZD1NYXRoLm1pbihhLGMudmFsdWUubGVuZ3RoKTtlbHNlIGlmKGE9KGI9Yy5vd25lckRvY3VtZW50fHxkb2N1bWVudCkmJmIuZGVmYXVsdFZpZXd8fHdpbmRvdyxhLmdldFNlbGVjdGlvbil7YT1hLmdldFNlbGVjdGlvbigpO3ZhciBlPWMudGV4dENvbnRlbnQubGVuZ3RoLGY9TWF0aC5taW4oZC5zdGFydCxlKTtkPXZvaWQgMD09PWQuZW5kP2Y6TWF0aC5taW4oZC5lbmQsZSk7IWEuZXh0ZW5kJiZmPmQmJihlPWQsZD1mLGY9ZSk7ZT1LZShjLGYpO3ZhciBnPUtlKGMsCmQpO2UmJmcmJigxIT09YS5yYW5nZUNvdW50fHxhLmFuY2hvck5vZGUhPT1lLm5vZGV8fGEuYW5jaG9yT2Zmc2V0IT09ZS5vZmZzZXR8fGEuZm9jdXNOb2RlIT09Zy5ub2RlfHxhLmZvY3VzT2Zmc2V0IT09Zy5vZmZzZXQpJiYoYj1iLmNyZWF0ZVJhbmdlKCksYi5zZXRTdGFydChlLm5vZGUsZS5vZmZzZXQpLGEucmVtb3ZlQWxsUmFuZ2VzKCksZj5kPyhhLmFkZFJhbmdlKGIpLGEuZXh0ZW5kKGcubm9kZSxnLm9mZnNldCkpOihiLnNldEVuZChnLm5vZGUsZy5vZmZzZXQpLGEuYWRkUmFuZ2UoYikpKX1iPVtdO2ZvcihhPWM7YT1hLnBhcmVudE5vZGU7KTE9PT1hLm5vZGVUeXBlJiZiLnB1c2goe2VsZW1lbnQ6YSxsZWZ0OmEuc2Nyb2xsTGVmdCx0b3A6YS5zY3JvbGxUb3B9KTsiZnVuY3Rpb24iPT09dHlwZW9mIGMuZm9jdXMmJmMuZm9jdXMoKTtmb3IoYz0wO2M8Yi5sZW5ndGg7YysrKWE9YltjXSxhLmVsZW1lbnQuc2Nyb2xsTGVmdD1hLmxlZnQsYS5lbGVtZW50LnNjcm9sbFRvcD1hLnRvcH19CnZhciBQZT1pYSYmImRvY3VtZW50TW9kZSJpbiBkb2N1bWVudCYmMTE+PWRvY3VtZW50LmRvY3VtZW50TW9kZSxRZT1udWxsLFJlPW51bGwsU2U9bnVsbCxUZT0hMTsKZnVuY3Rpb24gVWUoYSxiLGMpe3ZhciBkPWMud2luZG93PT09Yz9jLmRvY3VtZW50Ojk9PT1jLm5vZGVUeXBlP2M6Yy5vd25lckRvY3VtZW50O1RlfHxudWxsPT1RZXx8UWUhPT1YYShkKXx8KGQ9UWUsInNlbGVjdGlvblN0YXJ0ImluIGQmJk5lKGQpP2Q9e3N0YXJ0OmQuc2VsZWN0aW9uU3RhcnQsZW5kOmQuc2VsZWN0aW9uRW5kfTooZD0oZC5vd25lckRvY3VtZW50JiZkLm93bmVyRG9jdW1lbnQuZGVmYXVsdFZpZXd8fHdpbmRvdykuZ2V0U2VsZWN0aW9uKCksZD17YW5jaG9yTm9kZTpkLmFuY2hvck5vZGUsYW5jaG9yT2Zmc2V0OmQuYW5jaG9yT2Zmc2V0LGZvY3VzTm9kZTpkLmZvY3VzTm9kZSxmb2N1c09mZnNldDpkLmZvY3VzT2Zmc2V0fSksU2UmJkllKFNlLGQpfHwoU2U9ZCxkPW9lKFJlLCJvblNlbGVjdCIpLDA8ZC5sZW5ndGgmJihiPW5ldyB0ZCgib25TZWxlY3QiLCJzZWxlY3QiLG51bGwsYixjKSxhLnB1c2goe2V2ZW50OmIsbGlzdGVuZXJzOmR9KSxiLnRhcmdldD1RZSkpKX0KZnVuY3Rpb24gVmUoYSxiKXt2YXIgYz17fTtjW2EudG9Mb3dlckNhc2UoKV09Yi50b0xvd2VyQ2FzZSgpO2NbIldlYmtpdCIrYV09IndlYmtpdCIrYjtjWyJNb3oiK2FdPSJtb3oiK2I7cmV0dXJuIGN9dmFyIFdlPXthbmltYXRpb25lbmQ6VmUoIkFuaW1hdGlvbiIsIkFuaW1hdGlvbkVuZCIpLGFuaW1hdGlvbml0ZXJhdGlvbjpWZSgiQW5pbWF0aW9uIiwiQW5pbWF0aW9uSXRlcmF0aW9uIiksYW5pbWF0aW9uc3RhcnQ6VmUoIkFuaW1hdGlvbiIsIkFuaW1hdGlvblN0YXJ0IiksdHJhbnNpdGlvbmVuZDpWZSgiVHJhbnNpdGlvbiIsIlRyYW5zaXRpb25FbmQiKX0sWGU9e30sWWU9e307CmlhJiYoWWU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iikuc3R5bGUsIkFuaW1hdGlvbkV2ZW50ImluIHdpbmRvd3x8KGRlbGV0ZSBXZS5hbmltYXRpb25lbmQuYW5pbWF0aW9uLGRlbGV0ZSBXZS5hbmltYXRpb25pdGVyYXRpb24uYW5pbWF0aW9uLGRlbGV0ZSBXZS5hbmltYXRpb25zdGFydC5hbmltYXRpb24pLCJUcmFuc2l0aW9uRXZlbnQiaW4gd2luZG93fHxkZWxldGUgV2UudHJhbnNpdGlvbmVuZC50cmFuc2l0aW9uKTtmdW5jdGlvbiBaZShhKXtpZihYZVthXSlyZXR1cm4gWGVbYV07aWYoIVdlW2FdKXJldHVybiBhO3ZhciBiPVdlW2FdLGM7Zm9yKGMgaW4gYilpZihiLmhhc093blByb3BlcnR5KGMpJiZjIGluIFllKXJldHVybiBYZVthXT1iW2NdO3JldHVybiBhfXZhciAkZT1aZSgiYW5pbWF0aW9uZW5kIiksYWY9WmUoImFuaW1hdGlvbml0ZXJhdGlvbiIpLGJmPVplKCJhbmltYXRpb25zdGFydCIpLGNmPVplKCJ0cmFuc2l0aW9uZW5kIiksZGY9bmV3IE1hcCxlZj0iYWJvcnQgYXV4Q2xpY2sgY2FuY2VsIGNhblBsYXkgY2FuUGxheVRocm91Z2ggY2xpY2sgY2xvc2UgY29udGV4dE1lbnUgY29weSBjdXQgZHJhZyBkcmFnRW5kIGRyYWdFbnRlciBkcmFnRXhpdCBkcmFnTGVhdmUgZHJhZ092ZXIgZHJhZ1N0YXJ0IGRyb3AgZHVyYXRpb25DaGFuZ2UgZW1wdGllZCBlbmNyeXB0ZWQgZW5kZWQgZXJyb3IgZ290UG9pbnRlckNhcHR1cmUgaW5wdXQgaW52YWxpZCBrZXlEb3duIGtleVByZXNzIGtleVVwIGxvYWQgbG9hZGVkRGF0YSBsb2FkZWRNZXRhZGF0YSBsb2FkU3RhcnQgbG9zdFBvaW50ZXJDYXB0dXJlIG1vdXNlRG93biBtb3VzZU1vdmUgbW91c2VPdXQgbW91c2VPdmVyIG1vdXNlVXAgcGFzdGUgcGF1c2UgcGxheSBwbGF5aW5nIHBvaW50ZXJDYW5jZWwgcG9pbnRlckRvd24gcG9pbnRlck1vdmUgcG9pbnRlck91dCBwb2ludGVyT3ZlciBwb2ludGVyVXAgcHJvZ3Jlc3MgcmF0ZUNoYW5nZSByZXNldCByZXNpemUgc2Vla2VkIHNlZWtpbmcgc3RhbGxlZCBzdWJtaXQgc3VzcGVuZCB0aW1lVXBkYXRlIHRvdWNoQ2FuY2VsIHRvdWNoRW5kIHRvdWNoU3RhcnQgdm9sdW1lQ2hhbmdlIHNjcm9sbCB0b2dnbGUgdG91Y2hNb3ZlIHdhaXRpbmcgd2hlZWwiLnNwbGl0KCIgIik7CmZ1bmN0aW9uIGZmKGEsYil7ZGYuc2V0KGEsYik7ZmEoYixbYV0pfWZvcih2YXIgZ2Y9MDtnZjxlZi5sZW5ndGg7Z2YrKyl7dmFyIGhmPWVmW2dmXSxqZj1oZi50b0xvd2VyQ2FzZSgpLGtmPWhmWzBdLnRvVXBwZXJDYXNlKCkraGYuc2xpY2UoMSk7ZmYoamYsIm9uIitrZil9ZmYoJGUsIm9uQW5pbWF0aW9uRW5kIik7ZmYoYWYsIm9uQW5pbWF0aW9uSXRlcmF0aW9uIik7ZmYoYmYsIm9uQW5pbWF0aW9uU3RhcnQiKTtmZigiZGJsY2xpY2siLCJvbkRvdWJsZUNsaWNrIik7ZmYoImZvY3VzaW4iLCJvbkZvY3VzIik7ZmYoImZvY3Vzb3V0Iiwib25CbHVyIik7ZmYoY2YsIm9uVHJhbnNpdGlvbkVuZCIpO2hhKCJvbk1vdXNlRW50ZXIiLFsibW91c2VvdXQiLCJtb3VzZW92ZXIiXSk7aGEoIm9uTW91c2VMZWF2ZSIsWyJtb3VzZW91dCIsIm1vdXNlb3ZlciJdKTtoYSgib25Qb2ludGVyRW50ZXIiLFsicG9pbnRlcm91dCIsInBvaW50ZXJvdmVyIl0pOwpoYSgib25Qb2ludGVyTGVhdmUiLFsicG9pbnRlcm91dCIsInBvaW50ZXJvdmVyIl0pO2ZhKCJvbkNoYW5nZSIsImNoYW5nZSBjbGljayBmb2N1c2luIGZvY3Vzb3V0IGlucHV0IGtleWRvd24ga2V5dXAgc2VsZWN0aW9uY2hhbmdlIi5zcGxpdCgiICIpKTtmYSgib25TZWxlY3QiLCJmb2N1c291dCBjb250ZXh0bWVudSBkcmFnZW5kIGZvY3VzaW4ga2V5ZG93biBrZXl1cCBtb3VzZWRvd24gbW91c2V1cCBzZWxlY3Rpb25jaGFuZ2UiLnNwbGl0KCIgIikpO2ZhKCJvbkJlZm9yZUlucHV0IixbImNvbXBvc2l0aW9uZW5kIiwia2V5cHJlc3MiLCJ0ZXh0SW5wdXQiLCJwYXN0ZSJdKTtmYSgib25Db21wb3NpdGlvbkVuZCIsImNvbXBvc2l0aW9uZW5kIGZvY3Vzb3V0IGtleWRvd24ga2V5cHJlc3Mga2V5dXAgbW91c2Vkb3duIi5zcGxpdCgiICIpKTtmYSgib25Db21wb3NpdGlvblN0YXJ0IiwiY29tcG9zaXRpb25zdGFydCBmb2N1c291dCBrZXlkb3duIGtleXByZXNzIGtleXVwIG1vdXNlZG93biIuc3BsaXQoIiAiKSk7CmZhKCJvbkNvbXBvc2l0aW9uVXBkYXRlIiwiY29tcG9zaXRpb251cGRhdGUgZm9jdXNvdXQga2V5ZG93biBrZXlwcmVzcyBrZXl1cCBtb3VzZWRvd24iLnNwbGl0KCIgIikpO3ZhciBsZj0iYWJvcnQgY2FucGxheSBjYW5wbGF5dGhyb3VnaCBkdXJhdGlvbmNoYW5nZSBlbXB0aWVkIGVuY3J5cHRlZCBlbmRlZCBlcnJvciBsb2FkZWRkYXRhIGxvYWRlZG1ldGFkYXRhIGxvYWRzdGFydCBwYXVzZSBwbGF5IHBsYXlpbmcgcHJvZ3Jlc3MgcmF0ZWNoYW5nZSByZXNpemUgc2Vla2VkIHNlZWtpbmcgc3RhbGxlZCBzdXNwZW5kIHRpbWV1cGRhdGUgdm9sdW1lY2hhbmdlIHdhaXRpbmciLnNwbGl0KCIgIiksbWY9bmV3IFNldCgiY2FuY2VsIGNsb3NlIGludmFsaWQgbG9hZCBzY3JvbGwgdG9nZ2xlIi5zcGxpdCgiICIpLmNvbmNhdChsZikpOwpmdW5jdGlvbiBuZihhLGIsYyl7dmFyIGQ9YS50eXBlfHwidW5rbm93bi1ldmVudCI7YS5jdXJyZW50VGFyZ2V0PWM7VWIoZCxiLHZvaWQgMCxhKTthLmN1cnJlbnRUYXJnZXQ9bnVsbH0KZnVuY3Rpb24gc2UoYSxiKXtiPTAhPT0oYiY0KTtmb3IodmFyIGM9MDtjPGEubGVuZ3RoO2MrKyl7dmFyIGQ9YVtjXSxlPWQuZXZlbnQ7ZD1kLmxpc3RlbmVyczthOnt2YXIgZj12b2lkIDA7aWYoYilmb3IodmFyIGc9ZC5sZW5ndGgtMTswPD1nO2ctLSl7dmFyIGg9ZFtnXSxrPWguaW5zdGFuY2UsbD1oLmN1cnJlbnRUYXJnZXQ7aD1oLmxpc3RlbmVyO2lmKGshPT1mJiZlLmlzUHJvcGFnYXRpb25TdG9wcGVkKCkpYnJlYWsgYTtuZihlLGgsbCk7Zj1rfWVsc2UgZm9yKGc9MDtnPGQubGVuZ3RoO2crKyl7aD1kW2ddO2s9aC5pbnN0YW5jZTtsPWguY3VycmVudFRhcmdldDtoPWgubGlzdGVuZXI7aWYoayE9PWYmJmUuaXNQcm9wYWdhdGlvblN0b3BwZWQoKSlicmVhayBhO25mKGUsaCxsKTtmPWt9fX1pZihRYil0aHJvdyBhPVJiLFFiPSExLFJiPW51bGwsYTt9CmZ1bmN0aW9uIEQoYSxiKXt2YXIgYz1iW29mXTt2b2lkIDA9PT1jJiYoYz1iW29mXT1uZXcgU2V0KTt2YXIgZD1hKyJfX2J1YmJsZSI7Yy5oYXMoZCl8fChwZihiLGEsMiwhMSksYy5hZGQoZCkpfWZ1bmN0aW9uIHFmKGEsYixjKXt2YXIgZD0wO2ImJihkfD00KTtwZihjLGEsZCxiKX12YXIgcmY9Il9yZWFjdExpc3RlbmluZyIrTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMik7ZnVuY3Rpb24gc2YoYSl7aWYoIWFbcmZdKXthW3JmXT0hMDtkYS5mb3JFYWNoKGZ1bmN0aW9uKGIpeyJzZWxlY3Rpb25jaGFuZ2UiIT09YiYmKG1mLmhhcyhiKXx8cWYoYiwhMSxhKSxxZihiLCEwLGEpKX0pO3ZhciBiPTk9PT1hLm5vZGVUeXBlP2E6YS5vd25lckRvY3VtZW50O251bGw9PT1ifHxiW3JmXXx8KGJbcmZdPSEwLHFmKCJzZWxlY3Rpb25jaGFuZ2UiLCExLGIpKX19CmZ1bmN0aW9uIHBmKGEsYixjLGQpe3N3aXRjaChqZChiKSl7Y2FzZSAxOnZhciBlPWVkO2JyZWFrO2Nhc2UgNDplPWdkO2JyZWFrO2RlZmF1bHQ6ZT1mZH1jPWUuYmluZChudWxsLGIsYyxhKTtlPXZvaWQgMDshTGJ8fCJ0b3VjaHN0YXJ0IiE9PWImJiJ0b3VjaG1vdmUiIT09YiYmIndoZWVsIiE9PWJ8fChlPSEwKTtkP3ZvaWQgMCE9PWU/YS5hZGRFdmVudExpc3RlbmVyKGIsYyx7Y2FwdHVyZTohMCxwYXNzaXZlOmV9KTphLmFkZEV2ZW50TGlzdGVuZXIoYixjLCEwKTp2b2lkIDAhPT1lP2EuYWRkRXZlbnRMaXN0ZW5lcihiLGMse3Bhc3NpdmU6ZX0pOmEuYWRkRXZlbnRMaXN0ZW5lcihiLGMsITEpfQpmdW5jdGlvbiBoZChhLGIsYyxkLGUpe3ZhciBmPWQ7aWYoMD09PShiJjEpJiYwPT09KGImMikmJm51bGwhPT1kKWE6Zm9yKDs7KXtpZihudWxsPT09ZClyZXR1cm47dmFyIGc9ZC50YWc7aWYoMz09PWd8fDQ9PT1nKXt2YXIgaD1kLnN0YXRlTm9kZS5jb250YWluZXJJbmZvO2lmKGg9PT1lfHw4PT09aC5ub2RlVHlwZSYmaC5wYXJlbnROb2RlPT09ZSlicmVhaztpZig0PT09Zylmb3IoZz1kLnJldHVybjtudWxsIT09Zzspe3ZhciBrPWcudGFnO2lmKDM9PT1rfHw0PT09aylpZihrPWcuc3RhdGVOb2RlLmNvbnRhaW5lckluZm8saz09PWV8fDg9PT1rLm5vZGVUeXBlJiZrLnBhcmVudE5vZGU9PT1lKXJldHVybjtnPWcucmV0dXJufWZvcig7bnVsbCE9PWg7KXtnPVdjKGgpO2lmKG51bGw9PT1nKXJldHVybjtrPWcudGFnO2lmKDU9PT1rfHw2PT09ayl7ZD1mPWc7Y29udGludWUgYX1oPWgucGFyZW50Tm9kZX19ZD1kLnJldHVybn1KYihmdW5jdGlvbigpe3ZhciBkPWYsZT14YihjKSxnPVtdOwphOnt2YXIgaD1kZi5nZXQoYSk7aWYodm9pZCAwIT09aCl7dmFyIGs9dGQsbj1hO3N3aXRjaChhKXtjYXNlICJrZXlwcmVzcyI6aWYoMD09PW9kKGMpKWJyZWFrIGE7Y2FzZSAia2V5ZG93biI6Y2FzZSAia2V5dXAiOms9UmQ7YnJlYWs7Y2FzZSAiZm9jdXNpbiI6bj0iZm9jdXMiO2s9RmQ7YnJlYWs7Y2FzZSAiZm9jdXNvdXQiOm49ImJsdXIiO2s9RmQ7YnJlYWs7Y2FzZSAiYmVmb3JlYmx1ciI6Y2FzZSAiYWZ0ZXJibHVyIjprPUZkO2JyZWFrO2Nhc2UgImNsaWNrIjppZigyPT09Yy5idXR0b24pYnJlYWsgYTtjYXNlICJhdXhjbGljayI6Y2FzZSAiZGJsY2xpY2siOmNhc2UgIm1vdXNlZG93biI6Y2FzZSAibW91c2Vtb3ZlIjpjYXNlICJtb3VzZXVwIjpjYXNlICJtb3VzZW91dCI6Y2FzZSAibW91c2VvdmVyIjpjYXNlICJjb250ZXh0bWVudSI6az1CZDticmVhaztjYXNlICJkcmFnIjpjYXNlICJkcmFnZW5kIjpjYXNlICJkcmFnZW50ZXIiOmNhc2UgImRyYWdleGl0IjpjYXNlICJkcmFnbGVhdmUiOmNhc2UgImRyYWdvdmVyIjpjYXNlICJkcmFnc3RhcnQiOmNhc2UgImRyb3AiOms9CkRkO2JyZWFrO2Nhc2UgInRvdWNoY2FuY2VsIjpjYXNlICJ0b3VjaGVuZCI6Y2FzZSAidG91Y2htb3ZlIjpjYXNlICJ0b3VjaHN0YXJ0IjprPVZkO2JyZWFrO2Nhc2UgJGU6Y2FzZSBhZjpjYXNlIGJmOms9SGQ7YnJlYWs7Y2FzZSBjZjprPVhkO2JyZWFrO2Nhc2UgInNjcm9sbCI6az12ZDticmVhaztjYXNlICJ3aGVlbCI6az1aZDticmVhaztjYXNlICJjb3B5IjpjYXNlICJjdXQiOmNhc2UgInBhc3RlIjprPUpkO2JyZWFrO2Nhc2UgImdvdHBvaW50ZXJjYXB0dXJlIjpjYXNlICJsb3N0cG9pbnRlcmNhcHR1cmUiOmNhc2UgInBvaW50ZXJjYW5jZWwiOmNhc2UgInBvaW50ZXJkb3duIjpjYXNlICJwb2ludGVybW92ZSI6Y2FzZSAicG9pbnRlcm91dCI6Y2FzZSAicG9pbnRlcm92ZXIiOmNhc2UgInBvaW50ZXJ1cCI6az1UZH12YXIgdD0wIT09KGImNCksSj0hdCYmInNjcm9sbCI9PT1hLHg9dD9udWxsIT09aD9oKyJDYXB0dXJlIjpudWxsOmg7dD1bXTtmb3IodmFyIHc9ZCx1O251bGwhPT0Kdzspe3U9dzt2YXIgRj11LnN0YXRlTm9kZTs1PT09dS50YWcmJm51bGwhPT1GJiYodT1GLG51bGwhPT14JiYoRj1LYih3LHgpLG51bGwhPUYmJnQucHVzaCh0Zih3LEYsdSkpKSk7aWYoSilicmVhazt3PXcucmV0dXJufTA8dC5sZW5ndGgmJihoPW5ldyBrKGgsbixudWxsLGMsZSksZy5wdXNoKHtldmVudDpoLGxpc3RlbmVyczp0fSkpfX1pZigwPT09KGImNykpe2E6e2g9Im1vdXNlb3ZlciI9PT1hfHwicG9pbnRlcm92ZXIiPT09YTtrPSJtb3VzZW91dCI9PT1hfHwicG9pbnRlcm91dCI9PT1hO2lmKGgmJmMhPT13YiYmKG49Yy5yZWxhdGVkVGFyZ2V0fHxjLmZyb21FbGVtZW50KSYmKFdjKG4pfHxuW3VmXSkpYnJlYWsgYTtpZihrfHxoKXtoPWUud2luZG93PT09ZT9lOihoPWUub3duZXJEb2N1bWVudCk/aC5kZWZhdWx0Vmlld3x8aC5wYXJlbnRXaW5kb3c6d2luZG93O2lmKGspe2lmKG49Yy5yZWxhdGVkVGFyZ2V0fHxjLnRvRWxlbWVudCxrPWQsbj1uP1djKG4pOm51bGwsbnVsbCE9PQpuJiYoSj1WYihuKSxuIT09Snx8NSE9PW4udGFnJiY2IT09bi50YWcpKW49bnVsbH1lbHNlIGs9bnVsbCxuPWQ7aWYoayE9PW4pe3Q9QmQ7Rj0ib25Nb3VzZUxlYXZlIjt4PSJvbk1vdXNlRW50ZXIiO3c9Im1vdXNlIjtpZigicG9pbnRlcm91dCI9PT1hfHwicG9pbnRlcm92ZXIiPT09YSl0PVRkLEY9Im9uUG9pbnRlckxlYXZlIix4PSJvblBvaW50ZXJFbnRlciIsdz0icG9pbnRlciI7Sj1udWxsPT1rP2g6dWUoayk7dT1udWxsPT1uP2g6dWUobik7aD1uZXcgdChGLHcrImxlYXZlIixrLGMsZSk7aC50YXJnZXQ9SjtoLnJlbGF0ZWRUYXJnZXQ9dTtGPW51bGw7V2MoZSk9PT1kJiYodD1uZXcgdCh4LHcrImVudGVyIixuLGMsZSksdC50YXJnZXQ9dSx0LnJlbGF0ZWRUYXJnZXQ9SixGPXQpO0o9RjtpZihrJiZuKWI6e3Q9azt4PW47dz0wO2Zvcih1PXQ7dTt1PXZmKHUpKXcrKzt1PTA7Zm9yKEY9eDtGO0Y9dmYoRikpdSsrO2Zvcig7MDx3LXU7KXQ9dmYodCksdy0tO2Zvcig7MDx1LXc7KXg9CnZmKHgpLHUtLTtmb3IoO3ctLTspe2lmKHQ9PT14fHxudWxsIT09eCYmdD09PXguYWx0ZXJuYXRlKWJyZWFrIGI7dD12Zih0KTt4PXZmKHgpfXQ9bnVsbH1lbHNlIHQ9bnVsbDtudWxsIT09ayYmd2YoZyxoLGssdCwhMSk7bnVsbCE9PW4mJm51bGwhPT1KJiZ3ZihnLEosbix0LCEwKX19fWE6e2g9ZD91ZShkKTp3aW5kb3c7az1oLm5vZGVOYW1lJiZoLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7aWYoInNlbGVjdCI9PT1rfHwiaW5wdXQiPT09ayYmImZpbGUiPT09aC50eXBlKXZhciBuYT12ZTtlbHNlIGlmKG1lKGgpKWlmKHdlKW5hPUZlO2Vsc2V7bmE9RGU7dmFyIHhhPUNlfWVsc2Uoaz1oLm5vZGVOYW1lKSYmImlucHV0Ij09PWsudG9Mb3dlckNhc2UoKSYmKCJjaGVja2JveCI9PT1oLnR5cGV8fCJyYWRpbyI9PT1oLnR5cGUpJiYobmE9RWUpO2lmKG5hJiYobmE9bmEoYSxkKSkpe25lKGcsbmEsYyxlKTticmVhayBhfXhhJiZ4YShhLGgsZCk7ImZvY3Vzb3V0Ij09PWEmJih4YT1oLl93cmFwcGVyU3RhdGUpJiYKeGEuY29udHJvbGxlZCYmIm51bWJlciI9PT1oLnR5cGUmJmNiKGgsIm51bWJlciIsaC52YWx1ZSl9eGE9ZD91ZShkKTp3aW5kb3c7c3dpdGNoKGEpe2Nhc2UgImZvY3VzaW4iOmlmKG1lKHhhKXx8InRydWUiPT09eGEuY29udGVudEVkaXRhYmxlKVFlPXhhLFJlPWQsU2U9bnVsbDticmVhaztjYXNlICJmb2N1c291dCI6U2U9UmU9UWU9bnVsbDticmVhaztjYXNlICJtb3VzZWRvd24iOlRlPSEwO2JyZWFrO2Nhc2UgImNvbnRleHRtZW51IjpjYXNlICJtb3VzZXVwIjpjYXNlICJkcmFnZW5kIjpUZT0hMTtVZShnLGMsZSk7YnJlYWs7Y2FzZSAic2VsZWN0aW9uY2hhbmdlIjppZihQZSlicmVhaztjYXNlICJrZXlkb3duIjpjYXNlICJrZXl1cCI6VWUoZyxjLGUpfXZhciAkYTtpZihhZSliOntzd2l0Y2goYSl7Y2FzZSAiY29tcG9zaXRpb25zdGFydCI6dmFyIGJhPSJvbkNvbXBvc2l0aW9uU3RhcnQiO2JyZWFrIGI7Y2FzZSAiY29tcG9zaXRpb25lbmQiOmJhPSJvbkNvbXBvc2l0aW9uRW5kIjsKYnJlYWsgYjtjYXNlICJjb21wb3NpdGlvbnVwZGF0ZSI6YmE9Im9uQ29tcG9zaXRpb25VcGRhdGUiO2JyZWFrIGJ9YmE9dm9pZCAwfWVsc2UgaWU/Z2UoYSxjKSYmKGJhPSJvbkNvbXBvc2l0aW9uRW5kIik6ImtleWRvd24iPT09YSYmMjI5PT09Yy5rZXlDb2RlJiYoYmE9Im9uQ29tcG9zaXRpb25TdGFydCIpO2JhJiYoZGUmJiJrbyIhPT1jLmxvY2FsZSYmKGllfHwib25Db21wb3NpdGlvblN0YXJ0IiE9PWJhPyJvbkNvbXBvc2l0aW9uRW5kIj09PWJhJiZpZSYmKCRhPW5kKCkpOihrZD1lLGxkPSJ2YWx1ZSJpbiBrZD9rZC52YWx1ZTprZC50ZXh0Q29udGVudCxpZT0hMCkpLHhhPW9lKGQsYmEpLDA8eGEubGVuZ3RoJiYoYmE9bmV3IExkKGJhLGEsbnVsbCxjLGUpLGcucHVzaCh7ZXZlbnQ6YmEsbGlzdGVuZXJzOnhhfSksJGE/YmEuZGF0YT0kYTooJGE9aGUoYyksbnVsbCE9PSRhJiYoYmEuZGF0YT0kYSkpKSk7aWYoJGE9Y2U/amUoYSxjKTprZShhLGMpKWQ9b2UoZCwib25CZWZvcmVJbnB1dCIpLAowPGQubGVuZ3RoJiYoZT1uZXcgTGQoIm9uQmVmb3JlSW5wdXQiLCJiZWZvcmVpbnB1dCIsbnVsbCxjLGUpLGcucHVzaCh7ZXZlbnQ6ZSxsaXN0ZW5lcnM6ZH0pLGUuZGF0YT0kYSl9c2UoZyxiKX0pfWZ1bmN0aW9uIHRmKGEsYixjKXtyZXR1cm57aW5zdGFuY2U6YSxsaXN0ZW5lcjpiLGN1cnJlbnRUYXJnZXQ6Y319ZnVuY3Rpb24gb2UoYSxiKXtmb3IodmFyIGM9YisiQ2FwdHVyZSIsZD1bXTtudWxsIT09YTspe3ZhciBlPWEsZj1lLnN0YXRlTm9kZTs1PT09ZS50YWcmJm51bGwhPT1mJiYoZT1mLGY9S2IoYSxjKSxudWxsIT1mJiZkLnVuc2hpZnQodGYoYSxmLGUpKSxmPUtiKGEsYiksbnVsbCE9ZiYmZC5wdXNoKHRmKGEsZixlKSkpO2E9YS5yZXR1cm59cmV0dXJuIGR9ZnVuY3Rpb24gdmYoYSl7aWYobnVsbD09PWEpcmV0dXJuIG51bGw7ZG8gYT1hLnJldHVybjt3aGlsZShhJiY1IT09YS50YWcpO3JldHVybiBhP2E6bnVsbH0KZnVuY3Rpb24gd2YoYSxiLGMsZCxlKXtmb3IodmFyIGY9Yi5fcmVhY3ROYW1lLGc9W107bnVsbCE9PWMmJmMhPT1kOyl7dmFyIGg9YyxrPWguYWx0ZXJuYXRlLGw9aC5zdGF0ZU5vZGU7aWYobnVsbCE9PWsmJms9PT1kKWJyZWFrOzU9PT1oLnRhZyYmbnVsbCE9PWwmJihoPWwsZT8oaz1LYihjLGYpLG51bGwhPWsmJmcudW5zaGlmdCh0ZihjLGssaCkpKTplfHwoaz1LYihjLGYpLG51bGwhPWsmJmcucHVzaCh0ZihjLGssaCkpKSk7Yz1jLnJldHVybn0wIT09Zy5sZW5ndGgmJmEucHVzaCh7ZXZlbnQ6YixsaXN0ZW5lcnM6Z30pfXZhciB4Zj0vXHJcbj8vZyx5Zj0vXHUwMDAwfFx1RkZGRC9nO2Z1bmN0aW9uIHpmKGEpe3JldHVybigic3RyaW5nIj09PXR5cGVvZiBhP2E6IiIrYSkucmVwbGFjZSh4ZiwiXG4iKS5yZXBsYWNlKHlmLCIiKX1mdW5jdGlvbiBBZihhLGIsYyl7Yj16ZihiKTtpZih6ZihhKSE9PWImJmMpdGhyb3cgRXJyb3IocCg0MjUpKTt9ZnVuY3Rpb24gQmYoKXt9CnZhciBDZj1udWxsLERmPW51bGw7ZnVuY3Rpb24gRWYoYSxiKXtyZXR1cm4idGV4dGFyZWEiPT09YXx8Im5vc2NyaXB0Ij09PWF8fCJzdHJpbmciPT09dHlwZW9mIGIuY2hpbGRyZW58fCJudW1iZXIiPT09dHlwZW9mIGIuY2hpbGRyZW58fCJvYmplY3QiPT09dHlwZW9mIGIuZGFuZ2Vyb3VzbHlTZXRJbm5lckhUTUwmJm51bGwhPT1iLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MJiZudWxsIT1iLmRhbmdlcm91c2x5U2V0SW5uZXJIVE1MLl9faHRtbH0KdmFyIEZmPSJmdW5jdGlvbiI9PT10eXBlb2Ygc2V0VGltZW91dD9zZXRUaW1lb3V0OnZvaWQgMCxHZj0iZnVuY3Rpb24iPT09dHlwZW9mIGNsZWFyVGltZW91dD9jbGVhclRpbWVvdXQ6dm9pZCAwLEhmPSJmdW5jdGlvbiI9PT10eXBlb2YgUHJvbWlzZT9Qcm9taXNlOnZvaWQgMCxKZj0iZnVuY3Rpb24iPT09dHlwZW9mIHF1ZXVlTWljcm90YXNrP3F1ZXVlTWljcm90YXNrOiJ1bmRlZmluZWQiIT09dHlwZW9mIEhmP2Z1bmN0aW9uKGEpe3JldHVybiBIZi5yZXNvbHZlKG51bGwpLnRoZW4oYSkuY2F0Y2goSWYpfTpGZjtmdW5jdGlvbiBJZihhKXtzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7dGhyb3cgYTt9KX0KZnVuY3Rpb24gS2YoYSxiKXt2YXIgYz1iLGQ9MDtkb3t2YXIgZT1jLm5leHRTaWJsaW5nO2EucmVtb3ZlQ2hpbGQoYyk7aWYoZSYmOD09PWUubm9kZVR5cGUpaWYoYz1lLmRhdGEsIi8kIj09PWMpe2lmKDA9PT1kKXthLnJlbW92ZUNoaWxkKGUpO2JkKGIpO3JldHVybn1kLS19ZWxzZSIkIiE9PWMmJiIkPyIhPT1jJiYiJCEiIT09Y3x8ZCsrO2M9ZX13aGlsZShjKTtiZChiKX1mdW5jdGlvbiBMZihhKXtmb3IoO251bGwhPWE7YT1hLm5leHRTaWJsaW5nKXt2YXIgYj1hLm5vZGVUeXBlO2lmKDE9PT1ifHwzPT09YilicmVhaztpZig4PT09Yil7Yj1hLmRhdGE7aWYoIiQiPT09Ynx8IiQhIj09PWJ8fCIkPyI9PT1iKWJyZWFrO2lmKCIvJCI9PT1iKXJldHVybiBudWxsfX1yZXR1cm4gYX0KZnVuY3Rpb24gTWYoYSl7YT1hLnByZXZpb3VzU2libGluZztmb3IodmFyIGI9MDthOyl7aWYoOD09PWEubm9kZVR5cGUpe3ZhciBjPWEuZGF0YTtpZigiJCI9PT1jfHwiJCEiPT09Y3x8IiQ/Ij09PWMpe2lmKDA9PT1iKXJldHVybiBhO2ItLX1lbHNlIi8kIj09PWMmJmIrK31hPWEucHJldmlvdXNTaWJsaW5nfXJldHVybiBudWxsfXZhciBOZj1NYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyKSxPZj0iX19yZWFjdEZpYmVyJCIrTmYsUGY9Il9fcmVhY3RQcm9wcyQiK05mLHVmPSJfX3JlYWN0Q29udGFpbmVyJCIrTmYsb2Y9Il9fcmVhY3RFdmVudHMkIitOZixRZj0iX19yZWFjdExpc3RlbmVycyQiK05mLFJmPSJfX3JlYWN0SGFuZGxlcyQiK05mOwpmdW5jdGlvbiBXYyhhKXt2YXIgYj1hW09mXTtpZihiKXJldHVybiBiO2Zvcih2YXIgYz1hLnBhcmVudE5vZGU7Yzspe2lmKGI9Y1t1Zl18fGNbT2ZdKXtjPWIuYWx0ZXJuYXRlO2lmKG51bGwhPT1iLmNoaWxkfHxudWxsIT09YyYmbnVsbCE9PWMuY2hpbGQpZm9yKGE9TWYoYSk7bnVsbCE9PWE7KXtpZihjPWFbT2ZdKXJldHVybiBjO2E9TWYoYSl9cmV0dXJuIGJ9YT1jO2M9YS5wYXJlbnROb2RlfXJldHVybiBudWxsfWZ1bmN0aW9uIENiKGEpe2E9YVtPZl18fGFbdWZdO3JldHVybiFhfHw1IT09YS50YWcmJjYhPT1hLnRhZyYmMTMhPT1hLnRhZyYmMyE9PWEudGFnP251bGw6YX1mdW5jdGlvbiB1ZShhKXtpZig1PT09YS50YWd8fDY9PT1hLnRhZylyZXR1cm4gYS5zdGF0ZU5vZGU7dGhyb3cgRXJyb3IocCgzMykpO31mdW5jdGlvbiBEYihhKXtyZXR1cm4gYVtQZl18fG51bGx9dmFyIFNmPVtdLFRmPS0xO2Z1bmN0aW9uIFVmKGEpe3JldHVybntjdXJyZW50OmF9fQpmdW5jdGlvbiBFKGEpezA+VGZ8fChhLmN1cnJlbnQ9U2ZbVGZdLFNmW1RmXT1udWxsLFRmLS0pfWZ1bmN0aW9uIEcoYSxiKXtUZisrO1NmW1RmXT1hLmN1cnJlbnQ7YS5jdXJyZW50PWJ9dmFyIFZmPXt9LEg9VWYoVmYpLFdmPVVmKCExKSxYZj1WZjtmdW5jdGlvbiBZZihhLGIpe3ZhciBjPWEudHlwZS5jb250ZXh0VHlwZXM7aWYoIWMpcmV0dXJuIFZmO3ZhciBkPWEuc3RhdGVOb2RlO2lmKGQmJmQuX19yZWFjdEludGVybmFsTWVtb2l6ZWRVbm1hc2tlZENoaWxkQ29udGV4dD09PWIpcmV0dXJuIGQuX19yZWFjdEludGVybmFsTWVtb2l6ZWRNYXNrZWRDaGlsZENvbnRleHQ7dmFyIGU9e30sZjtmb3IoZiBpbiBjKWVbZl09YltmXTtkJiYoYT1hLnN0YXRlTm9kZSxhLl9fcmVhY3RJbnRlcm5hbE1lbW9pemVkVW5tYXNrZWRDaGlsZENvbnRleHQ9YixhLl9fcmVhY3RJbnRlcm5hbE1lbW9pemVkTWFza2VkQ2hpbGRDb250ZXh0PWUpO3JldHVybiBlfQpmdW5jdGlvbiBaZihhKXthPWEuY2hpbGRDb250ZXh0VHlwZXM7cmV0dXJuIG51bGwhPT1hJiZ2b2lkIDAhPT1hfWZ1bmN0aW9uICRmKCl7RShXZik7RShIKX1mdW5jdGlvbiBhZyhhLGIsYyl7aWYoSC5jdXJyZW50IT09VmYpdGhyb3cgRXJyb3IocCgxNjgpKTtHKEgsYik7RyhXZixjKX1mdW5jdGlvbiBiZyhhLGIsYyl7dmFyIGQ9YS5zdGF0ZU5vZGU7Yj1iLmNoaWxkQ29udGV4dFR5cGVzO2lmKCJmdW5jdGlvbiIhPT10eXBlb2YgZC5nZXRDaGlsZENvbnRleHQpcmV0dXJuIGM7ZD1kLmdldENoaWxkQ29udGV4dCgpO2Zvcih2YXIgZSBpbiBkKWlmKCEoZSBpbiBiKSl0aHJvdyBFcnJvcihwKDEwOCxSYShhKXx8IlVua25vd24iLGUpKTtyZXR1cm4gQSh7fSxjLGQpfQpmdW5jdGlvbiBjZyhhKXthPShhPWEuc3RhdGVOb2RlKSYmYS5fX3JlYWN0SW50ZXJuYWxNZW1vaXplZE1lcmdlZENoaWxkQ29udGV4dHx8VmY7WGY9SC5jdXJyZW50O0coSCxhKTtHKFdmLFdmLmN1cnJlbnQpO3JldHVybiEwfWZ1bmN0aW9uIGRnKGEsYixjKXt2YXIgZD1hLnN0YXRlTm9kZTtpZighZCl0aHJvdyBFcnJvcihwKDE2OSkpO2M/KGE9YmcoYSxiLFhmKSxkLl9fcmVhY3RJbnRlcm5hbE1lbW9pemVkTWVyZ2VkQ2hpbGRDb250ZXh0PWEsRShXZiksRShIKSxHKEgsYSkpOkUoV2YpO0coV2YsYyl9dmFyIGVnPW51bGwsZmc9ITEsZ2c9ITE7ZnVuY3Rpb24gaGcoYSl7bnVsbD09PWVnP2VnPVthXTplZy5wdXNoKGEpfWZ1bmN0aW9uIGlnKGEpe2ZnPSEwO2hnKGEpfQpmdW5jdGlvbiBqZygpe2lmKCFnZyYmbnVsbCE9PWVnKXtnZz0hMDt2YXIgYT0wLGI9Qzt0cnl7dmFyIGM9ZWc7Zm9yKEM9MTthPGMubGVuZ3RoO2ErKyl7dmFyIGQ9Y1thXTtkbyBkPWQoITApO3doaWxlKG51bGwhPT1kKX1lZz1udWxsO2ZnPSExfWNhdGNoKGUpe3Rocm93IG51bGwhPT1lZyYmKGVnPWVnLnNsaWNlKGErMSkpLGFjKGZjLGpnKSxlO31maW5hbGx5e0M9YixnZz0hMX19cmV0dXJuIG51bGx9dmFyIGtnPVtdLGxnPTAsbWc9bnVsbCxuZz0wLG9nPVtdLHBnPTAscWc9bnVsbCxyZz0xLHNnPSIiO2Z1bmN0aW9uIHRnKGEsYil7a2dbbGcrK109bmc7a2dbbGcrK109bWc7bWc9YTtuZz1ifQpmdW5jdGlvbiB1ZyhhLGIsYyl7b2dbcGcrK109cmc7b2dbcGcrK109c2c7b2dbcGcrK109cWc7cWc9YTt2YXIgZD1yZzthPXNnO3ZhciBlPTMyLW9jKGQpLTE7ZCY9figxPDxlKTtjKz0xO3ZhciBmPTMyLW9jKGIpK2U7aWYoMzA8Zil7dmFyIGc9ZS1lJTU7Zj0oZCYoMTw8ZyktMSkudG9TdHJpbmcoMzIpO2Q+Pj1nO2UtPWc7cmc9MTw8MzItb2MoYikrZXxjPDxlfGQ7c2c9ZithfWVsc2Ugcmc9MTw8ZnxjPDxlfGQsc2c9YX1mdW5jdGlvbiB2ZyhhKXtudWxsIT09YS5yZXR1cm4mJih0ZyhhLDEpLHVnKGEsMSwwKSl9ZnVuY3Rpb24gd2coYSl7Zm9yKDthPT09bWc7KW1nPWtnWy0tbGddLGtnW2xnXT1udWxsLG5nPWtnWy0tbGddLGtnW2xnXT1udWxsO2Zvcig7YT09PXFnOylxZz1vZ1stLXBnXSxvZ1twZ109bnVsbCxzZz1vZ1stLXBnXSxvZ1twZ109bnVsbCxyZz1vZ1stLXBnXSxvZ1twZ109bnVsbH12YXIgeGc9bnVsbCx5Zz1udWxsLEk9ITEsemc9bnVsbDsKZnVuY3Rpb24gQWcoYSxiKXt2YXIgYz1CZyg1LG51bGwsbnVsbCwwKTtjLmVsZW1lbnRUeXBlPSJERUxFVEVEIjtjLnN0YXRlTm9kZT1iO2MucmV0dXJuPWE7Yj1hLmRlbGV0aW9ucztudWxsPT09Yj8oYS5kZWxldGlvbnM9W2NdLGEuZmxhZ3N8PTE2KTpiLnB1c2goYyl9CmZ1bmN0aW9uIENnKGEsYil7c3dpdGNoKGEudGFnKXtjYXNlIDU6dmFyIGM9YS50eXBlO2I9MSE9PWIubm9kZVR5cGV8fGMudG9Mb3dlckNhc2UoKSE9PWIubm9kZU5hbWUudG9Mb3dlckNhc2UoKT9udWxsOmI7cmV0dXJuIG51bGwhPT1iPyhhLnN0YXRlTm9kZT1iLHhnPWEseWc9TGYoYi5maXJzdENoaWxkKSwhMCk6ITE7Y2FzZSA2OnJldHVybiBiPSIiPT09YS5wZW5kaW5nUHJvcHN8fDMhPT1iLm5vZGVUeXBlP251bGw6YixudWxsIT09Yj8oYS5zdGF0ZU5vZGU9Yix4Zz1hLHlnPW51bGwsITApOiExO2Nhc2UgMTM6cmV0dXJuIGI9OCE9PWIubm9kZVR5cGU/bnVsbDpiLG51bGwhPT1iPyhjPW51bGwhPT1xZz97aWQ6cmcsb3ZlcmZsb3c6c2d9Om51bGwsYS5tZW1vaXplZFN0YXRlPXtkZWh5ZHJhdGVkOmIsdHJlZUNvbnRleHQ6YyxyZXRyeUxhbmU6MTA3Mzc0MTgyNH0sYz1CZygxOCxudWxsLG51bGwsMCksYy5zdGF0ZU5vZGU9YixjLnJldHVybj1hLGEuY2hpbGQ9Yyx4Zz1hLHlnPQpudWxsLCEwKTohMTtkZWZhdWx0OnJldHVybiExfX1mdW5jdGlvbiBEZyhhKXtyZXR1cm4gMCE9PShhLm1vZGUmMSkmJjA9PT0oYS5mbGFncyYxMjgpfWZ1bmN0aW9uIEVnKGEpe2lmKEkpe3ZhciBiPXlnO2lmKGIpe3ZhciBjPWI7aWYoIUNnKGEsYikpe2lmKERnKGEpKXRocm93IEVycm9yKHAoNDE4KSk7Yj1MZihjLm5leHRTaWJsaW5nKTt2YXIgZD14ZztiJiZDZyhhLGIpP0FnKGQsYyk6KGEuZmxhZ3M9YS5mbGFncyYtNDA5N3wyLEk9ITEseGc9YSl9fWVsc2V7aWYoRGcoYSkpdGhyb3cgRXJyb3IocCg0MTgpKTthLmZsYWdzPWEuZmxhZ3MmLTQwOTd8MjtJPSExO3hnPWF9fX1mdW5jdGlvbiBGZyhhKXtmb3IoYT1hLnJldHVybjtudWxsIT09YSYmNSE9PWEudGFnJiYzIT09YS50YWcmJjEzIT09YS50YWc7KWE9YS5yZXR1cm47eGc9YX0KZnVuY3Rpb24gR2coYSl7aWYoYSE9PXhnKXJldHVybiExO2lmKCFJKXJldHVybiBGZyhhKSxJPSEwLCExO3ZhciBiOyhiPTMhPT1hLnRhZykmJiEoYj01IT09YS50YWcpJiYoYj1hLnR5cGUsYj0iaGVhZCIhPT1iJiYiYm9keSIhPT1iJiYhRWYoYS50eXBlLGEubWVtb2l6ZWRQcm9wcykpO2lmKGImJihiPXlnKSl7aWYoRGcoYSkpdGhyb3cgSGcoKSxFcnJvcihwKDQxOCkpO2Zvcig7YjspQWcoYSxiKSxiPUxmKGIubmV4dFNpYmxpbmcpfUZnKGEpO2lmKDEzPT09YS50YWcpe2E9YS5tZW1vaXplZFN0YXRlO2E9bnVsbCE9PWE/YS5kZWh5ZHJhdGVkOm51bGw7aWYoIWEpdGhyb3cgRXJyb3IocCgzMTcpKTthOnthPWEubmV4dFNpYmxpbmc7Zm9yKGI9MDthOyl7aWYoOD09PWEubm9kZVR5cGUpe3ZhciBjPWEuZGF0YTtpZigiLyQiPT09Yyl7aWYoMD09PWIpe3lnPUxmKGEubmV4dFNpYmxpbmcpO2JyZWFrIGF9Yi0tfWVsc2UiJCIhPT1jJiYiJCEiIT09YyYmIiQ/IiE9PWN8fGIrK31hPWEubmV4dFNpYmxpbmd9eWc9Cm51bGx9fWVsc2UgeWc9eGc/TGYoYS5zdGF0ZU5vZGUubmV4dFNpYmxpbmcpOm51bGw7cmV0dXJuITB9ZnVuY3Rpb24gSGcoKXtmb3IodmFyIGE9eWc7YTspYT1MZihhLm5leHRTaWJsaW5nKX1mdW5jdGlvbiBJZygpe3lnPXhnPW51bGw7ST0hMX1mdW5jdGlvbiBKZyhhKXtudWxsPT09emc/emc9W2FdOnpnLnB1c2goYSl9dmFyIEtnPXVhLlJlYWN0Q3VycmVudEJhdGNoQ29uZmlnO2Z1bmN0aW9uIExnKGEsYil7aWYoYSYmYS5kZWZhdWx0UHJvcHMpe2I9QSh7fSxiKTthPWEuZGVmYXVsdFByb3BzO2Zvcih2YXIgYyBpbiBhKXZvaWQgMD09PWJbY10mJihiW2NdPWFbY10pO3JldHVybiBifXJldHVybiBifXZhciBNZz1VZihudWxsKSxOZz1udWxsLE9nPW51bGwsUGc9bnVsbDtmdW5jdGlvbiBRZygpe1BnPU9nPU5nPW51bGx9ZnVuY3Rpb24gUmcoYSl7dmFyIGI9TWcuY3VycmVudDtFKE1nKTthLl9jdXJyZW50VmFsdWU9Yn0KZnVuY3Rpb24gU2coYSxiLGMpe2Zvcig7bnVsbCE9PWE7KXt2YXIgZD1hLmFsdGVybmF0ZTsoYS5jaGlsZExhbmVzJmIpIT09Yj8oYS5jaGlsZExhbmVzfD1iLG51bGwhPT1kJiYoZC5jaGlsZExhbmVzfD1iKSk6bnVsbCE9PWQmJihkLmNoaWxkTGFuZXMmYikhPT1iJiYoZC5jaGlsZExhbmVzfD1iKTtpZihhPT09YylicmVhazthPWEucmV0dXJufX1mdW5jdGlvbiBUZyhhLGIpe05nPWE7UGc9T2c9bnVsbDthPWEuZGVwZW5kZW5jaWVzO251bGwhPT1hJiZudWxsIT09YS5maXJzdENvbnRleHQmJigwIT09KGEubGFuZXMmYikmJihVZz0hMCksYS5maXJzdENvbnRleHQ9bnVsbCl9CmZ1bmN0aW9uIFZnKGEpe3ZhciBiPWEuX2N1cnJlbnRWYWx1ZTtpZihQZyE9PWEpaWYoYT17Y29udGV4dDphLG1lbW9pemVkVmFsdWU6YixuZXh0Om51bGx9LG51bGw9PT1PZyl7aWYobnVsbD09PU5nKXRocm93IEVycm9yKHAoMzA4KSk7T2c9YTtOZy5kZXBlbmRlbmNpZXM9e2xhbmVzOjAsZmlyc3RDb250ZXh0OmF9fWVsc2UgT2c9T2cubmV4dD1hO3JldHVybiBifXZhciBXZz1udWxsO2Z1bmN0aW9uIFhnKGEpe251bGw9PT1XZz9XZz1bYV06V2cucHVzaChhKX1mdW5jdGlvbiBZZyhhLGIsYyxkKXt2YXIgZT1iLmludGVybGVhdmVkO251bGw9PT1lPyhjLm5leHQ9YyxYZyhiKSk6KGMubmV4dD1lLm5leHQsZS5uZXh0PWMpO2IuaW50ZXJsZWF2ZWQ9YztyZXR1cm4gWmcoYSxkKX0KZnVuY3Rpb24gWmcoYSxiKXthLmxhbmVzfD1iO3ZhciBjPWEuYWx0ZXJuYXRlO251bGwhPT1jJiYoYy5sYW5lc3w9Yik7Yz1hO2ZvcihhPWEucmV0dXJuO251bGwhPT1hOylhLmNoaWxkTGFuZXN8PWIsYz1hLmFsdGVybmF0ZSxudWxsIT09YyYmKGMuY2hpbGRMYW5lc3w9YiksYz1hLGE9YS5yZXR1cm47cmV0dXJuIDM9PT1jLnRhZz9jLnN0YXRlTm9kZTpudWxsfXZhciAkZz0hMTtmdW5jdGlvbiBhaChhKXthLnVwZGF0ZVF1ZXVlPXtiYXNlU3RhdGU6YS5tZW1vaXplZFN0YXRlLGZpcnN0QmFzZVVwZGF0ZTpudWxsLGxhc3RCYXNlVXBkYXRlOm51bGwsc2hhcmVkOntwZW5kaW5nOm51bGwsaW50ZXJsZWF2ZWQ6bnVsbCxsYW5lczowfSxlZmZlY3RzOm51bGx9fQpmdW5jdGlvbiBiaChhLGIpe2E9YS51cGRhdGVRdWV1ZTtiLnVwZGF0ZVF1ZXVlPT09YSYmKGIudXBkYXRlUXVldWU9e2Jhc2VTdGF0ZTphLmJhc2VTdGF0ZSxmaXJzdEJhc2VVcGRhdGU6YS5maXJzdEJhc2VVcGRhdGUsbGFzdEJhc2VVcGRhdGU6YS5sYXN0QmFzZVVwZGF0ZSxzaGFyZWQ6YS5zaGFyZWQsZWZmZWN0czphLmVmZmVjdHN9KX1mdW5jdGlvbiBjaChhLGIpe3JldHVybntldmVudFRpbWU6YSxsYW5lOmIsdGFnOjAscGF5bG9hZDpudWxsLGNhbGxiYWNrOm51bGwsbmV4dDpudWxsfX0KZnVuY3Rpb24gZGgoYSxiLGMpe3ZhciBkPWEudXBkYXRlUXVldWU7aWYobnVsbD09PWQpcmV0dXJuIG51bGw7ZD1kLnNoYXJlZDtpZigwIT09KEsmMikpe3ZhciBlPWQucGVuZGluZztudWxsPT09ZT9iLm5leHQ9YjooYi5uZXh0PWUubmV4dCxlLm5leHQ9Yik7ZC5wZW5kaW5nPWI7cmV0dXJuIFpnKGEsYyl9ZT1kLmludGVybGVhdmVkO251bGw9PT1lPyhiLm5leHQ9YixYZyhkKSk6KGIubmV4dD1lLm5leHQsZS5uZXh0PWIpO2QuaW50ZXJsZWF2ZWQ9YjtyZXR1cm4gWmcoYSxjKX1mdW5jdGlvbiBlaChhLGIsYyl7Yj1iLnVwZGF0ZVF1ZXVlO2lmKG51bGwhPT1iJiYoYj1iLnNoYXJlZCwwIT09KGMmNDE5NDI0MCkpKXt2YXIgZD1iLmxhbmVzO2QmPWEucGVuZGluZ0xhbmVzO2N8PWQ7Yi5sYW5lcz1jO0NjKGEsYyl9fQpmdW5jdGlvbiBmaChhLGIpe3ZhciBjPWEudXBkYXRlUXVldWUsZD1hLmFsdGVybmF0ZTtpZihudWxsIT09ZCYmKGQ9ZC51cGRhdGVRdWV1ZSxjPT09ZCkpe3ZhciBlPW51bGwsZj1udWxsO2M9Yy5maXJzdEJhc2VVcGRhdGU7aWYobnVsbCE9PWMpe2Rve3ZhciBnPXtldmVudFRpbWU6Yy5ldmVudFRpbWUsbGFuZTpjLmxhbmUsdGFnOmMudGFnLHBheWxvYWQ6Yy5wYXlsb2FkLGNhbGxiYWNrOmMuY2FsbGJhY2ssbmV4dDpudWxsfTtudWxsPT09Zj9lPWY9ZzpmPWYubmV4dD1nO2M9Yy5uZXh0fXdoaWxlKG51bGwhPT1jKTtudWxsPT09Zj9lPWY9YjpmPWYubmV4dD1ifWVsc2UgZT1mPWI7Yz17YmFzZVN0YXRlOmQuYmFzZVN0YXRlLGZpcnN0QmFzZVVwZGF0ZTplLGxhc3RCYXNlVXBkYXRlOmYsc2hhcmVkOmQuc2hhcmVkLGVmZmVjdHM6ZC5lZmZlY3RzfTthLnVwZGF0ZVF1ZXVlPWM7cmV0dXJufWE9Yy5sYXN0QmFzZVVwZGF0ZTtudWxsPT09YT9jLmZpcnN0QmFzZVVwZGF0ZT1iOmEubmV4dD0KYjtjLmxhc3RCYXNlVXBkYXRlPWJ9CmZ1bmN0aW9uIGdoKGEsYixjLGQpe3ZhciBlPWEudXBkYXRlUXVldWU7JGc9ITE7dmFyIGY9ZS5maXJzdEJhc2VVcGRhdGUsZz1lLmxhc3RCYXNlVXBkYXRlLGg9ZS5zaGFyZWQucGVuZGluZztpZihudWxsIT09aCl7ZS5zaGFyZWQucGVuZGluZz1udWxsO3ZhciBrPWgsbD1rLm5leHQ7ay5uZXh0PW51bGw7bnVsbD09PWc/Zj1sOmcubmV4dD1sO2c9azt2YXIgbT1hLmFsdGVybmF0ZTtudWxsIT09bSYmKG09bS51cGRhdGVRdWV1ZSxoPW0ubGFzdEJhc2VVcGRhdGUsaCE9PWcmJihudWxsPT09aD9tLmZpcnN0QmFzZVVwZGF0ZT1sOmgubmV4dD1sLG0ubGFzdEJhc2VVcGRhdGU9aykpfWlmKG51bGwhPT1mKXt2YXIgcT1lLmJhc2VTdGF0ZTtnPTA7bT1sPWs9bnVsbDtoPWY7ZG97dmFyIHI9aC5sYW5lLHk9aC5ldmVudFRpbWU7aWYoKGQmcik9PT1yKXtudWxsIT09bSYmKG09bS5uZXh0PXtldmVudFRpbWU6eSxsYW5lOjAsdGFnOmgudGFnLHBheWxvYWQ6aC5wYXlsb2FkLGNhbGxiYWNrOmguY2FsbGJhY2ssCm5leHQ6bnVsbH0pO2E6e3ZhciBuPWEsdD1oO3I9Yjt5PWM7c3dpdGNoKHQudGFnKXtjYXNlIDE6bj10LnBheWxvYWQ7aWYoImZ1bmN0aW9uIj09PXR5cGVvZiBuKXtxPW4uY2FsbCh5LHEscik7YnJlYWsgYX1xPW47YnJlYWsgYTtjYXNlIDM6bi5mbGFncz1uLmZsYWdzJi02NTUzN3wxMjg7Y2FzZSAwOm49dC5wYXlsb2FkO3I9ImZ1bmN0aW9uIj09PXR5cGVvZiBuP24uY2FsbCh5LHEscik6bjtpZihudWxsPT09cnx8dm9pZCAwPT09cilicmVhayBhO3E9QSh7fSxxLHIpO2JyZWFrIGE7Y2FzZSAyOiRnPSEwfX1udWxsIT09aC5jYWxsYmFjayYmMCE9PWgubGFuZSYmKGEuZmxhZ3N8PTY0LHI9ZS5lZmZlY3RzLG51bGw9PT1yP2UuZWZmZWN0cz1baF06ci5wdXNoKGgpKX1lbHNlIHk9e2V2ZW50VGltZTp5LGxhbmU6cix0YWc6aC50YWcscGF5bG9hZDpoLnBheWxvYWQsY2FsbGJhY2s6aC5jYWxsYmFjayxuZXh0Om51bGx9LG51bGw9PT1tPyhsPW09eSxrPXEpOm09bS5uZXh0PXksZ3w9cjsKaD1oLm5leHQ7aWYobnVsbD09PWgpaWYoaD1lLnNoYXJlZC5wZW5kaW5nLG51bGw9PT1oKWJyZWFrO2Vsc2Ugcj1oLGg9ci5uZXh0LHIubmV4dD1udWxsLGUubGFzdEJhc2VVcGRhdGU9cixlLnNoYXJlZC5wZW5kaW5nPW51bGx9d2hpbGUoMSk7bnVsbD09PW0mJihrPXEpO2UuYmFzZVN0YXRlPWs7ZS5maXJzdEJhc2VVcGRhdGU9bDtlLmxhc3RCYXNlVXBkYXRlPW07Yj1lLnNoYXJlZC5pbnRlcmxlYXZlZDtpZihudWxsIT09Yil7ZT1iO2RvIGd8PWUubGFuZSxlPWUubmV4dDt3aGlsZShlIT09Yil9ZWxzZSBudWxsPT09ZiYmKGUuc2hhcmVkLmxhbmVzPTApO2hofD1nO2EubGFuZXM9ZzthLm1lbW9pemVkU3RhdGU9cX19CmZ1bmN0aW9uIGloKGEsYixjKXthPWIuZWZmZWN0cztiLmVmZmVjdHM9bnVsbDtpZihudWxsIT09YSlmb3IoYj0wO2I8YS5sZW5ndGg7YisrKXt2YXIgZD1hW2JdLGU9ZC5jYWxsYmFjaztpZihudWxsIT09ZSl7ZC5jYWxsYmFjaz1udWxsO2Q9YztpZigiZnVuY3Rpb24iIT09dHlwZW9mIGUpdGhyb3cgRXJyb3IocCgxOTEsZSkpO2UuY2FsbChkKX19fXZhciBqaD0obmV3IGFhLkNvbXBvbmVudCkucmVmcztmdW5jdGlvbiBraChhLGIsYyxkKXtiPWEubWVtb2l6ZWRTdGF0ZTtjPWMoZCxiKTtjPW51bGw9PT1jfHx2b2lkIDA9PT1jP2I6QSh7fSxiLGMpO2EubWVtb2l6ZWRTdGF0ZT1jOzA9PT1hLmxhbmVzJiYoYS51cGRhdGVRdWV1ZS5iYXNlU3RhdGU9Yyl9CnZhciBuaD17aXNNb3VudGVkOmZ1bmN0aW9uKGEpe3JldHVybihhPWEuX3JlYWN0SW50ZXJuYWxzKT9WYihhKT09PWE6ITF9LGVucXVldWVTZXRTdGF0ZTpmdW5jdGlvbihhLGIsYyl7YT1hLl9yZWFjdEludGVybmFsczt2YXIgZD1MKCksZT1saChhKSxmPWNoKGQsZSk7Zi5wYXlsb2FkPWI7dm9pZCAwIT09YyYmbnVsbCE9PWMmJihmLmNhbGxiYWNrPWMpO2I9ZGgoYSxmLGUpO251bGwhPT1iJiYobWgoYixhLGUsZCksZWgoYixhLGUpKX0sZW5xdWV1ZVJlcGxhY2VTdGF0ZTpmdW5jdGlvbihhLGIsYyl7YT1hLl9yZWFjdEludGVybmFsczt2YXIgZD1MKCksZT1saChhKSxmPWNoKGQsZSk7Zi50YWc9MTtmLnBheWxvYWQ9Yjt2b2lkIDAhPT1jJiZudWxsIT09YyYmKGYuY2FsbGJhY2s9Yyk7Yj1kaChhLGYsZSk7bnVsbCE9PWImJihtaChiLGEsZSxkKSxlaChiLGEsZSkpfSxlbnF1ZXVlRm9yY2VVcGRhdGU6ZnVuY3Rpb24oYSxiKXthPWEuX3JlYWN0SW50ZXJuYWxzO3ZhciBjPUwoKSxkPQpsaChhKSxlPWNoKGMsZCk7ZS50YWc9Mjt2b2lkIDAhPT1iJiZudWxsIT09YiYmKGUuY2FsbGJhY2s9Yik7Yj1kaChhLGUsZCk7bnVsbCE9PWImJihtaChiLGEsZCxjKSxlaChiLGEsZCkpfX07ZnVuY3Rpb24gb2goYSxiLGMsZCxlLGYsZyl7YT1hLnN0YXRlTm9kZTtyZXR1cm4iZnVuY3Rpb24iPT09dHlwZW9mIGEuc2hvdWxkQ29tcG9uZW50VXBkYXRlP2Euc2hvdWxkQ29tcG9uZW50VXBkYXRlKGQsZixnKTpiLnByb3RvdHlwZSYmYi5wcm90b3R5cGUuaXNQdXJlUmVhY3RDb21wb25lbnQ/IUllKGMsZCl8fCFJZShlLGYpOiEwfQpmdW5jdGlvbiBwaChhLGIsYyl7dmFyIGQ9ITEsZT1WZjt2YXIgZj1iLmNvbnRleHRUeXBlOyJvYmplY3QiPT09dHlwZW9mIGYmJm51bGwhPT1mP2Y9VmcoZik6KGU9WmYoYik/WGY6SC5jdXJyZW50LGQ9Yi5jb250ZXh0VHlwZXMsZj0oZD1udWxsIT09ZCYmdm9pZCAwIT09ZCk/WWYoYSxlKTpWZik7Yj1uZXcgYihjLGYpO2EubWVtb2l6ZWRTdGF0ZT1udWxsIT09Yi5zdGF0ZSYmdm9pZCAwIT09Yi5zdGF0ZT9iLnN0YXRlOm51bGw7Yi51cGRhdGVyPW5oO2Euc3RhdGVOb2RlPWI7Yi5fcmVhY3RJbnRlcm5hbHM9YTtkJiYoYT1hLnN0YXRlTm9kZSxhLl9fcmVhY3RJbnRlcm5hbE1lbW9pemVkVW5tYXNrZWRDaGlsZENvbnRleHQ9ZSxhLl9fcmVhY3RJbnRlcm5hbE1lbW9pemVkTWFza2VkQ2hpbGRDb250ZXh0PWYpO3JldHVybiBifQpmdW5jdGlvbiBxaChhLGIsYyxkKXthPWIuc3RhdGU7ImZ1bmN0aW9uIj09PXR5cGVvZiBiLmNvbXBvbmVudFdpbGxSZWNlaXZlUHJvcHMmJmIuY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyhjLGQpOyJmdW5jdGlvbiI9PT10eXBlb2YgYi5VTlNBRkVfY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyYmYi5VTlNBRkVfY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyhjLGQpO2Iuc3RhdGUhPT1hJiZuaC5lbnF1ZXVlUmVwbGFjZVN0YXRlKGIsYi5zdGF0ZSxudWxsKX0KZnVuY3Rpb24gcmgoYSxiLGMsZCl7dmFyIGU9YS5zdGF0ZU5vZGU7ZS5wcm9wcz1jO2Uuc3RhdGU9YS5tZW1vaXplZFN0YXRlO2UucmVmcz1qaDthaChhKTt2YXIgZj1iLmNvbnRleHRUeXBlOyJvYmplY3QiPT09dHlwZW9mIGYmJm51bGwhPT1mP2UuY29udGV4dD1WZyhmKTooZj1aZihiKT9YZjpILmN1cnJlbnQsZS5jb250ZXh0PVlmKGEsZikpO2Uuc3RhdGU9YS5tZW1vaXplZFN0YXRlO2Y9Yi5nZXREZXJpdmVkU3RhdGVGcm9tUHJvcHM7ImZ1bmN0aW9uIj09PXR5cGVvZiBmJiYoa2goYSxiLGYsYyksZS5zdGF0ZT1hLm1lbW9pemVkU3RhdGUpOyJmdW5jdGlvbiI9PT10eXBlb2YgYi5nZXREZXJpdmVkU3RhdGVGcm9tUHJvcHN8fCJmdW5jdGlvbiI9PT10eXBlb2YgZS5nZXRTbmFwc2hvdEJlZm9yZVVwZGF0ZXx8ImZ1bmN0aW9uIiE9PXR5cGVvZiBlLlVOU0FGRV9jb21wb25lbnRXaWxsTW91bnQmJiJmdW5jdGlvbiIhPT10eXBlb2YgZS5jb21wb25lbnRXaWxsTW91bnR8fChiPWUuc3RhdGUsCiJmdW5jdGlvbiI9PT10eXBlb2YgZS5jb21wb25lbnRXaWxsTW91bnQmJmUuY29tcG9uZW50V2lsbE1vdW50KCksImZ1bmN0aW9uIj09PXR5cGVvZiBlLlVOU0FGRV9jb21wb25lbnRXaWxsTW91bnQmJmUuVU5TQUZFX2NvbXBvbmVudFdpbGxNb3VudCgpLGIhPT1lLnN0YXRlJiZuaC5lbnF1ZXVlUmVwbGFjZVN0YXRlKGUsZS5zdGF0ZSxudWxsKSxnaChhLGMsZSxkKSxlLnN0YXRlPWEubWVtb2l6ZWRTdGF0ZSk7ImZ1bmN0aW9uIj09PXR5cGVvZiBlLmNvbXBvbmVudERpZE1vdW50JiYoYS5mbGFnc3w9NDE5NDMwOCl9CmZ1bmN0aW9uIHNoKGEsYixjKXthPWMucmVmO2lmKG51bGwhPT1hJiYiZnVuY3Rpb24iIT09dHlwZW9mIGEmJiJvYmplY3QiIT09dHlwZW9mIGEpe2lmKGMuX293bmVyKXtjPWMuX293bmVyO2lmKGMpe2lmKDEhPT1jLnRhZyl0aHJvdyBFcnJvcihwKDMwOSkpO3ZhciBkPWMuc3RhdGVOb2RlfWlmKCFkKXRocm93IEVycm9yKHAoMTQ3LGEpKTt2YXIgZT1kLGY9IiIrYTtpZihudWxsIT09YiYmbnVsbCE9PWIucmVmJiYiZnVuY3Rpb24iPT09dHlwZW9mIGIucmVmJiZiLnJlZi5fc3RyaW5nUmVmPT09ZilyZXR1cm4gYi5yZWY7Yj1mdW5jdGlvbihhKXt2YXIgYj1lLnJlZnM7Yj09PWpoJiYoYj1lLnJlZnM9e30pO251bGw9PT1hP2RlbGV0ZSBiW2ZdOmJbZl09YX07Yi5fc3RyaW5nUmVmPWY7cmV0dXJuIGJ9aWYoInN0cmluZyIhPT10eXBlb2YgYSl0aHJvdyBFcnJvcihwKDI4NCkpO2lmKCFjLl9vd25lcil0aHJvdyBFcnJvcihwKDI5MCxhKSk7fXJldHVybiBhfQpmdW5jdGlvbiB0aChhLGIpe2E9T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGIpO3Rocm93IEVycm9yKHAoMzEsIltvYmplY3QgT2JqZWN0XSI9PT1hPyJvYmplY3Qgd2l0aCBrZXlzIHsiK09iamVjdC5rZXlzKGIpLmpvaW4oIiwgIikrIn0iOmEpKTt9ZnVuY3Rpb24gdWgoYSl7dmFyIGI9YS5faW5pdDtyZXR1cm4gYihhLl9wYXlsb2FkKX0KZnVuY3Rpb24gdmgoYSl7ZnVuY3Rpb24gYihiLGMpe2lmKGEpe3ZhciBkPWIuZGVsZXRpb25zO251bGw9PT1kPyhiLmRlbGV0aW9ucz1bY10sYi5mbGFnc3w9MTYpOmQucHVzaChjKX19ZnVuY3Rpb24gYyhjLGQpe2lmKCFhKXJldHVybiBudWxsO2Zvcig7bnVsbCE9PWQ7KWIoYyxkKSxkPWQuc2libGluZztyZXR1cm4gbnVsbH1mdW5jdGlvbiBkKGEsYil7Zm9yKGE9bmV3IE1hcDtudWxsIT09YjspbnVsbCE9PWIua2V5P2Euc2V0KGIua2V5LGIpOmEuc2V0KGIuaW5kZXgsYiksYj1iLnNpYmxpbmc7cmV0dXJuIGF9ZnVuY3Rpb24gZShhLGIpe2E9d2goYSxiKTthLmluZGV4PTA7YS5zaWJsaW5nPW51bGw7cmV0dXJuIGF9ZnVuY3Rpb24gZihiLGMsZCl7Yi5pbmRleD1kO2lmKCFhKXJldHVybiBiLmZsYWdzfD0xMDQ4NTc2LGM7ZD1iLmFsdGVybmF0ZTtpZihudWxsIT09ZClyZXR1cm4gZD1kLmluZGV4LGQ8Yz8oYi5mbGFnc3w9MixjKTpkO2IuZmxhZ3N8PTI7cmV0dXJuIGN9ZnVuY3Rpb24gZyhiKXthJiYKbnVsbD09PWIuYWx0ZXJuYXRlJiYoYi5mbGFnc3w9Mik7cmV0dXJuIGJ9ZnVuY3Rpb24gaChhLGIsYyxkKXtpZihudWxsPT09Ynx8NiE9PWIudGFnKXJldHVybiBiPXhoKGMsYS5tb2RlLGQpLGIucmV0dXJuPWEsYjtiPWUoYixjKTtiLnJldHVybj1hO3JldHVybiBifWZ1bmN0aW9uIGsoYSxiLGMsZCl7dmFyIGY9Yy50eXBlO2lmKGY9PT15YSlyZXR1cm4gbShhLGIsYy5wcm9wcy5jaGlsZHJlbixkLGMua2V5KTtpZihudWxsIT09YiYmKGIuZWxlbWVudFR5cGU9PT1mfHwib2JqZWN0Ij09PXR5cGVvZiBmJiZudWxsIT09ZiYmZi4kJHR5cGVvZj09PUhhJiZ1aChmKT09PWIudHlwZSkpcmV0dXJuIGQ9ZShiLGMucHJvcHMpLGQucmVmPXNoKGEsYixjKSxkLnJldHVybj1hLGQ7ZD15aChjLnR5cGUsYy5rZXksYy5wcm9wcyxudWxsLGEubW9kZSxkKTtkLnJlZj1zaChhLGIsYyk7ZC5yZXR1cm49YTtyZXR1cm4gZH1mdW5jdGlvbiBsKGEsYixjLGQpe2lmKG51bGw9PT1ifHw0IT09Yi50YWd8fApiLnN0YXRlTm9kZS5jb250YWluZXJJbmZvIT09Yy5jb250YWluZXJJbmZvfHxiLnN0YXRlTm9kZS5pbXBsZW1lbnRhdGlvbiE9PWMuaW1wbGVtZW50YXRpb24pcmV0dXJuIGI9emgoYyxhLm1vZGUsZCksYi5yZXR1cm49YSxiO2I9ZShiLGMuY2hpbGRyZW58fFtdKTtiLnJldHVybj1hO3JldHVybiBifWZ1bmN0aW9uIG0oYSxiLGMsZCxmKXtpZihudWxsPT09Ynx8NyE9PWIudGFnKXJldHVybiBiPUFoKGMsYS5tb2RlLGQsZiksYi5yZXR1cm49YSxiO2I9ZShiLGMpO2IucmV0dXJuPWE7cmV0dXJuIGJ9ZnVuY3Rpb24gcShhLGIsYyl7aWYoInN0cmluZyI9PT10eXBlb2YgYiYmIiIhPT1ifHwibnVtYmVyIj09PXR5cGVvZiBiKXJldHVybiBiPXhoKCIiK2IsYS5tb2RlLGMpLGIucmV0dXJuPWEsYjtpZigib2JqZWN0Ij09PXR5cGVvZiBiJiZudWxsIT09Yil7c3dpdGNoKGIuJCR0eXBlb2Ype2Nhc2UgdmE6cmV0dXJuIGM9eWgoYi50eXBlLGIua2V5LGIucHJvcHMsbnVsbCxhLm1vZGUsYyksCmMucmVmPXNoKGEsbnVsbCxiKSxjLnJldHVybj1hLGM7Y2FzZSB3YTpyZXR1cm4gYj16aChiLGEubW9kZSxjKSxiLnJldHVybj1hLGI7Y2FzZSBIYTp2YXIgZD1iLl9pbml0O3JldHVybiBxKGEsZChiLl9wYXlsb2FkKSxjKX1pZihlYihiKXx8S2EoYikpcmV0dXJuIGI9QWgoYixhLm1vZGUsYyxudWxsKSxiLnJldHVybj1hLGI7dGgoYSxiKX1yZXR1cm4gbnVsbH1mdW5jdGlvbiByKGEsYixjLGQpe3ZhciBlPW51bGwhPT1iP2Iua2V5Om51bGw7aWYoInN0cmluZyI9PT10eXBlb2YgYyYmIiIhPT1jfHwibnVtYmVyIj09PXR5cGVvZiBjKXJldHVybiBudWxsIT09ZT9udWxsOmgoYSxiLCIiK2MsZCk7aWYoIm9iamVjdCI9PT10eXBlb2YgYyYmbnVsbCE9PWMpe3N3aXRjaChjLiQkdHlwZW9mKXtjYXNlIHZhOnJldHVybiBjLmtleT09PWU/ayhhLGIsYyxkKTpudWxsO2Nhc2Ugd2E6cmV0dXJuIGMua2V5PT09ZT9sKGEsYixjLGQpOm51bGw7Y2FzZSBIYTpyZXR1cm4gZT1jLl9pbml0LHIoYSwKYixlKGMuX3BheWxvYWQpLGQpfWlmKGViKGMpfHxLYShjKSlyZXR1cm4gbnVsbCE9PWU/bnVsbDptKGEsYixjLGQsbnVsbCk7dGgoYSxjKX1yZXR1cm4gbnVsbH1mdW5jdGlvbiB5KGEsYixjLGQsZSl7aWYoInN0cmluZyI9PT10eXBlb2YgZCYmIiIhPT1kfHwibnVtYmVyIj09PXR5cGVvZiBkKXJldHVybiBhPWEuZ2V0KGMpfHxudWxsLGgoYixhLCIiK2QsZSk7aWYoIm9iamVjdCI9PT10eXBlb2YgZCYmbnVsbCE9PWQpe3N3aXRjaChkLiQkdHlwZW9mKXtjYXNlIHZhOnJldHVybiBhPWEuZ2V0KG51bGw9PT1kLmtleT9jOmQua2V5KXx8bnVsbCxrKGIsYSxkLGUpO2Nhc2Ugd2E6cmV0dXJuIGE9YS5nZXQobnVsbD09PWQua2V5P2M6ZC5rZXkpfHxudWxsLGwoYixhLGQsZSk7Y2FzZSBIYTp2YXIgZj1kLl9pbml0O3JldHVybiB5KGEsYixjLGYoZC5fcGF5bG9hZCksZSl9aWYoZWIoZCl8fEthKGQpKXJldHVybiBhPWEuZ2V0KGMpfHxudWxsLG0oYixhLGQsZSxudWxsKTt0aChiLGQpfXJldHVybiBudWxsfQpmdW5jdGlvbiBuKGUsZyxoLGspe2Zvcih2YXIgbD1udWxsLG09bnVsbCx1PWcsdz1nPTAseD1udWxsO251bGwhPT11JiZ3PGgubGVuZ3RoO3crKyl7dS5pbmRleD53Pyh4PXUsdT1udWxsKTp4PXUuc2libGluZzt2YXIgbj1yKGUsdSxoW3ddLGspO2lmKG51bGw9PT1uKXtudWxsPT09dSYmKHU9eCk7YnJlYWt9YSYmdSYmbnVsbD09PW4uYWx0ZXJuYXRlJiZiKGUsdSk7Zz1mKG4sZyx3KTtudWxsPT09bT9sPW46bS5zaWJsaW5nPW47bT1uO3U9eH1pZih3PT09aC5sZW5ndGgpcmV0dXJuIGMoZSx1KSxJJiZ0ZyhlLHcpLGw7aWYobnVsbD09PXUpe2Zvcig7dzxoLmxlbmd0aDt3KyspdT1xKGUsaFt3XSxrKSxudWxsIT09dSYmKGc9Zih1LGcsdyksbnVsbD09PW0/bD11Om0uc2libGluZz11LG09dSk7SSYmdGcoZSx3KTtyZXR1cm4gbH1mb3IodT1kKGUsdSk7dzxoLmxlbmd0aDt3KyspeD15KHUsZSx3LGhbd10sayksbnVsbCE9PXgmJihhJiZudWxsIT09eC5hbHRlcm5hdGUmJnUuZGVsZXRlKG51bGw9PT0KeC5rZXk/dzp4LmtleSksZz1mKHgsZyx3KSxudWxsPT09bT9sPXg6bS5zaWJsaW5nPXgsbT14KTthJiZ1LmZvckVhY2goZnVuY3Rpb24oYSl7cmV0dXJuIGIoZSxhKX0pO0kmJnRnKGUsdyk7cmV0dXJuIGx9ZnVuY3Rpb24gdChlLGcsaCxrKXt2YXIgbD1LYShoKTtpZigiZnVuY3Rpb24iIT09dHlwZW9mIGwpdGhyb3cgRXJyb3IocCgxNTApKTtoPWwuY2FsbChoKTtpZihudWxsPT1oKXRocm93IEVycm9yKHAoMTUxKSk7Zm9yKHZhciB1PWw9bnVsbCxtPWcsdz1nPTAseD1udWxsLG49aC5uZXh0KCk7bnVsbCE9PW0mJiFuLmRvbmU7dysrLG49aC5uZXh0KCkpe20uaW5kZXg+dz8oeD1tLG09bnVsbCk6eD1tLnNpYmxpbmc7dmFyIHQ9cihlLG0sbi52YWx1ZSxrKTtpZihudWxsPT09dCl7bnVsbD09PW0mJihtPXgpO2JyZWFrfWEmJm0mJm51bGw9PT10LmFsdGVybmF0ZSYmYihlLG0pO2c9Zih0LGcsdyk7bnVsbD09PXU/bD10OnUuc2libGluZz10O3U9dDttPXh9aWYobi5kb25lKXJldHVybiBjKGUsCm0pLEkmJnRnKGUsdyksbDtpZihudWxsPT09bSl7Zm9yKDshbi5kb25lO3crKyxuPWgubmV4dCgpKW49cShlLG4udmFsdWUsayksbnVsbCE9PW4mJihnPWYobixnLHcpLG51bGw9PT11P2w9bjp1LnNpYmxpbmc9bix1PW4pO0kmJnRnKGUsdyk7cmV0dXJuIGx9Zm9yKG09ZChlLG0pOyFuLmRvbmU7dysrLG49aC5uZXh0KCkpbj15KG0sZSx3LG4udmFsdWUsayksbnVsbCE9PW4mJihhJiZudWxsIT09bi5hbHRlcm5hdGUmJm0uZGVsZXRlKG51bGw9PT1uLmtleT93Om4ua2V5KSxnPWYobixnLHcpLG51bGw9PT11P2w9bjp1LnNpYmxpbmc9bix1PW4pO2EmJm0uZm9yRWFjaChmdW5jdGlvbihhKXtyZXR1cm4gYihlLGEpfSk7SSYmdGcoZSx3KTtyZXR1cm4gbH1mdW5jdGlvbiBKKGEsZCxmLGgpeyJvYmplY3QiPT09dHlwZW9mIGYmJm51bGwhPT1mJiZmLnR5cGU9PT15YSYmbnVsbD09PWYua2V5JiYoZj1mLnByb3BzLmNoaWxkcmVuKTtpZigib2JqZWN0Ij09PXR5cGVvZiBmJiZudWxsIT09Zil7c3dpdGNoKGYuJCR0eXBlb2Ype2Nhc2UgdmE6YTp7Zm9yKHZhciBrPQpmLmtleSxsPWQ7bnVsbCE9PWw7KXtpZihsLmtleT09PWspe2s9Zi50eXBlO2lmKGs9PT15YSl7aWYoNz09PWwudGFnKXtjKGEsbC5zaWJsaW5nKTtkPWUobCxmLnByb3BzLmNoaWxkcmVuKTtkLnJldHVybj1hO2E9ZDticmVhayBhfX1lbHNlIGlmKGwuZWxlbWVudFR5cGU9PT1rfHwib2JqZWN0Ij09PXR5cGVvZiBrJiZudWxsIT09ayYmay4kJHR5cGVvZj09PUhhJiZ1aChrKT09PWwudHlwZSl7YyhhLGwuc2libGluZyk7ZD1lKGwsZi5wcm9wcyk7ZC5yZWY9c2goYSxsLGYpO2QucmV0dXJuPWE7YT1kO2JyZWFrIGF9YyhhLGwpO2JyZWFrfWVsc2UgYihhLGwpO2w9bC5zaWJsaW5nfWYudHlwZT09PXlhPyhkPUFoKGYucHJvcHMuY2hpbGRyZW4sYS5tb2RlLGgsZi5rZXkpLGQucmV0dXJuPWEsYT1kKTooaD15aChmLnR5cGUsZi5rZXksZi5wcm9wcyxudWxsLGEubW9kZSxoKSxoLnJlZj1zaChhLGQsZiksaC5yZXR1cm49YSxhPWgpfXJldHVybiBnKGEpO2Nhc2Ugd2E6YTp7Zm9yKGw9Zi5rZXk7bnVsbCE9PQpkOyl7aWYoZC5rZXk9PT1sKWlmKDQ9PT1kLnRhZyYmZC5zdGF0ZU5vZGUuY29udGFpbmVySW5mbz09PWYuY29udGFpbmVySW5mbyYmZC5zdGF0ZU5vZGUuaW1wbGVtZW50YXRpb249PT1mLmltcGxlbWVudGF0aW9uKXtjKGEsZC5zaWJsaW5nKTtkPWUoZCxmLmNoaWxkcmVufHxbXSk7ZC5yZXR1cm49YTthPWQ7YnJlYWsgYX1lbHNle2MoYSxkKTticmVha31lbHNlIGIoYSxkKTtkPWQuc2libGluZ31kPXpoKGYsYS5tb2RlLGgpO2QucmV0dXJuPWE7YT1kfXJldHVybiBnKGEpO2Nhc2UgSGE6cmV0dXJuIGw9Zi5faW5pdCxKKGEsZCxsKGYuX3BheWxvYWQpLGgpfWlmKGViKGYpKXJldHVybiBuKGEsZCxmLGgpO2lmKEthKGYpKXJldHVybiB0KGEsZCxmLGgpO3RoKGEsZil9cmV0dXJuInN0cmluZyI9PT10eXBlb2YgZiYmIiIhPT1mfHwibnVtYmVyIj09PXR5cGVvZiBmPyhmPSIiK2YsbnVsbCE9PWQmJjY9PT1kLnRhZz8oYyhhLGQuc2libGluZyksZD1lKGQsZiksZC5yZXR1cm49YSxhPWQpOgooYyhhLGQpLGQ9eGgoZixhLm1vZGUsaCksZC5yZXR1cm49YSxhPWQpLGcoYSkpOmMoYSxkKX1yZXR1cm4gSn12YXIgQmg9dmgoITApLENoPXZoKCExKSxEaD17fSxFaD1VZihEaCksRmg9VWYoRGgpLEdoPVVmKERoKTtmdW5jdGlvbiBIaChhKXtpZihhPT09RGgpdGhyb3cgRXJyb3IocCgxNzQpKTtyZXR1cm4gYX1mdW5jdGlvbiBJaChhLGIpe0coR2gsYik7RyhGaCxhKTtHKEVoLERoKTthPWIubm9kZVR5cGU7c3dpdGNoKGEpe2Nhc2UgOTpjYXNlIDExOmI9KGI9Yi5kb2N1bWVudEVsZW1lbnQpP2IubmFtZXNwYWNlVVJJOmxiKG51bGwsIiIpO2JyZWFrO2RlZmF1bHQ6YT04PT09YT9iLnBhcmVudE5vZGU6YixiPWEubmFtZXNwYWNlVVJJfHxudWxsLGE9YS50YWdOYW1lLGI9bGIoYixhKX1FKEVoKTtHKEVoLGIpfWZ1bmN0aW9uIEpoKCl7RShFaCk7RShGaCk7RShHaCl9CmZ1bmN0aW9uIEtoKGEpe0hoKEdoLmN1cnJlbnQpO3ZhciBiPUhoKEVoLmN1cnJlbnQpO3ZhciBjPWxiKGIsYS50eXBlKTtiIT09YyYmKEcoRmgsYSksRyhFaCxjKSl9ZnVuY3Rpb24gTGgoYSl7RmguY3VycmVudD09PWEmJihFKEVoKSxFKEZoKSl9dmFyIE09VWYoMCk7CmZ1bmN0aW9uIE1oKGEpe2Zvcih2YXIgYj1hO251bGwhPT1iOyl7aWYoMTM9PT1iLnRhZyl7dmFyIGM9Yi5tZW1vaXplZFN0YXRlO2lmKG51bGwhPT1jJiYoYz1jLmRlaHlkcmF0ZWQsbnVsbD09PWN8fCIkPyI9PT1jLmRhdGF8fCIkISI9PT1jLmRhdGEpKXJldHVybiBifWVsc2UgaWYoMTk9PT1iLnRhZyYmdm9pZCAwIT09Yi5tZW1vaXplZFByb3BzLnJldmVhbE9yZGVyKXtpZigwIT09KGIuZmxhZ3MmMTI4KSlyZXR1cm4gYn1lbHNlIGlmKG51bGwhPT1iLmNoaWxkKXtiLmNoaWxkLnJldHVybj1iO2I9Yi5jaGlsZDtjb250aW51ZX1pZihiPT09YSlicmVhaztmb3IoO251bGw9PT1iLnNpYmxpbmc7KXtpZihudWxsPT09Yi5yZXR1cm58fGIucmV0dXJuPT09YSlyZXR1cm4gbnVsbDtiPWIucmV0dXJufWIuc2libGluZy5yZXR1cm49Yi5yZXR1cm47Yj1iLnNpYmxpbmd9cmV0dXJuIG51bGx9dmFyIE5oPVtdOwpmdW5jdGlvbiBPaCgpe2Zvcih2YXIgYT0wO2E8TmgubGVuZ3RoO2ErKylOaFthXS5fd29ya0luUHJvZ3Jlc3NWZXJzaW9uUHJpbWFyeT1udWxsO05oLmxlbmd0aD0wfXZhciBQaD11YS5SZWFjdEN1cnJlbnREaXNwYXRjaGVyLFFoPXVhLlJlYWN0Q3VycmVudEJhdGNoQ29uZmlnLFJoPTAsTj1udWxsLE89bnVsbCxQPW51bGwsU2g9ITEsVGg9ITEsVWg9MCxWaD0wO2Z1bmN0aW9uIFEoKXt0aHJvdyBFcnJvcihwKDMyMSkpO31mdW5jdGlvbiBXaChhLGIpe2lmKG51bGw9PT1iKXJldHVybiExO2Zvcih2YXIgYz0wO2M8Yi5sZW5ndGgmJmM8YS5sZW5ndGg7YysrKWlmKCFIZShhW2NdLGJbY10pKXJldHVybiExO3JldHVybiEwfQpmdW5jdGlvbiBYaChhLGIsYyxkLGUsZil7Umg9ZjtOPWI7Yi5tZW1vaXplZFN0YXRlPW51bGw7Yi51cGRhdGVRdWV1ZT1udWxsO2IubGFuZXM9MDtQaC5jdXJyZW50PW51bGw9PT1hfHxudWxsPT09YS5tZW1vaXplZFN0YXRlP1loOlpoO2E9YyhkLGUpO2lmKFRoKXtmPTA7ZG97VGg9ITE7VWg9MDtpZigyNTw9Zil0aHJvdyBFcnJvcihwKDMwMSkpO2YrPTE7UD1PPW51bGw7Yi51cGRhdGVRdWV1ZT1udWxsO1BoLmN1cnJlbnQ9JGg7YT1jKGQsZSl9d2hpbGUoVGgpfVBoLmN1cnJlbnQ9YWk7Yj1udWxsIT09TyYmbnVsbCE9PU8ubmV4dDtSaD0wO1A9Tz1OPW51bGw7U2g9ITE7aWYoYil0aHJvdyBFcnJvcihwKDMwMCkpO3JldHVybiBhfWZ1bmN0aW9uIGJpKCl7dmFyIGE9MCE9PVVoO1VoPTA7cmV0dXJuIGF9CmZ1bmN0aW9uIGNpKCl7dmFyIGE9e21lbW9pemVkU3RhdGU6bnVsbCxiYXNlU3RhdGU6bnVsbCxiYXNlUXVldWU6bnVsbCxxdWV1ZTpudWxsLG5leHQ6bnVsbH07bnVsbD09PVA/Ti5tZW1vaXplZFN0YXRlPVA9YTpQPVAubmV4dD1hO3JldHVybiBQfWZ1bmN0aW9uIGRpKCl7aWYobnVsbD09PU8pe3ZhciBhPU4uYWx0ZXJuYXRlO2E9bnVsbCE9PWE/YS5tZW1vaXplZFN0YXRlOm51bGx9ZWxzZSBhPU8ubmV4dDt2YXIgYj1udWxsPT09UD9OLm1lbW9pemVkU3RhdGU6UC5uZXh0O2lmKG51bGwhPT1iKVA9YixPPWE7ZWxzZXtpZihudWxsPT09YSl0aHJvdyBFcnJvcihwKDMxMCkpO089YTthPXttZW1vaXplZFN0YXRlOk8ubWVtb2l6ZWRTdGF0ZSxiYXNlU3RhdGU6Ty5iYXNlU3RhdGUsYmFzZVF1ZXVlOk8uYmFzZVF1ZXVlLHF1ZXVlOk8ucXVldWUsbmV4dDpudWxsfTtudWxsPT09UD9OLm1lbW9pemVkU3RhdGU9UD1hOlA9UC5uZXh0PWF9cmV0dXJuIFB9CmZ1bmN0aW9uIGVpKGEsYil7cmV0dXJuImZ1bmN0aW9uIj09PXR5cGVvZiBiP2IoYSk6Yn0KZnVuY3Rpb24gZmkoYSl7dmFyIGI9ZGkoKSxjPWIucXVldWU7aWYobnVsbD09PWMpdGhyb3cgRXJyb3IocCgzMTEpKTtjLmxhc3RSZW5kZXJlZFJlZHVjZXI9YTt2YXIgZD1PLGU9ZC5iYXNlUXVldWUsZj1jLnBlbmRpbmc7aWYobnVsbCE9PWYpe2lmKG51bGwhPT1lKXt2YXIgZz1lLm5leHQ7ZS5uZXh0PWYubmV4dDtmLm5leHQ9Z31kLmJhc2VRdWV1ZT1lPWY7Yy5wZW5kaW5nPW51bGx9aWYobnVsbCE9PWUpe2Y9ZS5uZXh0O2Q9ZC5iYXNlU3RhdGU7dmFyIGg9Zz1udWxsLGs9bnVsbCxsPWY7ZG97dmFyIG09bC5sYW5lO2lmKChSaCZtKT09PW0pbnVsbCE9PWsmJihrPWsubmV4dD17bGFuZTowLGFjdGlvbjpsLmFjdGlvbixoYXNFYWdlclN0YXRlOmwuaGFzRWFnZXJTdGF0ZSxlYWdlclN0YXRlOmwuZWFnZXJTdGF0ZSxuZXh0Om51bGx9KSxkPWwuaGFzRWFnZXJTdGF0ZT9sLmVhZ2VyU3RhdGU6YShkLGwuYWN0aW9uKTtlbHNle3ZhciBxPXtsYW5lOm0sYWN0aW9uOmwuYWN0aW9uLGhhc0VhZ2VyU3RhdGU6bC5oYXNFYWdlclN0YXRlLAplYWdlclN0YXRlOmwuZWFnZXJTdGF0ZSxuZXh0Om51bGx9O251bGw9PT1rPyhoPWs9cSxnPWQpOms9ay5uZXh0PXE7Ti5sYW5lc3w9bTtoaHw9bX1sPWwubmV4dH13aGlsZShudWxsIT09bCYmbCE9PWYpO251bGw9PT1rP2c9ZDprLm5leHQ9aDtIZShkLGIubWVtb2l6ZWRTdGF0ZSl8fChVZz0hMCk7Yi5tZW1vaXplZFN0YXRlPWQ7Yi5iYXNlU3RhdGU9ZztiLmJhc2VRdWV1ZT1rO2MubGFzdFJlbmRlcmVkU3RhdGU9ZH1hPWMuaW50ZXJsZWF2ZWQ7aWYobnVsbCE9PWEpe2U9YTtkbyBmPWUubGFuZSxOLmxhbmVzfD1mLGhofD1mLGU9ZS5uZXh0O3doaWxlKGUhPT1hKX1lbHNlIG51bGw9PT1lJiYoYy5sYW5lcz0wKTtyZXR1cm5bYi5tZW1vaXplZFN0YXRlLGMuZGlzcGF0Y2hdfQpmdW5jdGlvbiBnaShhKXt2YXIgYj1kaSgpLGM9Yi5xdWV1ZTtpZihudWxsPT09Yyl0aHJvdyBFcnJvcihwKDMxMSkpO2MubGFzdFJlbmRlcmVkUmVkdWNlcj1hO3ZhciBkPWMuZGlzcGF0Y2gsZT1jLnBlbmRpbmcsZj1iLm1lbW9pemVkU3RhdGU7aWYobnVsbCE9PWUpe2MucGVuZGluZz1udWxsO3ZhciBnPWU9ZS5uZXh0O2RvIGY9YShmLGcuYWN0aW9uKSxnPWcubmV4dDt3aGlsZShnIT09ZSk7SGUoZixiLm1lbW9pemVkU3RhdGUpfHwoVWc9ITApO2IubWVtb2l6ZWRTdGF0ZT1mO251bGw9PT1iLmJhc2VRdWV1ZSYmKGIuYmFzZVN0YXRlPWYpO2MubGFzdFJlbmRlcmVkU3RhdGU9Zn1yZXR1cm5bZixkXX1mdW5jdGlvbiBoaSgpe30KZnVuY3Rpb24gaWkoYSxiKXt2YXIgYz1OLGQ9ZGkoKSxlPWIoKSxmPSFIZShkLm1lbW9pemVkU3RhdGUsZSk7ZiYmKGQubWVtb2l6ZWRTdGF0ZT1lLFVnPSEwKTtkPWQucXVldWU7amkoa2kuYmluZChudWxsLGMsZCxhKSxbYV0pO2lmKGQuZ2V0U25hcHNob3QhPT1ifHxmfHxudWxsIT09UCYmUC5tZW1vaXplZFN0YXRlLnRhZyYxKXtjLmZsYWdzfD0yMDQ4O2xpKDksbWkuYmluZChudWxsLGMsZCxlLGIpLHZvaWQgMCxudWxsKTtpZihudWxsPT09Uil0aHJvdyBFcnJvcihwKDM0OSkpOzAhPT0oUmgmMzApfHxuaShjLGIsZSl9cmV0dXJuIGV9ZnVuY3Rpb24gbmkoYSxiLGMpe2EuZmxhZ3N8PTE2Mzg0O2E9e2dldFNuYXBzaG90OmIsdmFsdWU6Y307Yj1OLnVwZGF0ZVF1ZXVlO251bGw9PT1iPyhiPXtsYXN0RWZmZWN0Om51bGwsc3RvcmVzOm51bGx9LE4udXBkYXRlUXVldWU9YixiLnN0b3Jlcz1bYV0pOihjPWIuc3RvcmVzLG51bGw9PT1jP2Iuc3RvcmVzPVthXTpjLnB1c2goYSkpfQpmdW5jdGlvbiBtaShhLGIsYyxkKXtiLnZhbHVlPWM7Yi5nZXRTbmFwc2hvdD1kO29pKGIpJiZwaShhKX1mdW5jdGlvbiBraShhLGIsYyl7cmV0dXJuIGMoZnVuY3Rpb24oKXtvaShiKSYmcGkoYSl9KX1mdW5jdGlvbiBvaShhKXt2YXIgYj1hLmdldFNuYXBzaG90O2E9YS52YWx1ZTt0cnl7dmFyIGM9YigpO3JldHVybiFIZShhLGMpfWNhdGNoKGQpe3JldHVybiEwfX1mdW5jdGlvbiBwaShhKXt2YXIgYj1aZyhhLDEpO251bGwhPT1iJiZtaChiLGEsMSwtMSl9CmZ1bmN0aW9uIHFpKGEpe3ZhciBiPWNpKCk7ImZ1bmN0aW9uIj09PXR5cGVvZiBhJiYoYT1hKCkpO2IubWVtb2l6ZWRTdGF0ZT1iLmJhc2VTdGF0ZT1hO2E9e3BlbmRpbmc6bnVsbCxpbnRlcmxlYXZlZDpudWxsLGxhbmVzOjAsZGlzcGF0Y2g6bnVsbCxsYXN0UmVuZGVyZWRSZWR1Y2VyOmVpLGxhc3RSZW5kZXJlZFN0YXRlOmF9O2IucXVldWU9YTthPWEuZGlzcGF0Y2g9cmkuYmluZChudWxsLE4sYSk7cmV0dXJuW2IubWVtb2l6ZWRTdGF0ZSxhXX0KZnVuY3Rpb24gbGkoYSxiLGMsZCl7YT17dGFnOmEsY3JlYXRlOmIsZGVzdHJveTpjLGRlcHM6ZCxuZXh0Om51bGx9O2I9Ti51cGRhdGVRdWV1ZTtudWxsPT09Yj8oYj17bGFzdEVmZmVjdDpudWxsLHN0b3JlczpudWxsfSxOLnVwZGF0ZVF1ZXVlPWIsYi5sYXN0RWZmZWN0PWEubmV4dD1hKTooYz1iLmxhc3RFZmZlY3QsbnVsbD09PWM/Yi5sYXN0RWZmZWN0PWEubmV4dD1hOihkPWMubmV4dCxjLm5leHQ9YSxhLm5leHQ9ZCxiLmxhc3RFZmZlY3Q9YSkpO3JldHVybiBhfWZ1bmN0aW9uIHNpKCl7cmV0dXJuIGRpKCkubWVtb2l6ZWRTdGF0ZX1mdW5jdGlvbiB0aShhLGIsYyxkKXt2YXIgZT1jaSgpO04uZmxhZ3N8PWE7ZS5tZW1vaXplZFN0YXRlPWxpKDF8YixjLHZvaWQgMCx2b2lkIDA9PT1kP251bGw6ZCl9CmZ1bmN0aW9uIHVpKGEsYixjLGQpe3ZhciBlPWRpKCk7ZD12b2lkIDA9PT1kP251bGw6ZDt2YXIgZj12b2lkIDA7aWYobnVsbCE9PU8pe3ZhciBnPU8ubWVtb2l6ZWRTdGF0ZTtmPWcuZGVzdHJveTtpZihudWxsIT09ZCYmV2goZCxnLmRlcHMpKXtlLm1lbW9pemVkU3RhdGU9bGkoYixjLGYsZCk7cmV0dXJufX1OLmZsYWdzfD1hO2UubWVtb2l6ZWRTdGF0ZT1saSgxfGIsYyxmLGQpfWZ1bmN0aW9uIHZpKGEsYil7cmV0dXJuIHRpKDgzOTA2NTYsOCxhLGIpfWZ1bmN0aW9uIGppKGEsYil7cmV0dXJuIHVpKDIwNDgsOCxhLGIpfWZ1bmN0aW9uIHdpKGEsYil7cmV0dXJuIHVpKDQsMixhLGIpfWZ1bmN0aW9uIHhpKGEsYil7cmV0dXJuIHVpKDQsNCxhLGIpfQpmdW5jdGlvbiB5aShhLGIpe2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgYilyZXR1cm4gYT1hKCksYihhKSxmdW5jdGlvbigpe2IobnVsbCl9O2lmKG51bGwhPT1iJiZ2b2lkIDAhPT1iKXJldHVybiBhPWEoKSxiLmN1cnJlbnQ9YSxmdW5jdGlvbigpe2IuY3VycmVudD1udWxsfX1mdW5jdGlvbiB6aShhLGIsYyl7Yz1udWxsIT09YyYmdm9pZCAwIT09Yz9jLmNvbmNhdChbYV0pOm51bGw7cmV0dXJuIHVpKDQsNCx5aS5iaW5kKG51bGwsYixhKSxjKX1mdW5jdGlvbiBBaSgpe31mdW5jdGlvbiBCaShhLGIpe3ZhciBjPWRpKCk7Yj12b2lkIDA9PT1iP251bGw6Yjt2YXIgZD1jLm1lbW9pemVkU3RhdGU7aWYobnVsbCE9PWQmJm51bGwhPT1iJiZXaChiLGRbMV0pKXJldHVybiBkWzBdO2MubWVtb2l6ZWRTdGF0ZT1bYSxiXTtyZXR1cm4gYX0KZnVuY3Rpb24gQ2koYSxiKXt2YXIgYz1kaSgpO2I9dm9pZCAwPT09Yj9udWxsOmI7dmFyIGQ9Yy5tZW1vaXplZFN0YXRlO2lmKG51bGwhPT1kJiZudWxsIT09YiYmV2goYixkWzFdKSlyZXR1cm4gZFswXTthPWEoKTtjLm1lbW9pemVkU3RhdGU9W2EsYl07cmV0dXJuIGF9ZnVuY3Rpb24gRGkoYSxiLGMpe2lmKDA9PT0oUmgmMjEpKXJldHVybiBhLmJhc2VTdGF0ZSYmKGEuYmFzZVN0YXRlPSExLFVnPSEwKSxhLm1lbW9pemVkU3RhdGU9YztIZShjLGIpfHwoYz15YygpLE4ubGFuZXN8PWMsaGh8PWMsYS5iYXNlU3RhdGU9ITApO3JldHVybiBifWZ1bmN0aW9uIEVpKGEsYil7dmFyIGM9QztDPTAhPT1jJiY0PmM/Yzo0O2EoITApO3ZhciBkPVFoLnRyYW5zaXRpb247UWgudHJhbnNpdGlvbj17fTt0cnl7YSghMSksYigpfWZpbmFsbHl7Qz1jLFFoLnRyYW5zaXRpb249ZH19ZnVuY3Rpb24gRmkoKXtyZXR1cm4gZGkoKS5tZW1vaXplZFN0YXRlfQpmdW5jdGlvbiBHaShhLGIsYyl7dmFyIGQ9bGgoYSk7Yz17bGFuZTpkLGFjdGlvbjpjLGhhc0VhZ2VyU3RhdGU6ITEsZWFnZXJTdGF0ZTpudWxsLG5leHQ6bnVsbH07aWYoSGkoYSkpSWkoYixjKTtlbHNlIGlmKGM9WWcoYSxiLGMsZCksbnVsbCE9PWMpe3ZhciBlPUwoKTttaChjLGEsZCxlKTtKaShjLGIsZCl9fQpmdW5jdGlvbiByaShhLGIsYyl7dmFyIGQ9bGgoYSksZT17bGFuZTpkLGFjdGlvbjpjLGhhc0VhZ2VyU3RhdGU6ITEsZWFnZXJTdGF0ZTpudWxsLG5leHQ6bnVsbH07aWYoSGkoYSkpSWkoYixlKTtlbHNle3ZhciBmPWEuYWx0ZXJuYXRlO2lmKDA9PT1hLmxhbmVzJiYobnVsbD09PWZ8fDA9PT1mLmxhbmVzKSYmKGY9Yi5sYXN0UmVuZGVyZWRSZWR1Y2VyLG51bGwhPT1mKSl0cnl7dmFyIGc9Yi5sYXN0UmVuZGVyZWRTdGF0ZSxoPWYoZyxjKTtlLmhhc0VhZ2VyU3RhdGU9ITA7ZS5lYWdlclN0YXRlPWg7aWYoSGUoaCxnKSl7dmFyIGs9Yi5pbnRlcmxlYXZlZDtudWxsPT09az8oZS5uZXh0PWUsWGcoYikpOihlLm5leHQ9ay5uZXh0LGsubmV4dD1lKTtiLmludGVybGVhdmVkPWU7cmV0dXJufX1jYXRjaChsKXt9ZmluYWxseXt9Yz1ZZyhhLGIsZSxkKTtudWxsIT09YyYmKGU9TCgpLG1oKGMsYSxkLGUpLEppKGMsYixkKSl9fQpmdW5jdGlvbiBIaShhKXt2YXIgYj1hLmFsdGVybmF0ZTtyZXR1cm4gYT09PU58fG51bGwhPT1iJiZiPT09Tn1mdW5jdGlvbiBJaShhLGIpe1RoPVNoPSEwO3ZhciBjPWEucGVuZGluZztudWxsPT09Yz9iLm5leHQ9YjooYi5uZXh0PWMubmV4dCxjLm5leHQ9Yik7YS5wZW5kaW5nPWJ9ZnVuY3Rpb24gSmkoYSxiLGMpe2lmKDAhPT0oYyY0MTk0MjQwKSl7dmFyIGQ9Yi5sYW5lcztkJj1hLnBlbmRpbmdMYW5lcztjfD1kO2IubGFuZXM9YztDYyhhLGMpfX0KdmFyIGFpPXtyZWFkQ29udGV4dDpWZyx1c2VDYWxsYmFjazpRLHVzZUNvbnRleHQ6USx1c2VFZmZlY3Q6USx1c2VJbXBlcmF0aXZlSGFuZGxlOlEsdXNlSW5zZXJ0aW9uRWZmZWN0OlEsdXNlTGF5b3V0RWZmZWN0OlEsdXNlTWVtbzpRLHVzZVJlZHVjZXI6USx1c2VSZWY6USx1c2VTdGF0ZTpRLHVzZURlYnVnVmFsdWU6USx1c2VEZWZlcnJlZFZhbHVlOlEsdXNlVHJhbnNpdGlvbjpRLHVzZU11dGFibGVTb3VyY2U6USx1c2VTeW5jRXh0ZXJuYWxTdG9yZTpRLHVzZUlkOlEsdW5zdGFibGVfaXNOZXdSZWNvbmNpbGVyOiExfSxZaD17cmVhZENvbnRleHQ6VmcsdXNlQ2FsbGJhY2s6ZnVuY3Rpb24oYSxiKXtjaSgpLm1lbW9pemVkU3RhdGU9W2Esdm9pZCAwPT09Yj9udWxsOmJdO3JldHVybiBhfSx1c2VDb250ZXh0OlZnLHVzZUVmZmVjdDp2aSx1c2VJbXBlcmF0aXZlSGFuZGxlOmZ1bmN0aW9uKGEsYixjKXtjPW51bGwhPT1jJiZ2b2lkIDAhPT1jP2MuY29uY2F0KFthXSk6bnVsbDtyZXR1cm4gdGkoNDE5NDMwOCwKNCx5aS5iaW5kKG51bGwsYixhKSxjKX0sdXNlTGF5b3V0RWZmZWN0OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRpKDQxOTQzMDgsNCxhLGIpfSx1c2VJbnNlcnRpb25FZmZlY3Q6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGkoNCwyLGEsYil9LHVzZU1lbW86ZnVuY3Rpb24oYSxiKXt2YXIgYz1jaSgpO2I9dm9pZCAwPT09Yj9udWxsOmI7YT1hKCk7Yy5tZW1vaXplZFN0YXRlPVthLGJdO3JldHVybiBhfSx1c2VSZWR1Y2VyOmZ1bmN0aW9uKGEsYixjKXt2YXIgZD1jaSgpO2I9dm9pZCAwIT09Yz9jKGIpOmI7ZC5tZW1vaXplZFN0YXRlPWQuYmFzZVN0YXRlPWI7YT17cGVuZGluZzpudWxsLGludGVybGVhdmVkOm51bGwsbGFuZXM6MCxkaXNwYXRjaDpudWxsLGxhc3RSZW5kZXJlZFJlZHVjZXI6YSxsYXN0UmVuZGVyZWRTdGF0ZTpifTtkLnF1ZXVlPWE7YT1hLmRpc3BhdGNoPUdpLmJpbmQobnVsbCxOLGEpO3JldHVybltkLm1lbW9pemVkU3RhdGUsYV19LHVzZVJlZjpmdW5jdGlvbihhKXt2YXIgYj0KY2koKTthPXtjdXJyZW50OmF9O3JldHVybiBiLm1lbW9pemVkU3RhdGU9YX0sdXNlU3RhdGU6cWksdXNlRGVidWdWYWx1ZTpBaSx1c2VEZWZlcnJlZFZhbHVlOmZ1bmN0aW9uKGEpe3JldHVybiBjaSgpLm1lbW9pemVkU3RhdGU9YX0sdXNlVHJhbnNpdGlvbjpmdW5jdGlvbigpe3ZhciBhPXFpKCExKSxiPWFbMF07YT1FaS5iaW5kKG51bGwsYVsxXSk7Y2koKS5tZW1vaXplZFN0YXRlPWE7cmV0dXJuW2IsYV19LHVzZU11dGFibGVTb3VyY2U6ZnVuY3Rpb24oKXt9LHVzZVN5bmNFeHRlcm5hbFN0b3JlOmZ1bmN0aW9uKGEsYixjKXt2YXIgZD1OLGU9Y2koKTtpZihJKXtpZih2b2lkIDA9PT1jKXRocm93IEVycm9yKHAoNDA3KSk7Yz1jKCl9ZWxzZXtjPWIoKTtpZihudWxsPT09Uil0aHJvdyBFcnJvcihwKDM0OSkpOzAhPT0oUmgmMzApfHxuaShkLGIsYyl9ZS5tZW1vaXplZFN0YXRlPWM7dmFyIGY9e3ZhbHVlOmMsZ2V0U25hcHNob3Q6Yn07ZS5xdWV1ZT1mO3ZpKGtpLmJpbmQobnVsbCxkLApmLGEpLFthXSk7ZC5mbGFnc3w9MjA0ODtsaSg5LG1pLmJpbmQobnVsbCxkLGYsYyxiKSx2b2lkIDAsbnVsbCk7cmV0dXJuIGN9LHVzZUlkOmZ1bmN0aW9uKCl7dmFyIGE9Y2koKSxiPVIuaWRlbnRpZmllclByZWZpeDtpZihJKXt2YXIgYz1zZzt2YXIgZD1yZztjPShkJn4oMTw8MzItb2MoZCktMSkpLnRvU3RyaW5nKDMyKStjO2I9IjoiK2IrIlIiK2M7Yz1VaCsrOzA8YyYmKGIrPSJIIitjLnRvU3RyaW5nKDMyKSk7Yis9IjoifWVsc2UgYz1WaCsrLGI9IjoiK2IrInIiK2MudG9TdHJpbmcoMzIpKyI6IjtyZXR1cm4gYS5tZW1vaXplZFN0YXRlPWJ9LHVuc3RhYmxlX2lzTmV3UmVjb25jaWxlcjohMX0sWmg9e3JlYWRDb250ZXh0OlZnLHVzZUNhbGxiYWNrOkJpLHVzZUNvbnRleHQ6VmcsdXNlRWZmZWN0OmppLHVzZUltcGVyYXRpdmVIYW5kbGU6emksdXNlSW5zZXJ0aW9uRWZmZWN0OndpLHVzZUxheW91dEVmZmVjdDp4aSx1c2VNZW1vOkNpLHVzZVJlZHVjZXI6ZmksdXNlUmVmOnNpLHVzZVN0YXRlOmZ1bmN0aW9uKCl7cmV0dXJuIGZpKGVpKX0sCnVzZURlYnVnVmFsdWU6QWksdXNlRGVmZXJyZWRWYWx1ZTpmdW5jdGlvbihhKXt2YXIgYj1kaSgpO3JldHVybiBEaShiLE8ubWVtb2l6ZWRTdGF0ZSxhKX0sdXNlVHJhbnNpdGlvbjpmdW5jdGlvbigpe3ZhciBhPWZpKGVpKVswXSxiPWRpKCkubWVtb2l6ZWRTdGF0ZTtyZXR1cm5bYSxiXX0sdXNlTXV0YWJsZVNvdXJjZTpoaSx1c2VTeW5jRXh0ZXJuYWxTdG9yZTppaSx1c2VJZDpGaSx1bnN0YWJsZV9pc05ld1JlY29uY2lsZXI6ITF9LCRoPXtyZWFkQ29udGV4dDpWZyx1c2VDYWxsYmFjazpCaSx1c2VDb250ZXh0OlZnLHVzZUVmZmVjdDpqaSx1c2VJbXBlcmF0aXZlSGFuZGxlOnppLHVzZUluc2VydGlvbkVmZmVjdDp3aSx1c2VMYXlvdXRFZmZlY3Q6eGksdXNlTWVtbzpDaSx1c2VSZWR1Y2VyOmdpLHVzZVJlZjpzaSx1c2VTdGF0ZTpmdW5jdGlvbigpe3JldHVybiBnaShlaSl9LHVzZURlYnVnVmFsdWU6QWksdXNlRGVmZXJyZWRWYWx1ZTpmdW5jdGlvbihhKXt2YXIgYj1kaSgpO3JldHVybiBudWxsPT09Ck8/Yi5tZW1vaXplZFN0YXRlPWE6RGkoYixPLm1lbW9pemVkU3RhdGUsYSl9LHVzZVRyYW5zaXRpb246ZnVuY3Rpb24oKXt2YXIgYT1naShlaSlbMF0sYj1kaSgpLm1lbW9pemVkU3RhdGU7cmV0dXJuW2EsYl19LHVzZU11dGFibGVTb3VyY2U6aGksdXNlU3luY0V4dGVybmFsU3RvcmU6aWksdXNlSWQ6RmksdW5zdGFibGVfaXNOZXdSZWNvbmNpbGVyOiExfTtmdW5jdGlvbiBLaShhLGIpe3RyeXt2YXIgYz0iIixkPWI7ZG8gYys9UGEoZCksZD1kLnJldHVybjt3aGlsZShkKTt2YXIgZT1jfWNhdGNoKGYpe2U9IlxuRXJyb3IgZ2VuZXJhdGluZyBzdGFjazogIitmLm1lc3NhZ2UrIlxuIitmLnN0YWNrfXJldHVybnt2YWx1ZTphLHNvdXJjZTpiLHN0YWNrOmUsZGlnZXN0Om51bGx9fWZ1bmN0aW9uIExpKGEsYixjKXtyZXR1cm57dmFsdWU6YSxzb3VyY2U6bnVsbCxzdGFjazpudWxsIT1jP2M6bnVsbCxkaWdlc3Q6bnVsbCE9Yj9iOm51bGx9fQpmdW5jdGlvbiBNaShhLGIpe3RyeXtjb25zb2xlLmVycm9yKGIudmFsdWUpfWNhdGNoKGMpe3NldFRpbWVvdXQoZnVuY3Rpb24oKXt0aHJvdyBjO30pfX12YXIgTmk9ImZ1bmN0aW9uIj09PXR5cGVvZiBXZWFrTWFwP1dlYWtNYXA6TWFwO2Z1bmN0aW9uIE9pKGEsYixjKXtjPWNoKC0xLGMpO2MudGFnPTM7Yy5wYXlsb2FkPXtlbGVtZW50Om51bGx9O3ZhciBkPWIudmFsdWU7Yy5jYWxsYmFjaz1mdW5jdGlvbigpe1BpfHwoUGk9ITAsUWk9ZCk7TWkoYSxiKX07cmV0dXJuIGN9CmZ1bmN0aW9uIFJpKGEsYixjKXtjPWNoKC0xLGMpO2MudGFnPTM7dmFyIGQ9YS50eXBlLmdldERlcml2ZWRTdGF0ZUZyb21FcnJvcjtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGQpe3ZhciBlPWIudmFsdWU7Yy5wYXlsb2FkPWZ1bmN0aW9uKCl7cmV0dXJuIGQoZSl9O2MuY2FsbGJhY2s9ZnVuY3Rpb24oKXtNaShhLGIpfX12YXIgZj1hLnN0YXRlTm9kZTtudWxsIT09ZiYmImZ1bmN0aW9uIj09PXR5cGVvZiBmLmNvbXBvbmVudERpZENhdGNoJiYoYy5jYWxsYmFjaz1mdW5jdGlvbigpe01pKGEsYik7ImZ1bmN0aW9uIiE9PXR5cGVvZiBkJiYobnVsbD09PVNpP1NpPW5ldyBTZXQoW3RoaXNdKTpTaS5hZGQodGhpcykpO3ZhciBjPWIuc3RhY2s7dGhpcy5jb21wb25lbnREaWRDYXRjaChiLnZhbHVlLHtjb21wb25lbnRTdGFjazpudWxsIT09Yz9jOiIifSl9KTtyZXR1cm4gY30KZnVuY3Rpb24gVGkoYSxiLGMpe3ZhciBkPWEucGluZ0NhY2hlO2lmKG51bGw9PT1kKXtkPWEucGluZ0NhY2hlPW5ldyBOaTt2YXIgZT1uZXcgU2V0O2Quc2V0KGIsZSl9ZWxzZSBlPWQuZ2V0KGIpLHZvaWQgMD09PWUmJihlPW5ldyBTZXQsZC5zZXQoYixlKSk7ZS5oYXMoYyl8fChlLmFkZChjKSxhPVVpLmJpbmQobnVsbCxhLGIsYyksYi50aGVuKGEsYSkpfWZ1bmN0aW9uIFZpKGEpe2Rve3ZhciBiO2lmKGI9MTM9PT1hLnRhZyliPWEubWVtb2l6ZWRTdGF0ZSxiPW51bGwhPT1iP251bGwhPT1iLmRlaHlkcmF0ZWQ/ITA6ITE6ITA7aWYoYilyZXR1cm4gYTthPWEucmV0dXJufXdoaWxlKG51bGwhPT1hKTtyZXR1cm4gbnVsbH0KZnVuY3Rpb24gV2koYSxiLGMsZCxlKXtpZigwPT09KGEubW9kZSYxKSlyZXR1cm4gYT09PWI/YS5mbGFnc3w9NjU1MzY6KGEuZmxhZ3N8PTEyOCxjLmZsYWdzfD0xMzEwNzIsYy5mbGFncyY9LTUyODA1LDE9PT1jLnRhZyYmKG51bGw9PT1jLmFsdGVybmF0ZT9jLnRhZz0xNzooYj1jaCgtMSwxKSxiLnRhZz0yLGRoKGMsYiwxKSkpLGMubGFuZXN8PTEpLGE7YS5mbGFnc3w9NjU1MzY7YS5sYW5lcz1lO3JldHVybiBhfXZhciBYaT11YS5SZWFjdEN1cnJlbnRPd25lcixVZz0hMTtmdW5jdGlvbiBZaShhLGIsYyxkKXtiLmNoaWxkPW51bGw9PT1hP0NoKGIsbnVsbCxjLGQpOkJoKGIsYS5jaGlsZCxjLGQpfQpmdW5jdGlvbiBaaShhLGIsYyxkLGUpe2M9Yy5yZW5kZXI7dmFyIGY9Yi5yZWY7VGcoYixlKTtkPVhoKGEsYixjLGQsZixlKTtjPWJpKCk7aWYobnVsbCE9PWEmJiFVZylyZXR1cm4gYi51cGRhdGVRdWV1ZT1hLnVwZGF0ZVF1ZXVlLGIuZmxhZ3MmPS0yMDUzLGEubGFuZXMmPX5lLCRpKGEsYixlKTtJJiZjJiZ2ZyhiKTtiLmZsYWdzfD0xO1lpKGEsYixkLGUpO3JldHVybiBiLmNoaWxkfQpmdW5jdGlvbiBhaihhLGIsYyxkLGUpe2lmKG51bGw9PT1hKXt2YXIgZj1jLnR5cGU7aWYoImZ1bmN0aW9uIj09PXR5cGVvZiBmJiYhYmooZikmJnZvaWQgMD09PWYuZGVmYXVsdFByb3BzJiZudWxsPT09Yy5jb21wYXJlJiZ2b2lkIDA9PT1jLmRlZmF1bHRQcm9wcylyZXR1cm4gYi50YWc9MTUsYi50eXBlPWYsY2ooYSxiLGYsZCxlKTthPXloKGMudHlwZSxudWxsLGQsYixiLm1vZGUsZSk7YS5yZWY9Yi5yZWY7YS5yZXR1cm49YjtyZXR1cm4gYi5jaGlsZD1hfWY9YS5jaGlsZDtpZigwPT09KGEubGFuZXMmZSkpe3ZhciBnPWYubWVtb2l6ZWRQcm9wcztjPWMuY29tcGFyZTtjPW51bGwhPT1jP2M6SWU7aWYoYyhnLGQpJiZhLnJlZj09PWIucmVmKXJldHVybiAkaShhLGIsZSl9Yi5mbGFnc3w9MTthPXdoKGYsZCk7YS5yZWY9Yi5yZWY7YS5yZXR1cm49YjtyZXR1cm4gYi5jaGlsZD1hfQpmdW5jdGlvbiBjaihhLGIsYyxkLGUpe2lmKG51bGwhPT1hKXt2YXIgZj1hLm1lbW9pemVkUHJvcHM7aWYoSWUoZixkKSYmYS5yZWY9PT1iLnJlZilpZihVZz0hMSxiLnBlbmRpbmdQcm9wcz1kPWYsMCE9PShhLmxhbmVzJmUpKTAhPT0oYS5mbGFncyYxMzEwNzIpJiYoVWc9ITApO2Vsc2UgcmV0dXJuIGIubGFuZXM9YS5sYW5lcywkaShhLGIsZSl9cmV0dXJuIGRqKGEsYixjLGQsZSl9CmZ1bmN0aW9uIGVqKGEsYixjKXt2YXIgZD1iLnBlbmRpbmdQcm9wcyxlPWQuY2hpbGRyZW4sZj1udWxsIT09YT9hLm1lbW9pemVkU3RhdGU6bnVsbDtpZigiaGlkZGVuIj09PWQubW9kZSlpZigwPT09KGIubW9kZSYxKSliLm1lbW9pemVkU3RhdGU9e2Jhc2VMYW5lczowLGNhY2hlUG9vbDpudWxsLHRyYW5zaXRpb25zOm51bGx9LEcoZmosZ2opLGdqfD1jO2Vsc2V7aWYoMD09PShjJjEwNzM3NDE4MjQpKXJldHVybiBhPW51bGwhPT1mP2YuYmFzZUxhbmVzfGM6YyxiLmxhbmVzPWIuY2hpbGRMYW5lcz0xMDczNzQxODI0LGIubWVtb2l6ZWRTdGF0ZT17YmFzZUxhbmVzOmEsY2FjaGVQb29sOm51bGwsdHJhbnNpdGlvbnM6bnVsbH0sYi51cGRhdGVRdWV1ZT1udWxsLEcoZmosZ2opLGdqfD1hLG51bGw7Yi5tZW1vaXplZFN0YXRlPXtiYXNlTGFuZXM6MCxjYWNoZVBvb2w6bnVsbCx0cmFuc2l0aW9uczpudWxsfTtkPW51bGwhPT1mP2YuYmFzZUxhbmVzOmM7Ryhmaixnaik7Z2p8PWR9ZWxzZSBudWxsIT09CmY/KGQ9Zi5iYXNlTGFuZXN8YyxiLm1lbW9pemVkU3RhdGU9bnVsbCk6ZD1jLEcoZmosZ2opLGdqfD1kO1lpKGEsYixlLGMpO3JldHVybiBiLmNoaWxkfWZ1bmN0aW9uIGhqKGEsYil7dmFyIGM9Yi5yZWY7aWYobnVsbD09PWEmJm51bGwhPT1jfHxudWxsIT09YSYmYS5yZWYhPT1jKWIuZmxhZ3N8PTUxMixiLmZsYWdzfD0yMDk3MTUyfWZ1bmN0aW9uIGRqKGEsYixjLGQsZSl7dmFyIGY9WmYoYyk/WGY6SC5jdXJyZW50O2Y9WWYoYixmKTtUZyhiLGUpO2M9WGgoYSxiLGMsZCxmLGUpO2Q9YmkoKTtpZihudWxsIT09YSYmIVVnKXJldHVybiBiLnVwZGF0ZVF1ZXVlPWEudXBkYXRlUXVldWUsYi5mbGFncyY9LTIwNTMsYS5sYW5lcyY9fmUsJGkoYSxiLGUpO0kmJmQmJnZnKGIpO2IuZmxhZ3N8PTE7WWkoYSxiLGMsZSk7cmV0dXJuIGIuY2hpbGR9CmZ1bmN0aW9uIGlqKGEsYixjLGQsZSl7aWYoWmYoYykpe3ZhciBmPSEwO2NnKGIpfWVsc2UgZj0hMTtUZyhiLGUpO2lmKG51bGw9PT1iLnN0YXRlTm9kZSlqaihhLGIpLHBoKGIsYyxkKSxyaChiLGMsZCxlKSxkPSEwO2Vsc2UgaWYobnVsbD09PWEpe3ZhciBnPWIuc3RhdGVOb2RlLGg9Yi5tZW1vaXplZFByb3BzO2cucHJvcHM9aDt2YXIgaz1nLmNvbnRleHQsbD1jLmNvbnRleHRUeXBlOyJvYmplY3QiPT09dHlwZW9mIGwmJm51bGwhPT1sP2w9VmcobCk6KGw9WmYoYyk/WGY6SC5jdXJyZW50LGw9WWYoYixsKSk7dmFyIG09Yy5nZXREZXJpdmVkU3RhdGVGcm9tUHJvcHMscT0iZnVuY3Rpb24iPT09dHlwZW9mIG18fCJmdW5jdGlvbiI9PT10eXBlb2YgZy5nZXRTbmFwc2hvdEJlZm9yZVVwZGF0ZTtxfHwiZnVuY3Rpb24iIT09dHlwZW9mIGcuVU5TQUZFX2NvbXBvbmVudFdpbGxSZWNlaXZlUHJvcHMmJiJmdW5jdGlvbiIhPT10eXBlb2YgZy5jb21wb25lbnRXaWxsUmVjZWl2ZVByb3BzfHwKKGghPT1kfHxrIT09bCkmJnFoKGIsZyxkLGwpOyRnPSExO3ZhciByPWIubWVtb2l6ZWRTdGF0ZTtnLnN0YXRlPXI7Z2goYixkLGcsZSk7az1iLm1lbW9pemVkU3RhdGU7aCE9PWR8fHIhPT1rfHxXZi5jdXJyZW50fHwkZz8oImZ1bmN0aW9uIj09PXR5cGVvZiBtJiYoa2goYixjLG0sZCksaz1iLm1lbW9pemVkU3RhdGUpLChoPSRnfHxvaChiLGMsaCxkLHIsayxsKSk/KHF8fCJmdW5jdGlvbiIhPT10eXBlb2YgZy5VTlNBRkVfY29tcG9uZW50V2lsbE1vdW50JiYiZnVuY3Rpb24iIT09dHlwZW9mIGcuY29tcG9uZW50V2lsbE1vdW50fHwoImZ1bmN0aW9uIj09PXR5cGVvZiBnLmNvbXBvbmVudFdpbGxNb3VudCYmZy5jb21wb25lbnRXaWxsTW91bnQoKSwiZnVuY3Rpb24iPT09dHlwZW9mIGcuVU5TQUZFX2NvbXBvbmVudFdpbGxNb3VudCYmZy5VTlNBRkVfY29tcG9uZW50V2lsbE1vdW50KCkpLCJmdW5jdGlvbiI9PT10eXBlb2YgZy5jb21wb25lbnREaWRNb3VudCYmKGIuZmxhZ3N8PTQxOTQzMDgpKToKKCJmdW5jdGlvbiI9PT10eXBlb2YgZy5jb21wb25lbnREaWRNb3VudCYmKGIuZmxhZ3N8PTQxOTQzMDgpLGIubWVtb2l6ZWRQcm9wcz1kLGIubWVtb2l6ZWRTdGF0ZT1rKSxnLnByb3BzPWQsZy5zdGF0ZT1rLGcuY29udGV4dD1sLGQ9aCk6KCJmdW5jdGlvbiI9PT10eXBlb2YgZy5jb21wb25lbnREaWRNb3VudCYmKGIuZmxhZ3N8PTQxOTQzMDgpLGQ9ITEpfWVsc2V7Zz1iLnN0YXRlTm9kZTtiaChhLGIpO2g9Yi5tZW1vaXplZFByb3BzO2w9Yi50eXBlPT09Yi5lbGVtZW50VHlwZT9oOkxnKGIudHlwZSxoKTtnLnByb3BzPWw7cT1iLnBlbmRpbmdQcm9wcztyPWcuY29udGV4dDtrPWMuY29udGV4dFR5cGU7Im9iamVjdCI9PT10eXBlb2YgayYmbnVsbCE9PWs/az1WZyhrKTooaz1aZihjKT9YZjpILmN1cnJlbnQsaz1ZZihiLGspKTt2YXIgeT1jLmdldERlcml2ZWRTdGF0ZUZyb21Qcm9wczsobT0iZnVuY3Rpb24iPT09dHlwZW9mIHl8fCJmdW5jdGlvbiI9PT10eXBlb2YgZy5nZXRTbmFwc2hvdEJlZm9yZVVwZGF0ZSl8fAoiZnVuY3Rpb24iIT09dHlwZW9mIGcuVU5TQUZFX2NvbXBvbmVudFdpbGxSZWNlaXZlUHJvcHMmJiJmdW5jdGlvbiIhPT10eXBlb2YgZy5jb21wb25lbnRXaWxsUmVjZWl2ZVByb3BzfHwoaCE9PXF8fHIhPT1rKSYmcWgoYixnLGQsayk7JGc9ITE7cj1iLm1lbW9pemVkU3RhdGU7Zy5zdGF0ZT1yO2doKGIsZCxnLGUpO3ZhciBuPWIubWVtb2l6ZWRTdGF0ZTtoIT09cXx8ciE9PW58fFdmLmN1cnJlbnR8fCRnPygiZnVuY3Rpb24iPT09dHlwZW9mIHkmJihraChiLGMseSxkKSxuPWIubWVtb2l6ZWRTdGF0ZSksKGw9JGd8fG9oKGIsYyxsLGQscixuLGspfHwhMSk/KG18fCJmdW5jdGlvbiIhPT10eXBlb2YgZy5VTlNBRkVfY29tcG9uZW50V2lsbFVwZGF0ZSYmImZ1bmN0aW9uIiE9PXR5cGVvZiBnLmNvbXBvbmVudFdpbGxVcGRhdGV8fCgiZnVuY3Rpb24iPT09dHlwZW9mIGcuY29tcG9uZW50V2lsbFVwZGF0ZSYmZy5jb21wb25lbnRXaWxsVXBkYXRlKGQsbixrKSwiZnVuY3Rpb24iPT09dHlwZW9mIGcuVU5TQUZFX2NvbXBvbmVudFdpbGxVcGRhdGUmJgpnLlVOU0FGRV9jb21wb25lbnRXaWxsVXBkYXRlKGQsbixrKSksImZ1bmN0aW9uIj09PXR5cGVvZiBnLmNvbXBvbmVudERpZFVwZGF0ZSYmKGIuZmxhZ3N8PTQpLCJmdW5jdGlvbiI9PT10eXBlb2YgZy5nZXRTbmFwc2hvdEJlZm9yZVVwZGF0ZSYmKGIuZmxhZ3N8PTEwMjQpKTooImZ1bmN0aW9uIiE9PXR5cGVvZiBnLmNvbXBvbmVudERpZFVwZGF0ZXx8aD09PWEubWVtb2l6ZWRQcm9wcyYmcj09PWEubWVtb2l6ZWRTdGF0ZXx8KGIuZmxhZ3N8PTQpLCJmdW5jdGlvbiIhPT10eXBlb2YgZy5nZXRTbmFwc2hvdEJlZm9yZVVwZGF0ZXx8aD09PWEubWVtb2l6ZWRQcm9wcyYmcj09PWEubWVtb2l6ZWRTdGF0ZXx8KGIuZmxhZ3N8PTEwMjQpLGIubWVtb2l6ZWRQcm9wcz1kLGIubWVtb2l6ZWRTdGF0ZT1uKSxnLnByb3BzPWQsZy5zdGF0ZT1uLGcuY29udGV4dD1rLGQ9bCk6KCJmdW5jdGlvbiIhPT10eXBlb2YgZy5jb21wb25lbnREaWRVcGRhdGV8fGg9PT1hLm1lbW9pemVkUHJvcHMmJnI9PT0KYS5tZW1vaXplZFN0YXRlfHwoYi5mbGFnc3w9NCksImZ1bmN0aW9uIiE9PXR5cGVvZiBnLmdldFNuYXBzaG90QmVmb3JlVXBkYXRlfHxoPT09YS5tZW1vaXplZFByb3BzJiZyPT09YS5tZW1vaXplZFN0YXRlfHwoYi5mbGFnc3w9MTAyNCksZD0hMSl9cmV0dXJuIGtqKGEsYixjLGQsZixlKX0KZnVuY3Rpb24ga2ooYSxiLGMsZCxlLGYpe2hqKGEsYik7dmFyIGc9MCE9PShiLmZsYWdzJjEyOCk7aWYoIWQmJiFnKXJldHVybiBlJiZkZyhiLGMsITEpLCRpKGEsYixmKTtkPWIuc3RhdGVOb2RlO1hpLmN1cnJlbnQ9Yjt2YXIgaD1nJiYiZnVuY3Rpb24iIT09dHlwZW9mIGMuZ2V0RGVyaXZlZFN0YXRlRnJvbUVycm9yP251bGw6ZC5yZW5kZXIoKTtiLmZsYWdzfD0xO251bGwhPT1hJiZnPyhiLmNoaWxkPUJoKGIsYS5jaGlsZCxudWxsLGYpLGIuY2hpbGQ9QmgoYixudWxsLGgsZikpOllpKGEsYixoLGYpO2IubWVtb2l6ZWRTdGF0ZT1kLnN0YXRlO2UmJmRnKGIsYywhMCk7cmV0dXJuIGIuY2hpbGR9ZnVuY3Rpb24gbGooYSl7dmFyIGI9YS5zdGF0ZU5vZGU7Yi5wZW5kaW5nQ29udGV4dD9hZyhhLGIucGVuZGluZ0NvbnRleHQsYi5wZW5kaW5nQ29udGV4dCE9PWIuY29udGV4dCk6Yi5jb250ZXh0JiZhZyhhLGIuY29udGV4dCwhMSk7SWgoYSxiLmNvbnRhaW5lckluZm8pfQpmdW5jdGlvbiBtaihhLGIsYyxkLGUpe0lnKCk7SmcoZSk7Yi5mbGFnc3w9MjU2O1lpKGEsYixjLGQpO3JldHVybiBiLmNoaWxkfXZhciBuaj17ZGVoeWRyYXRlZDpudWxsLHRyZWVDb250ZXh0Om51bGwscmV0cnlMYW5lOjB9O2Z1bmN0aW9uIG9qKGEpe3JldHVybntiYXNlTGFuZXM6YSxjYWNoZVBvb2w6bnVsbCx0cmFuc2l0aW9uczpudWxsfX0KZnVuY3Rpb24gcGooYSxiLGMpe3ZhciBkPWIucGVuZGluZ1Byb3BzLGU9TS5jdXJyZW50LGY9ITEsZz0wIT09KGIuZmxhZ3MmMTI4KSxoOyhoPWcpfHwoaD1udWxsIT09YSYmbnVsbD09PWEubWVtb2l6ZWRTdGF0ZT8hMTowIT09KGUmMikpO2lmKGgpZj0hMCxiLmZsYWdzJj0tMTI5O2Vsc2UgaWYobnVsbD09PWF8fG51bGwhPT1hLm1lbW9pemVkU3RhdGUpZXw9MTtHKE0sZSYxKTtpZihudWxsPT09YSl7RWcoYik7YT1iLm1lbW9pemVkU3RhdGU7aWYobnVsbCE9PWEmJihhPWEuZGVoeWRyYXRlZCxudWxsIT09YSkpcmV0dXJuIDA9PT0oYi5tb2RlJjEpP2IubGFuZXM9MToiJCEiPT09YS5kYXRhP2IubGFuZXM9ODpiLmxhbmVzPTEwNzM3NDE4MjQsbnVsbDtnPWQuY2hpbGRyZW47YT1kLmZhbGxiYWNrO3JldHVybiBmPyhkPWIubW9kZSxmPWIuY2hpbGQsZz17bW9kZToiaGlkZGVuIixjaGlsZHJlbjpnfSwwPT09KGQmMSkmJm51bGwhPT1mPyhmLmNoaWxkTGFuZXM9MCxmLnBlbmRpbmdQcm9wcz0KZyk6Zj1xaihnLGQsMCxudWxsKSxhPUFoKGEsZCxjLG51bGwpLGYucmV0dXJuPWIsYS5yZXR1cm49YixmLnNpYmxpbmc9YSxiLmNoaWxkPWYsYi5jaGlsZC5tZW1vaXplZFN0YXRlPW9qKGMpLGIubWVtb2l6ZWRTdGF0ZT1uaixhKTpyaihiLGcpfWU9YS5tZW1vaXplZFN0YXRlO2lmKG51bGwhPT1lJiYoaD1lLmRlaHlkcmF0ZWQsbnVsbCE9PWgpKXJldHVybiBzaihhLGIsZyxkLGgsZSxjKTtpZihmKXtmPWQuZmFsbGJhY2s7Zz1iLm1vZGU7ZT1hLmNoaWxkO2g9ZS5zaWJsaW5nO3ZhciBrPXttb2RlOiJoaWRkZW4iLGNoaWxkcmVuOmQuY2hpbGRyZW59OzA9PT0oZyYxKSYmYi5jaGlsZCE9PWU/KGQ9Yi5jaGlsZCxkLmNoaWxkTGFuZXM9MCxkLnBlbmRpbmdQcm9wcz1rLGIuZGVsZXRpb25zPW51bGwpOihkPXdoKGUsayksZC5zdWJ0cmVlRmxhZ3M9ZS5zdWJ0cmVlRmxhZ3MmMTQ2ODAwNjQpO251bGwhPT1oP2Y9d2goaCxmKTooZj1BaChmLGcsYyxudWxsKSxmLmZsYWdzfD0yKTtmLnJldHVybj0KYjtkLnJldHVybj1iO2Quc2libGluZz1mO2IuY2hpbGQ9ZDtkPWY7Zj1iLmNoaWxkO2c9YS5jaGlsZC5tZW1vaXplZFN0YXRlO2c9bnVsbD09PWc/b2ooYyk6e2Jhc2VMYW5lczpnLmJhc2VMYW5lc3xjLGNhY2hlUG9vbDpudWxsLHRyYW5zaXRpb25zOmcudHJhbnNpdGlvbnN9O2YubWVtb2l6ZWRTdGF0ZT1nO2YuY2hpbGRMYW5lcz1hLmNoaWxkTGFuZXMmfmM7Yi5tZW1vaXplZFN0YXRlPW5qO3JldHVybiBkfWY9YS5jaGlsZDthPWYuc2libGluZztkPXdoKGYse21vZGU6InZpc2libGUiLGNoaWxkcmVuOmQuY2hpbGRyZW59KTswPT09KGIubW9kZSYxKSYmKGQubGFuZXM9Yyk7ZC5yZXR1cm49YjtkLnNpYmxpbmc9bnVsbDtudWxsIT09YSYmKGM9Yi5kZWxldGlvbnMsbnVsbD09PWM/KGIuZGVsZXRpb25zPVthXSxiLmZsYWdzfD0xNik6Yy5wdXNoKGEpKTtiLmNoaWxkPWQ7Yi5tZW1vaXplZFN0YXRlPW51bGw7cmV0dXJuIGR9CmZ1bmN0aW9uIHJqKGEsYil7Yj1xaih7bW9kZToidmlzaWJsZSIsY2hpbGRyZW46Yn0sYS5tb2RlLDAsbnVsbCk7Yi5yZXR1cm49YTtyZXR1cm4gYS5jaGlsZD1ifWZ1bmN0aW9uIHRqKGEsYixjLGQpe251bGwhPT1kJiZKZyhkKTtCaChiLGEuY2hpbGQsbnVsbCxjKTthPXJqKGIsYi5wZW5kaW5nUHJvcHMuY2hpbGRyZW4pO2EuZmxhZ3N8PTI7Yi5tZW1vaXplZFN0YXRlPW51bGw7cmV0dXJuIGF9CmZ1bmN0aW9uIHNqKGEsYixjLGQsZSxmLGcpe2lmKGMpe2lmKGIuZmxhZ3MmMjU2KXJldHVybiBiLmZsYWdzJj0tMjU3LGQ9TGkoRXJyb3IocCg0MjIpKSksdGooYSxiLGcsZCk7aWYobnVsbCE9PWIubWVtb2l6ZWRTdGF0ZSlyZXR1cm4gYi5jaGlsZD1hLmNoaWxkLGIuZmxhZ3N8PTEyOCxudWxsO2Y9ZC5mYWxsYmFjaztlPWIubW9kZTtkPXFqKHttb2RlOiJ2aXNpYmxlIixjaGlsZHJlbjpkLmNoaWxkcmVufSxlLDAsbnVsbCk7Zj1BaChmLGUsZyxudWxsKTtmLmZsYWdzfD0yO2QucmV0dXJuPWI7Zi5yZXR1cm49YjtkLnNpYmxpbmc9ZjtiLmNoaWxkPWQ7MCE9PShiLm1vZGUmMSkmJkJoKGIsYS5jaGlsZCxudWxsLGcpO2IuY2hpbGQubWVtb2l6ZWRTdGF0ZT1vaihnKTtiLm1lbW9pemVkU3RhdGU9bmo7cmV0dXJuIGZ9aWYoMD09PShiLm1vZGUmMSkpcmV0dXJuIHRqKGEsYixnLG51bGwpO2lmKCIkISI9PT1lLmRhdGEpe2Q9ZS5uZXh0U2libGluZyYmZS5uZXh0U2libGluZy5kYXRhc2V0OwppZihkKXZhciBoPWQuZGdzdDtkPWg7Zj1FcnJvcihwKDQxOSkpO2Q9TGkoZixkLHZvaWQgMCk7cmV0dXJuIHRqKGEsYixnLGQpfWg9MCE9PShnJmEuY2hpbGRMYW5lcyk7aWYoVWd8fGgpe2Q9UjtpZihudWxsIT09ZCl7c3dpdGNoKGcmLWcpe2Nhc2UgNDplPTI7YnJlYWs7Y2FzZSAxNjplPTg7YnJlYWs7Y2FzZSA2NDpjYXNlIDEyODpjYXNlIDI1NjpjYXNlIDUxMjpjYXNlIDEwMjQ6Y2FzZSAyMDQ4OmNhc2UgNDA5NjpjYXNlIDgxOTI6Y2FzZSAxNjM4NDpjYXNlIDMyNzY4OmNhc2UgNjU1MzY6Y2FzZSAxMzEwNzI6Y2FzZSAyNjIxNDQ6Y2FzZSA1MjQyODg6Y2FzZSAxMDQ4NTc2OmNhc2UgMjA5NzE1MjpjYXNlIDQxOTQzMDQ6Y2FzZSA4Mzg4NjA4OmNhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpjYXNlIDY3MTA4ODY0OmU9MzI7YnJlYWs7Y2FzZSA1MzY4NzA5MTI6ZT0yNjg0MzU0NTY7YnJlYWs7ZGVmYXVsdDplPTB9ZT0wIT09KGUmKGQuc3VzcGVuZGVkTGFuZXN8ZykpPzA6ZTsKMCE9PWUmJmUhPT1mLnJldHJ5TGFuZSYmKGYucmV0cnlMYW5lPWUsWmcoYSxlKSxtaChkLGEsZSwtMSkpfXVqKCk7ZD1MaShFcnJvcihwKDQyMSkpKTtyZXR1cm4gdGooYSxiLGcsZCl9aWYoIiQ/Ij09PWUuZGF0YSlyZXR1cm4gYi5mbGFnc3w9MTI4LGIuY2hpbGQ9YS5jaGlsZCxiPXZqLmJpbmQobnVsbCxhKSxlLl9yZWFjdFJldHJ5PWIsbnVsbDthPWYudHJlZUNvbnRleHQ7eWc9TGYoZS5uZXh0U2libGluZyk7eGc9YjtJPSEwO3pnPW51bGw7bnVsbCE9PWEmJihvZ1twZysrXT1yZyxvZ1twZysrXT1zZyxvZ1twZysrXT1xZyxyZz1hLmlkLHNnPWEub3ZlcmZsb3cscWc9Yik7Yj1yaihiLGQuY2hpbGRyZW4pO2IuZmxhZ3N8PTQwOTY7cmV0dXJuIGJ9ZnVuY3Rpb24gd2ooYSxiLGMpe2EubGFuZXN8PWI7dmFyIGQ9YS5hbHRlcm5hdGU7bnVsbCE9PWQmJihkLmxhbmVzfD1iKTtTZyhhLnJldHVybixiLGMpfQpmdW5jdGlvbiB4aihhLGIsYyxkLGUpe3ZhciBmPWEubWVtb2l6ZWRTdGF0ZTtudWxsPT09Zj9hLm1lbW9pemVkU3RhdGU9e2lzQmFja3dhcmRzOmIscmVuZGVyaW5nOm51bGwscmVuZGVyaW5nU3RhcnRUaW1lOjAsbGFzdDpkLHRhaWw6Yyx0YWlsTW9kZTplfTooZi5pc0JhY2t3YXJkcz1iLGYucmVuZGVyaW5nPW51bGwsZi5yZW5kZXJpbmdTdGFydFRpbWU9MCxmLmxhc3Q9ZCxmLnRhaWw9YyxmLnRhaWxNb2RlPWUpfQpmdW5jdGlvbiB5aihhLGIsYyl7dmFyIGQ9Yi5wZW5kaW5nUHJvcHMsZT1kLnJldmVhbE9yZGVyLGY9ZC50YWlsO1lpKGEsYixkLmNoaWxkcmVuLGMpO2Q9TS5jdXJyZW50O2lmKDAhPT0oZCYyKSlkPWQmMXwyLGIuZmxhZ3N8PTEyODtlbHNle2lmKG51bGwhPT1hJiYwIT09KGEuZmxhZ3MmMTI4KSlhOmZvcihhPWIuY2hpbGQ7bnVsbCE9PWE7KXtpZigxMz09PWEudGFnKW51bGwhPT1hLm1lbW9pemVkU3RhdGUmJndqKGEsYyxiKTtlbHNlIGlmKDE5PT09YS50YWcpd2ooYSxjLGIpO2Vsc2UgaWYobnVsbCE9PWEuY2hpbGQpe2EuY2hpbGQucmV0dXJuPWE7YT1hLmNoaWxkO2NvbnRpbnVlfWlmKGE9PT1iKWJyZWFrIGE7Zm9yKDtudWxsPT09YS5zaWJsaW5nOyl7aWYobnVsbD09PWEucmV0dXJufHxhLnJldHVybj09PWIpYnJlYWsgYTthPWEucmV0dXJufWEuc2libGluZy5yZXR1cm49YS5yZXR1cm47YT1hLnNpYmxpbmd9ZCY9MX1HKE0sZCk7aWYoMD09PShiLm1vZGUmMSkpYi5tZW1vaXplZFN0YXRlPQpudWxsO2Vsc2Ugc3dpdGNoKGUpe2Nhc2UgImZvcndhcmRzIjpjPWIuY2hpbGQ7Zm9yKGU9bnVsbDtudWxsIT09YzspYT1jLmFsdGVybmF0ZSxudWxsIT09YSYmbnVsbD09PU1oKGEpJiYoZT1jKSxjPWMuc2libGluZztjPWU7bnVsbD09PWM/KGU9Yi5jaGlsZCxiLmNoaWxkPW51bGwpOihlPWMuc2libGluZyxjLnNpYmxpbmc9bnVsbCk7eGooYiwhMSxlLGMsZik7YnJlYWs7Y2FzZSAiYmFja3dhcmRzIjpjPW51bGw7ZT1iLmNoaWxkO2ZvcihiLmNoaWxkPW51bGw7bnVsbCE9PWU7KXthPWUuYWx0ZXJuYXRlO2lmKG51bGwhPT1hJiZudWxsPT09TWgoYSkpe2IuY2hpbGQ9ZTticmVha31hPWUuc2libGluZztlLnNpYmxpbmc9YztjPWU7ZT1hfXhqKGIsITAsYyxudWxsLGYpO2JyZWFrO2Nhc2UgInRvZ2V0aGVyIjp4aihiLCExLG51bGwsbnVsbCx2b2lkIDApO2JyZWFrO2RlZmF1bHQ6Yi5tZW1vaXplZFN0YXRlPW51bGx9cmV0dXJuIGIuY2hpbGR9CmZ1bmN0aW9uIGpqKGEsYil7MD09PShiLm1vZGUmMSkmJm51bGwhPT1hJiYoYS5hbHRlcm5hdGU9bnVsbCxiLmFsdGVybmF0ZT1udWxsLGIuZmxhZ3N8PTIpfWZ1bmN0aW9uICRpKGEsYixjKXtudWxsIT09YSYmKGIuZGVwZW5kZW5jaWVzPWEuZGVwZW5kZW5jaWVzKTtoaHw9Yi5sYW5lcztpZigwPT09KGMmYi5jaGlsZExhbmVzKSlyZXR1cm4gbnVsbDtpZihudWxsIT09YSYmYi5jaGlsZCE9PWEuY2hpbGQpdGhyb3cgRXJyb3IocCgxNTMpKTtpZihudWxsIT09Yi5jaGlsZCl7YT1iLmNoaWxkO2M9d2goYSxhLnBlbmRpbmdQcm9wcyk7Yi5jaGlsZD1jO2ZvcihjLnJldHVybj1iO251bGwhPT1hLnNpYmxpbmc7KWE9YS5zaWJsaW5nLGM9Yy5zaWJsaW5nPXdoKGEsYS5wZW5kaW5nUHJvcHMpLGMucmV0dXJuPWI7Yy5zaWJsaW5nPW51bGx9cmV0dXJuIGIuY2hpbGR9CmZ1bmN0aW9uIHpqKGEsYixjKXtzd2l0Y2goYi50YWcpe2Nhc2UgMzpsaihiKTtJZygpO2JyZWFrO2Nhc2UgNTpLaChiKTticmVhaztjYXNlIDE6WmYoYi50eXBlKSYmY2coYik7YnJlYWs7Y2FzZSA0OkloKGIsYi5zdGF0ZU5vZGUuY29udGFpbmVySW5mbyk7YnJlYWs7Y2FzZSAxMDp2YXIgZD1iLnR5cGUuX2NvbnRleHQsZT1iLm1lbW9pemVkUHJvcHMudmFsdWU7RyhNZyxkLl9jdXJyZW50VmFsdWUpO2QuX2N1cnJlbnRWYWx1ZT1lO2JyZWFrO2Nhc2UgMTM6ZD1iLm1lbW9pemVkU3RhdGU7aWYobnVsbCE9PWQpe2lmKG51bGwhPT1kLmRlaHlkcmF0ZWQpcmV0dXJuIEcoTSxNLmN1cnJlbnQmMSksYi5mbGFnc3w9MTI4LG51bGw7aWYoMCE9PShjJmIuY2hpbGQuY2hpbGRMYW5lcykpcmV0dXJuIHBqKGEsYixjKTtHKE0sTS5jdXJyZW50JjEpO2E9JGkoYSxiLGMpO3JldHVybiBudWxsIT09YT9hLnNpYmxpbmc6bnVsbH1HKE0sTS5jdXJyZW50JjEpO2JyZWFrO2Nhc2UgMTk6ZD0wIT09KGMmCmIuY2hpbGRMYW5lcyk7aWYoMCE9PShhLmZsYWdzJjEyOCkpe2lmKGQpcmV0dXJuIHlqKGEsYixjKTtiLmZsYWdzfD0xMjh9ZT1iLm1lbW9pemVkU3RhdGU7bnVsbCE9PWUmJihlLnJlbmRlcmluZz1udWxsLGUudGFpbD1udWxsLGUubGFzdEVmZmVjdD1udWxsKTtHKE0sTS5jdXJyZW50KTtpZihkKWJyZWFrO2Vsc2UgcmV0dXJuIG51bGw7Y2FzZSAyMjpjYXNlIDIzOnJldHVybiBiLmxhbmVzPTAsZWooYSxiLGMpfXJldHVybiAkaShhLGIsYyl9dmFyIEFqLEJqLENqLERqOwpBaj1mdW5jdGlvbihhLGIpe2Zvcih2YXIgYz1iLmNoaWxkO251bGwhPT1jOyl7aWYoNT09PWMudGFnfHw2PT09Yy50YWcpYS5hcHBlbmRDaGlsZChjLnN0YXRlTm9kZSk7ZWxzZSBpZig0IT09Yy50YWcmJm51bGwhPT1jLmNoaWxkKXtjLmNoaWxkLnJldHVybj1jO2M9Yy5jaGlsZDtjb250aW51ZX1pZihjPT09YilicmVhaztmb3IoO251bGw9PT1jLnNpYmxpbmc7KXtpZihudWxsPT09Yy5yZXR1cm58fGMucmV0dXJuPT09YilyZXR1cm47Yz1jLnJldHVybn1jLnNpYmxpbmcucmV0dXJuPWMucmV0dXJuO2M9Yy5zaWJsaW5nfX07Qmo9ZnVuY3Rpb24oKXt9OwpDaj1mdW5jdGlvbihhLGIsYyxkKXt2YXIgZT1hLm1lbW9pemVkUHJvcHM7aWYoZSE9PWQpe2E9Yi5zdGF0ZU5vZGU7SGgoRWguY3VycmVudCk7dmFyIGY9bnVsbDtzd2l0Y2goYyl7Y2FzZSAiaW5wdXQiOmU9WWEoYSxlKTtkPVlhKGEsZCk7Zj1bXTticmVhaztjYXNlICJzZWxlY3QiOmU9QSh7fSxlLHt2YWx1ZTp2b2lkIDB9KTtkPUEoe30sZCx7dmFsdWU6dm9pZCAwfSk7Zj1bXTticmVhaztjYXNlICJ0ZXh0YXJlYSI6ZT1nYihhLGUpO2Q9Z2IoYSxkKTtmPVtdO2JyZWFrO2RlZmF1bHQ6ImZ1bmN0aW9uIiE9PXR5cGVvZiBlLm9uQ2xpY2smJiJmdW5jdGlvbiI9PT10eXBlb2YgZC5vbkNsaWNrJiYoYS5vbmNsaWNrPUJmKX11YihjLGQpO3ZhciBnO2M9bnVsbDtmb3IobCBpbiBlKWlmKCFkLmhhc093blByb3BlcnR5KGwpJiZlLmhhc093blByb3BlcnR5KGwpJiZudWxsIT1lW2xdKWlmKCJzdHlsZSI9PT1sKXt2YXIgaD1lW2xdO2ZvcihnIGluIGgpaC5oYXNPd25Qcm9wZXJ0eShnKSYmCihjfHwoYz17fSksY1tnXT0iIil9ZWxzZSJkYW5nZXJvdXNseVNldElubmVySFRNTCIhPT1sJiYiY2hpbGRyZW4iIT09bCYmInN1cHByZXNzQ29udGVudEVkaXRhYmxlV2FybmluZyIhPT1sJiYic3VwcHJlc3NIeWRyYXRpb25XYXJuaW5nIiE9PWwmJiJhdXRvRm9jdXMiIT09bCYmKGVhLmhhc093blByb3BlcnR5KGwpP2Z8fChmPVtdKTooZj1mfHxbXSkucHVzaChsLG51bGwpKTtmb3IobCBpbiBkKXt2YXIgaz1kW2xdO2g9bnVsbCE9ZT9lW2xdOnZvaWQgMDtpZihkLmhhc093blByb3BlcnR5KGwpJiZrIT09aCYmKG51bGwhPWt8fG51bGwhPWgpKWlmKCJzdHlsZSI9PT1sKWlmKGgpe2ZvcihnIGluIGgpIWguaGFzT3duUHJvcGVydHkoZyl8fGsmJmsuaGFzT3duUHJvcGVydHkoZyl8fChjfHwoYz17fSksY1tnXT0iIik7Zm9yKGcgaW4gaylrLmhhc093blByb3BlcnR5KGcpJiZoW2ddIT09a1tnXSYmKGN8fChjPXt9KSxjW2ddPWtbZ10pfWVsc2UgY3x8KGZ8fChmPVtdKSxmLnB1c2gobCwKYykpLGM9aztlbHNlImRhbmdlcm91c2x5U2V0SW5uZXJIVE1MIj09PWw/KGs9az9rLl9faHRtbDp2b2lkIDAsaD1oP2guX19odG1sOnZvaWQgMCxudWxsIT1rJiZoIT09ayYmKGY9Znx8W10pLnB1c2gobCxrKSk6ImNoaWxkcmVuIj09PWw/InN0cmluZyIhPT10eXBlb2YgayYmIm51bWJlciIhPT10eXBlb2Yga3x8KGY9Znx8W10pLnB1c2gobCwiIitrKToic3VwcHJlc3NDb250ZW50RWRpdGFibGVXYXJuaW5nIiE9PWwmJiJzdXBwcmVzc0h5ZHJhdGlvbldhcm5pbmciIT09bCYmKGVhLmhhc093blByb3BlcnR5KGwpPyhudWxsIT1rJiYib25TY3JvbGwiPT09bCYmRCgic2Nyb2xsIixhKSxmfHxoPT09a3x8KGY9W10pKTooZj1mfHxbXSkucHVzaChsLGspKX1jJiYoZj1mfHxbXSkucHVzaCgic3R5bGUiLGMpO3ZhciBsPWY7aWYoYi51cGRhdGVRdWV1ZT1sKWIuZmxhZ3N8PTR9fTtEaj1mdW5jdGlvbihhLGIsYyxkKXtjIT09ZCYmKGIuZmxhZ3N8PTQpfTsKZnVuY3Rpb24gRWooYSxiKXtpZighSSlzd2l0Y2goYS50YWlsTW9kZSl7Y2FzZSAiaGlkZGVuIjpiPWEudGFpbDtmb3IodmFyIGM9bnVsbDtudWxsIT09YjspbnVsbCE9PWIuYWx0ZXJuYXRlJiYoYz1iKSxiPWIuc2libGluZztudWxsPT09Yz9hLnRhaWw9bnVsbDpjLnNpYmxpbmc9bnVsbDticmVhaztjYXNlICJjb2xsYXBzZWQiOmM9YS50YWlsO2Zvcih2YXIgZD1udWxsO251bGwhPT1jOyludWxsIT09Yy5hbHRlcm5hdGUmJihkPWMpLGM9Yy5zaWJsaW5nO251bGw9PT1kP2J8fG51bGw9PT1hLnRhaWw/YS50YWlsPW51bGw6YS50YWlsLnNpYmxpbmc9bnVsbDpkLnNpYmxpbmc9bnVsbH19CmZ1bmN0aW9uIFMoYSl7dmFyIGI9bnVsbCE9PWEuYWx0ZXJuYXRlJiZhLmFsdGVybmF0ZS5jaGlsZD09PWEuY2hpbGQsYz0wLGQ9MDtpZihiKWZvcih2YXIgZT1hLmNoaWxkO251bGwhPT1lOyljfD1lLmxhbmVzfGUuY2hpbGRMYW5lcyxkfD1lLnN1YnRyZWVGbGFncyYxNDY4MDA2NCxkfD1lLmZsYWdzJjE0NjgwMDY0LGUucmV0dXJuPWEsZT1lLnNpYmxpbmc7ZWxzZSBmb3IoZT1hLmNoaWxkO251bGwhPT1lOyljfD1lLmxhbmVzfGUuY2hpbGRMYW5lcyxkfD1lLnN1YnRyZWVGbGFncyxkfD1lLmZsYWdzLGUucmV0dXJuPWEsZT1lLnNpYmxpbmc7YS5zdWJ0cmVlRmxhZ3N8PWQ7YS5jaGlsZExhbmVzPWM7cmV0dXJuIGJ9CmZ1bmN0aW9uIEZqKGEsYixjKXt2YXIgZD1iLnBlbmRpbmdQcm9wczt3ZyhiKTtzd2l0Y2goYi50YWcpe2Nhc2UgMjpjYXNlIDE2OmNhc2UgMTU6Y2FzZSAwOmNhc2UgMTE6Y2FzZSA3OmNhc2UgODpjYXNlIDEyOmNhc2UgOTpjYXNlIDE0OnJldHVybiBTKGIpLG51bGw7Y2FzZSAxOnJldHVybiBaZihiLnR5cGUpJiYkZigpLFMoYiksbnVsbDtjYXNlIDM6ZD1iLnN0YXRlTm9kZTtKaCgpO0UoV2YpO0UoSCk7T2goKTtkLnBlbmRpbmdDb250ZXh0JiYoZC5jb250ZXh0PWQucGVuZGluZ0NvbnRleHQsZC5wZW5kaW5nQ29udGV4dD1udWxsKTtpZihudWxsPT09YXx8bnVsbD09PWEuY2hpbGQpR2coYik/Yi5mbGFnc3w9NDpudWxsPT09YXx8YS5tZW1vaXplZFN0YXRlLmlzRGVoeWRyYXRlZCYmMD09PShiLmZsYWdzJjI1Nil8fChiLmZsYWdzfD0xMDI0LG51bGwhPT16ZyYmKEdqKHpnKSx6Zz1udWxsKSk7QmooYSxiKTtTKGIpO3JldHVybiBudWxsO2Nhc2UgNTpMaChiKTt2YXIgZT1IaChHaC5jdXJyZW50KTsKYz1iLnR5cGU7aWYobnVsbCE9PWEmJm51bGwhPWIuc3RhdGVOb2RlKUNqKGEsYixjLGQsZSksYS5yZWYhPT1iLnJlZiYmKGIuZmxhZ3N8PTUxMixiLmZsYWdzfD0yMDk3MTUyKTtlbHNle2lmKCFkKXtpZihudWxsPT09Yi5zdGF0ZU5vZGUpdGhyb3cgRXJyb3IocCgxNjYpKTtTKGIpO3JldHVybiBudWxsfWE9SGgoRWguY3VycmVudCk7aWYoR2coYikpe2Q9Yi5zdGF0ZU5vZGU7Yz1iLnR5cGU7dmFyIGY9Yi5tZW1vaXplZFByb3BzO2RbT2ZdPWI7ZFtQZl09ZjthPTAhPT0oYi5tb2RlJjEpO3N3aXRjaChjKXtjYXNlICJkaWFsb2ciOkQoImNhbmNlbCIsZCk7RCgiY2xvc2UiLGQpO2JyZWFrO2Nhc2UgImlmcmFtZSI6Y2FzZSAib2JqZWN0IjpjYXNlICJlbWJlZCI6RCgibG9hZCIsZCk7YnJlYWs7Y2FzZSAidmlkZW8iOmNhc2UgImF1ZGlvIjpmb3IoZT0wO2U8bGYubGVuZ3RoO2UrKylEKGxmW2VdLGQpO2JyZWFrO2Nhc2UgInNvdXJjZSI6RCgiZXJyb3IiLGQpO2JyZWFrO2Nhc2UgImltZyI6Y2FzZSAiaW1hZ2UiOmNhc2UgImxpbmsiOkQoImVycm9yIiwKZCk7RCgibG9hZCIsZCk7YnJlYWs7Y2FzZSAiZGV0YWlscyI6RCgidG9nZ2xlIixkKTticmVhaztjYXNlICJpbnB1dCI6WmEoZCxmKTtEKCJpbnZhbGlkIixkKTticmVhaztjYXNlICJzZWxlY3QiOmQuX3dyYXBwZXJTdGF0ZT17d2FzTXVsdGlwbGU6ISFmLm11bHRpcGxlfTtEKCJpbnZhbGlkIixkKTticmVhaztjYXNlICJ0ZXh0YXJlYSI6aGIoZCxmKSxEKCJpbnZhbGlkIixkKX11YihjLGYpO2U9bnVsbDtmb3IodmFyIGcgaW4gZilpZihmLmhhc093blByb3BlcnR5KGcpKXt2YXIgaD1mW2ddOyJjaGlsZHJlbiI9PT1nPyJzdHJpbmciPT09dHlwZW9mIGg/ZC50ZXh0Q29udGVudCE9PWgmJighMCE9PWYuc3VwcHJlc3NIeWRyYXRpb25XYXJuaW5nJiZBZihkLnRleHRDb250ZW50LGgsYSksZT1bImNoaWxkcmVuIixoXSk6Im51bWJlciI9PT10eXBlb2YgaCYmZC50ZXh0Q29udGVudCE9PSIiK2gmJighMCE9PWYuc3VwcHJlc3NIeWRyYXRpb25XYXJuaW5nJiZBZihkLnRleHRDb250ZW50LApoLGEpLGU9WyJjaGlsZHJlbiIsIiIraF0pOmVhLmhhc093blByb3BlcnR5KGcpJiZudWxsIT1oJiYib25TY3JvbGwiPT09ZyYmRCgic2Nyb2xsIixkKX1zd2l0Y2goYyl7Y2FzZSAiaW5wdXQiOlZhKGQpO2RiKGQsZiwhMCk7YnJlYWs7Y2FzZSAidGV4dGFyZWEiOlZhKGQpO2piKGQpO2JyZWFrO2Nhc2UgInNlbGVjdCI6Y2FzZSAib3B0aW9uIjpicmVhaztkZWZhdWx0OiJmdW5jdGlvbiI9PT10eXBlb2YgZi5vbkNsaWNrJiYoZC5vbmNsaWNrPUJmKX1kPWU7Yi51cGRhdGVRdWV1ZT1kO251bGwhPT1kJiYoYi5mbGFnc3w9NCl9ZWxzZXtnPTk9PT1lLm5vZGVUeXBlP2U6ZS5vd25lckRvY3VtZW50OyJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj09PWEmJihhPWtiKGMpKTsiaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCI9PT1hPyJzY3JpcHQiPT09Yz8oYT1nLmNyZWF0ZUVsZW1lbnQoImRpdiIpLGEuaW5uZXJIVE1MPSI8c2NyaXB0Plx4M2Mvc2NyaXB0PiIsYT1hLnJlbW92ZUNoaWxkKGEuZmlyc3RDaGlsZCkpOgoic3RyaW5nIj09PXR5cGVvZiBkLmlzP2E9Zy5jcmVhdGVFbGVtZW50KGMse2lzOmQuaXN9KTooYT1nLmNyZWF0ZUVsZW1lbnQoYyksInNlbGVjdCI9PT1jJiYoZz1hLGQubXVsdGlwbGU/Zy5tdWx0aXBsZT0hMDpkLnNpemUmJihnLnNpemU9ZC5zaXplKSkpOmE9Zy5jcmVhdGVFbGVtZW50TlMoYSxjKTthW09mXT1iO2FbUGZdPWQ7QWooYSxiLCExLCExKTtiLnN0YXRlTm9kZT1hO2E6e2c9dmIoYyxkKTtzd2l0Y2goYyl7Y2FzZSAiZGlhbG9nIjpEKCJjYW5jZWwiLGEpO0QoImNsb3NlIixhKTtlPWQ7YnJlYWs7Y2FzZSAiaWZyYW1lIjpjYXNlICJvYmplY3QiOmNhc2UgImVtYmVkIjpEKCJsb2FkIixhKTtlPWQ7YnJlYWs7Y2FzZSAidmlkZW8iOmNhc2UgImF1ZGlvIjpmb3IoZT0wO2U8bGYubGVuZ3RoO2UrKylEKGxmW2VdLGEpO2U9ZDticmVhaztjYXNlICJzb3VyY2UiOkQoImVycm9yIixhKTtlPWQ7YnJlYWs7Y2FzZSAiaW1nIjpjYXNlICJpbWFnZSI6Y2FzZSAibGluayI6RCgiZXJyb3IiLAphKTtEKCJsb2FkIixhKTtlPWQ7YnJlYWs7Y2FzZSAiZGV0YWlscyI6RCgidG9nZ2xlIixhKTtlPWQ7YnJlYWs7Y2FzZSAiaW5wdXQiOlphKGEsZCk7ZT1ZYShhLGQpO0QoImludmFsaWQiLGEpO2JyZWFrO2Nhc2UgIm9wdGlvbiI6ZT1kO2JyZWFrO2Nhc2UgInNlbGVjdCI6YS5fd3JhcHBlclN0YXRlPXt3YXNNdWx0aXBsZTohIWQubXVsdGlwbGV9O2U9QSh7fSxkLHt2YWx1ZTp2b2lkIDB9KTtEKCJpbnZhbGlkIixhKTticmVhaztjYXNlICJ0ZXh0YXJlYSI6aGIoYSxkKTtlPWdiKGEsZCk7RCgiaW52YWxpZCIsYSk7YnJlYWs7ZGVmYXVsdDplPWR9dWIoYyxlKTtoPWU7Zm9yKGYgaW4gaClpZihoLmhhc093blByb3BlcnR5KGYpKXt2YXIgaz1oW2ZdOyJzdHlsZSI9PT1mP3NiKGEsayk6ImRhbmdlcm91c2x5U2V0SW5uZXJIVE1MIj09PWY/KGs9az9rLl9faHRtbDp2b2lkIDAsbnVsbCE9ayYmbmIoYSxrKSk6ImNoaWxkcmVuIj09PWY/InN0cmluZyI9PT10eXBlb2Ygaz8oInRleHRhcmVhIiE9PQpjfHwiIiE9PWspJiZvYihhLGspOiJudW1iZXIiPT09dHlwZW9mIGsmJm9iKGEsIiIrayk6InN1cHByZXNzQ29udGVudEVkaXRhYmxlV2FybmluZyIhPT1mJiYic3VwcHJlc3NIeWRyYXRpb25XYXJuaW5nIiE9PWYmJiJhdXRvRm9jdXMiIT09ZiYmKGVhLmhhc093blByb3BlcnR5KGYpP251bGwhPWsmJiJvblNjcm9sbCI9PT1mJiZEKCJzY3JvbGwiLGEpOm51bGwhPWsmJnRhKGEsZixrLGcpKX1zd2l0Y2goYyl7Y2FzZSAiaW5wdXQiOlZhKGEpO2RiKGEsZCwhMSk7YnJlYWs7Y2FzZSAidGV4dGFyZWEiOlZhKGEpO2piKGEpO2JyZWFrO2Nhc2UgIm9wdGlvbiI6bnVsbCE9ZC52YWx1ZSYmYS5zZXRBdHRyaWJ1dGUoInZhbHVlIiwiIitTYShkLnZhbHVlKSk7YnJlYWs7Y2FzZSAic2VsZWN0IjphLm11bHRpcGxlPSEhZC5tdWx0aXBsZTtmPWQudmFsdWU7bnVsbCE9Zj9mYihhLCEhZC5tdWx0aXBsZSxmLCExKTpudWxsIT1kLmRlZmF1bHRWYWx1ZSYmZmIoYSwhIWQubXVsdGlwbGUsZC5kZWZhdWx0VmFsdWUsCiEwKTticmVhaztkZWZhdWx0OiJmdW5jdGlvbiI9PT10eXBlb2YgZS5vbkNsaWNrJiYoYS5vbmNsaWNrPUJmKX1zd2l0Y2goYyl7Y2FzZSAiYnV0dG9uIjpjYXNlICJpbnB1dCI6Y2FzZSAic2VsZWN0IjpjYXNlICJ0ZXh0YXJlYSI6ZD0hIWQuYXV0b0ZvY3VzO2JyZWFrIGE7Y2FzZSAiaW1nIjpkPSEwO2JyZWFrIGE7ZGVmYXVsdDpkPSExfX1kJiYoYi5mbGFnc3w9NCl9bnVsbCE9PWIucmVmJiYoYi5mbGFnc3w9NTEyLGIuZmxhZ3N8PTIwOTcxNTIpfVMoYik7cmV0dXJuIG51bGw7Y2FzZSA2OmlmKGEmJm51bGwhPWIuc3RhdGVOb2RlKURqKGEsYixhLm1lbW9pemVkUHJvcHMsZCk7ZWxzZXtpZigic3RyaW5nIiE9PXR5cGVvZiBkJiZudWxsPT09Yi5zdGF0ZU5vZGUpdGhyb3cgRXJyb3IocCgxNjYpKTtjPUhoKEdoLmN1cnJlbnQpO0hoKEVoLmN1cnJlbnQpO2lmKEdnKGIpKXtkPWIuc3RhdGVOb2RlO2M9Yi5tZW1vaXplZFByb3BzO2RbT2ZdPWI7aWYoZj1kLm5vZGVWYWx1ZSE9PWMpaWYoYT0KeGcsbnVsbCE9PWEpc3dpdGNoKGEudGFnKXtjYXNlIDM6QWYoZC5ub2RlVmFsdWUsYywwIT09KGEubW9kZSYxKSk7YnJlYWs7Y2FzZSA1OiEwIT09YS5tZW1vaXplZFByb3BzLnN1cHByZXNzSHlkcmF0aW9uV2FybmluZyYmQWYoZC5ub2RlVmFsdWUsYywwIT09KGEubW9kZSYxKSl9ZiYmKGIuZmxhZ3N8PTQpfWVsc2UgZD0oOT09PWMubm9kZVR5cGU/YzpjLm93bmVyRG9jdW1lbnQpLmNyZWF0ZVRleHROb2RlKGQpLGRbT2ZdPWIsYi5zdGF0ZU5vZGU9ZH1TKGIpO3JldHVybiBudWxsO2Nhc2UgMTM6RShNKTtkPWIubWVtb2l6ZWRTdGF0ZTtpZihudWxsPT09YXx8bnVsbCE9PWEubWVtb2l6ZWRTdGF0ZSYmbnVsbCE9PWEubWVtb2l6ZWRTdGF0ZS5kZWh5ZHJhdGVkKXtpZihJJiZudWxsIT09eWcmJjAhPT0oYi5tb2RlJjEpJiYwPT09KGIuZmxhZ3MmMTI4KSlIZygpLElnKCksYi5mbGFnc3w9OTg1NjAsZj0hMTtlbHNlIGlmKGY9R2coYiksbnVsbCE9PWQmJm51bGwhPT1kLmRlaHlkcmF0ZWQpe2lmKG51bGw9PT0KYSl7aWYoIWYpdGhyb3cgRXJyb3IocCgzMTgpKTtmPWIubWVtb2l6ZWRTdGF0ZTtmPW51bGwhPT1mP2YuZGVoeWRyYXRlZDpudWxsO2lmKCFmKXRocm93IEVycm9yKHAoMzE3KSk7ZltPZl09Yn1lbHNlIElnKCksMD09PShiLmZsYWdzJjEyOCkmJihiLm1lbW9pemVkU3RhdGU9bnVsbCksYi5mbGFnc3w9NDtTKGIpO2Y9ITF9ZWxzZSBudWxsIT09emcmJihHaih6Zyksemc9bnVsbCksZj0hMDtpZighZilyZXR1cm4gYi5mbGFncyY2NTUzNj9iOm51bGx9aWYoMCE9PShiLmZsYWdzJjEyOCkpcmV0dXJuIGIubGFuZXM9YyxiO2Q9bnVsbCE9PWQ7ZCE9PShudWxsIT09YSYmbnVsbCE9PWEubWVtb2l6ZWRTdGF0ZSkmJmQmJihiLmNoaWxkLmZsYWdzfD04MTkyLDAhPT0oYi5tb2RlJjEpJiYobnVsbD09PWF8fDAhPT0oTS5jdXJyZW50JjEpPzA9PT1UJiYoVD0zKTp1aigpKSk7bnVsbCE9PWIudXBkYXRlUXVldWUmJihiLmZsYWdzfD00KTtTKGIpO3JldHVybiBudWxsO2Nhc2UgNDpyZXR1cm4gSmgoKSwKQmooYSxiKSxudWxsPT09YSYmc2YoYi5zdGF0ZU5vZGUuY29udGFpbmVySW5mbyksUyhiKSxudWxsO2Nhc2UgMTA6cmV0dXJuIFJnKGIudHlwZS5fY29udGV4dCksUyhiKSxudWxsO2Nhc2UgMTc6cmV0dXJuIFpmKGIudHlwZSkmJiRmKCksUyhiKSxudWxsO2Nhc2UgMTk6RShNKTtmPWIubWVtb2l6ZWRTdGF0ZTtpZihudWxsPT09ZilyZXR1cm4gUyhiKSxudWxsO2Q9MCE9PShiLmZsYWdzJjEyOCk7Zz1mLnJlbmRlcmluZztpZihudWxsPT09ZylpZihkKUVqKGYsITEpO2Vsc2V7aWYoMCE9PVR8fG51bGwhPT1hJiYwIT09KGEuZmxhZ3MmMTI4KSlmb3IoYT1iLmNoaWxkO251bGwhPT1hOyl7Zz1NaChhKTtpZihudWxsIT09Zyl7Yi5mbGFnc3w9MTI4O0VqKGYsITEpO2Q9Zy51cGRhdGVRdWV1ZTtudWxsIT09ZCYmKGIudXBkYXRlUXVldWU9ZCxiLmZsYWdzfD00KTtiLnN1YnRyZWVGbGFncz0wO2Q9Yztmb3IoYz1iLmNoaWxkO251bGwhPT1jOylmPWMsYT1kLGYuZmxhZ3MmPTE0NjgwMDY2LApnPWYuYWx0ZXJuYXRlLG51bGw9PT1nPyhmLmNoaWxkTGFuZXM9MCxmLmxhbmVzPWEsZi5jaGlsZD1udWxsLGYuc3VidHJlZUZsYWdzPTAsZi5tZW1vaXplZFByb3BzPW51bGwsZi5tZW1vaXplZFN0YXRlPW51bGwsZi51cGRhdGVRdWV1ZT1udWxsLGYuZGVwZW5kZW5jaWVzPW51bGwsZi5zdGF0ZU5vZGU9bnVsbCk6KGYuY2hpbGRMYW5lcz1nLmNoaWxkTGFuZXMsZi5sYW5lcz1nLmxhbmVzLGYuY2hpbGQ9Zy5jaGlsZCxmLnN1YnRyZWVGbGFncz0wLGYuZGVsZXRpb25zPW51bGwsZi5tZW1vaXplZFByb3BzPWcubWVtb2l6ZWRQcm9wcyxmLm1lbW9pemVkU3RhdGU9Zy5tZW1vaXplZFN0YXRlLGYudXBkYXRlUXVldWU9Zy51cGRhdGVRdWV1ZSxmLnR5cGU9Zy50eXBlLGE9Zy5kZXBlbmRlbmNpZXMsZi5kZXBlbmRlbmNpZXM9bnVsbD09PWE/bnVsbDp7bGFuZXM6YS5sYW5lcyxmaXJzdENvbnRleHQ6YS5maXJzdENvbnRleHR9KSxjPWMuc2libGluZztHKE0sTS5jdXJyZW50JjF8Mik7cmV0dXJuIGIuY2hpbGR9YT0KYS5zaWJsaW5nfW51bGwhPT1mLnRhaWwmJkIoKT5IaiYmKGIuZmxhZ3N8PTEyOCxkPSEwLEVqKGYsITEpLGIubGFuZXM9NDE5NDMwNCl9ZWxzZXtpZighZClpZihhPU1oKGcpLG51bGwhPT1hKXtpZihiLmZsYWdzfD0xMjgsZD0hMCxjPWEudXBkYXRlUXVldWUsbnVsbCE9PWMmJihiLnVwZGF0ZVF1ZXVlPWMsYi5mbGFnc3w9NCksRWooZiwhMCksbnVsbD09PWYudGFpbCYmImhpZGRlbiI9PT1mLnRhaWxNb2RlJiYhZy5hbHRlcm5hdGUmJiFJKXJldHVybiBTKGIpLG51bGx9ZWxzZSAyKkIoKS1mLnJlbmRlcmluZ1N0YXJ0VGltZT5IaiYmMTA3Mzc0MTgyNCE9PWMmJihiLmZsYWdzfD0xMjgsZD0hMCxFaihmLCExKSxiLmxhbmVzPTQxOTQzMDQpO2YuaXNCYWNrd2FyZHM/KGcuc2libGluZz1iLmNoaWxkLGIuY2hpbGQ9Zyk6KGM9Zi5sYXN0LG51bGwhPT1jP2Muc2libGluZz1nOmIuY2hpbGQ9ZyxmLmxhc3Q9Zyl9aWYobnVsbCE9PWYudGFpbClyZXR1cm4gYj1mLnRhaWwsZi5yZW5kZXJpbmc9CmIsZi50YWlsPWIuc2libGluZyxmLnJlbmRlcmluZ1N0YXJ0VGltZT1CKCksYi5zaWJsaW5nPW51bGwsYz1NLmN1cnJlbnQsRyhNLGQ/YyYxfDI6YyYxKSxiO1MoYik7cmV0dXJuIG51bGw7Y2FzZSAyMjpjYXNlIDIzOnJldHVybiBJaigpLGQ9bnVsbCE9PWIubWVtb2l6ZWRTdGF0ZSxudWxsIT09YSYmbnVsbCE9PWEubWVtb2l6ZWRTdGF0ZSE9PWQmJihiLmZsYWdzfD04MTkyKSxkJiYwIT09KGIubW9kZSYxKT8wIT09KGdqJjEwNzM3NDE4MjQpJiYoUyhiKSxiLnN1YnRyZWVGbGFncyY2JiYoYi5mbGFnc3w9ODE5MikpOlMoYiksbnVsbDtjYXNlIDI0OnJldHVybiBudWxsO2Nhc2UgMjU6cmV0dXJuIG51bGx9dGhyb3cgRXJyb3IocCgxNTYsYi50YWcpKTt9CmZ1bmN0aW9uIEpqKGEsYil7d2coYik7c3dpdGNoKGIudGFnKXtjYXNlIDE6cmV0dXJuIFpmKGIudHlwZSkmJiRmKCksYT1iLmZsYWdzLGEmNjU1MzY/KGIuZmxhZ3M9YSYtNjU1Mzd8MTI4LGIpOm51bGw7Y2FzZSAzOnJldHVybiBKaCgpLEUoV2YpLEUoSCksT2goKSxhPWIuZmxhZ3MsMCE9PShhJjY1NTM2KSYmMD09PShhJjEyOCk/KGIuZmxhZ3M9YSYtNjU1Mzd8MTI4LGIpOm51bGw7Y2FzZSA1OnJldHVybiBMaChiKSxudWxsO2Nhc2UgMTM6RShNKTthPWIubWVtb2l6ZWRTdGF0ZTtpZihudWxsIT09YSYmbnVsbCE9PWEuZGVoeWRyYXRlZCl7aWYobnVsbD09PWIuYWx0ZXJuYXRlKXRocm93IEVycm9yKHAoMzQwKSk7SWcoKX1hPWIuZmxhZ3M7cmV0dXJuIGEmNjU1MzY/KGIuZmxhZ3M9YSYtNjU1Mzd8MTI4LGIpOm51bGw7Y2FzZSAxOTpyZXR1cm4gRShNKSxudWxsO2Nhc2UgNDpyZXR1cm4gSmgoKSxudWxsO2Nhc2UgMTA6cmV0dXJuIFJnKGIudHlwZS5fY29udGV4dCksbnVsbDtjYXNlIDIyOmNhc2UgMjM6cmV0dXJuIElqKCksCm51bGw7Y2FzZSAyNDpyZXR1cm4gbnVsbDtkZWZhdWx0OnJldHVybiBudWxsfX12YXIgS2o9ITEsVT0hMSxMaj0iZnVuY3Rpb24iPT09dHlwZW9mIFdlYWtTZXQ/V2Vha1NldDpTZXQsVj1udWxsO2Z1bmN0aW9uIE1qKGEsYil7dmFyIGM9YS5yZWY7aWYobnVsbCE9PWMpaWYoImZ1bmN0aW9uIj09PXR5cGVvZiBjKXRyeXtjKG51bGwpfWNhdGNoKGQpe1coYSxiLGQpfWVsc2UgYy5jdXJyZW50PW51bGx9ZnVuY3Rpb24gTmooYSxiLGMpe3RyeXtjKCl9Y2F0Y2goZCl7VyhhLGIsZCl9fXZhciBPaj0hMTsKZnVuY3Rpb24gUGooYSxiKXtDZj1kZDthPU1lKCk7aWYoTmUoYSkpe2lmKCJzZWxlY3Rpb25TdGFydCJpbiBhKXZhciBjPXtzdGFydDphLnNlbGVjdGlvblN0YXJ0LGVuZDphLnNlbGVjdGlvbkVuZH07ZWxzZSBhOntjPShjPWEub3duZXJEb2N1bWVudCkmJmMuZGVmYXVsdFZpZXd8fHdpbmRvdzt2YXIgZD1jLmdldFNlbGVjdGlvbiYmYy5nZXRTZWxlY3Rpb24oKTtpZihkJiYwIT09ZC5yYW5nZUNvdW50KXtjPWQuYW5jaG9yTm9kZTt2YXIgZT1kLmFuY2hvck9mZnNldCxmPWQuZm9jdXNOb2RlO2Q9ZC5mb2N1c09mZnNldDt0cnl7Yy5ub2RlVHlwZSxmLm5vZGVUeXBlfWNhdGNoKEYpe2M9bnVsbDticmVhayBhfXZhciBnPTAsaD0tMSxrPS0xLGw9MCxtPTAscT1hLHI9bnVsbDtiOmZvcig7Oyl7Zm9yKHZhciB5Ozspe3EhPT1jfHwwIT09ZSYmMyE9PXEubm9kZVR5cGV8fChoPWcrZSk7cSE9PWZ8fDAhPT1kJiYzIT09cS5ub2RlVHlwZXx8KGs9ZytkKTszPT09cS5ub2RlVHlwZSYmKGcrPQpxLm5vZGVWYWx1ZS5sZW5ndGgpO2lmKG51bGw9PT0oeT1xLmZpcnN0Q2hpbGQpKWJyZWFrO3I9cTtxPXl9Zm9yKDs7KXtpZihxPT09YSlicmVhayBiO3I9PT1jJiYrK2w9PT1lJiYoaD1nKTtyPT09ZiYmKyttPT09ZCYmKGs9Zyk7aWYobnVsbCE9PSh5PXEubmV4dFNpYmxpbmcpKWJyZWFrO3E9cjtyPXEucGFyZW50Tm9kZX1xPXl9Yz0tMT09PWh8fC0xPT09az9udWxsOntzdGFydDpoLGVuZDprfX1lbHNlIGM9bnVsbH1jPWN8fHtzdGFydDowLGVuZDowfX1lbHNlIGM9bnVsbDtEZj17Zm9jdXNlZEVsZW06YSxzZWxlY3Rpb25SYW5nZTpjfTtkZD0hMTtmb3IoVj1iO251bGwhPT1WOylpZihiPVYsYT1iLmNoaWxkLDAhPT0oYi5zdWJ0cmVlRmxhZ3MmMTAyOCkmJm51bGwhPT1hKWEucmV0dXJuPWIsVj1hO2Vsc2UgZm9yKDtudWxsIT09Vjspe2I9Vjt0cnl7dmFyIG49Yi5hbHRlcm5hdGU7aWYoMCE9PShiLmZsYWdzJjEwMjQpKXN3aXRjaChiLnRhZyl7Y2FzZSAwOmNhc2UgMTE6Y2FzZSAxNTpicmVhazsKY2FzZSAxOmlmKG51bGwhPT1uKXt2YXIgdD1uLm1lbW9pemVkUHJvcHMsSj1uLm1lbW9pemVkU3RhdGUseD1iLnN0YXRlTm9kZSx3PXguZ2V0U25hcHNob3RCZWZvcmVVcGRhdGUoYi5lbGVtZW50VHlwZT09PWIudHlwZT90OkxnKGIudHlwZSx0KSxKKTt4Ll9fcmVhY3RJbnRlcm5hbFNuYXBzaG90QmVmb3JlVXBkYXRlPXd9YnJlYWs7Y2FzZSAzOnZhciB1PWIuc3RhdGVOb2RlLmNvbnRhaW5lckluZm87MT09PXUubm9kZVR5cGU/dS50ZXh0Q29udGVudD0iIjo5PT09dS5ub2RlVHlwZSYmdS5kb2N1bWVudEVsZW1lbnQmJnUucmVtb3ZlQ2hpbGQodS5kb2N1bWVudEVsZW1lbnQpO2JyZWFrO2Nhc2UgNTpjYXNlIDY6Y2FzZSA0OmNhc2UgMTc6YnJlYWs7ZGVmYXVsdDp0aHJvdyBFcnJvcihwKDE2MykpO319Y2F0Y2goRil7VyhiLGIucmV0dXJuLEYpfWE9Yi5zaWJsaW5nO2lmKG51bGwhPT1hKXthLnJldHVybj1iLnJldHVybjtWPWE7YnJlYWt9Vj1iLnJldHVybn1uPU9qO09qPSExO3JldHVybiBufQpmdW5jdGlvbiBRaihhLGIsYyl7dmFyIGQ9Yi51cGRhdGVRdWV1ZTtkPW51bGwhPT1kP2QubGFzdEVmZmVjdDpudWxsO2lmKG51bGwhPT1kKXt2YXIgZT1kPWQubmV4dDtkb3tpZigoZS50YWcmYSk9PT1hKXt2YXIgZj1lLmRlc3Ryb3k7ZS5kZXN0cm95PXZvaWQgMDt2b2lkIDAhPT1mJiZOaihiLGMsZil9ZT1lLm5leHR9d2hpbGUoZSE9PWQpfX1mdW5jdGlvbiBSaihhLGIpe2I9Yi51cGRhdGVRdWV1ZTtiPW51bGwhPT1iP2IubGFzdEVmZmVjdDpudWxsO2lmKG51bGwhPT1iKXt2YXIgYz1iPWIubmV4dDtkb3tpZigoYy50YWcmYSk9PT1hKXt2YXIgZD1jLmNyZWF0ZTtjLmRlc3Ryb3k9ZCgpfWM9Yy5uZXh0fXdoaWxlKGMhPT1iKX19ZnVuY3Rpb24gU2ooYSl7dmFyIGI9YS5yZWY7aWYobnVsbCE9PWIpe3ZhciBjPWEuc3RhdGVOb2RlO3N3aXRjaChhLnRhZyl7Y2FzZSA1OmE9YzticmVhaztkZWZhdWx0OmE9Y30iZnVuY3Rpb24iPT09dHlwZW9mIGI/YihhKTpiLmN1cnJlbnQ9YX19CmZ1bmN0aW9uIFRqKGEpe3ZhciBiPWEuYWx0ZXJuYXRlO251bGwhPT1iJiYoYS5hbHRlcm5hdGU9bnVsbCxUaihiKSk7YS5jaGlsZD1udWxsO2EuZGVsZXRpb25zPW51bGw7YS5zaWJsaW5nPW51bGw7NT09PWEudGFnJiYoYj1hLnN0YXRlTm9kZSxudWxsIT09YiYmKGRlbGV0ZSBiW09mXSxkZWxldGUgYltQZl0sZGVsZXRlIGJbb2ZdLGRlbGV0ZSBiW1FmXSxkZWxldGUgYltSZl0pKTthLnN0YXRlTm9kZT1udWxsO2EucmV0dXJuPW51bGw7YS5kZXBlbmRlbmNpZXM9bnVsbDthLm1lbW9pemVkUHJvcHM9bnVsbDthLm1lbW9pemVkU3RhdGU9bnVsbDthLnBlbmRpbmdQcm9wcz1udWxsO2Euc3RhdGVOb2RlPW51bGw7YS51cGRhdGVRdWV1ZT1udWxsfWZ1bmN0aW9uIFVqKGEpe3JldHVybiA1PT09YS50YWd8fDM9PT1hLnRhZ3x8ND09PWEudGFnfQpmdW5jdGlvbiBWaihhKXthOmZvcig7Oyl7Zm9yKDtudWxsPT09YS5zaWJsaW5nOyl7aWYobnVsbD09PWEucmV0dXJufHxVaihhLnJldHVybikpcmV0dXJuIG51bGw7YT1hLnJldHVybn1hLnNpYmxpbmcucmV0dXJuPWEucmV0dXJuO2ZvcihhPWEuc2libGluZzs1IT09YS50YWcmJjYhPT1hLnRhZyYmMTghPT1hLnRhZzspe2lmKGEuZmxhZ3MmMiljb250aW51ZSBhO2lmKG51bGw9PT1hLmNoaWxkfHw0PT09YS50YWcpY29udGludWUgYTtlbHNlIGEuY2hpbGQucmV0dXJuPWEsYT1hLmNoaWxkfWlmKCEoYS5mbGFncyYyKSlyZXR1cm4gYS5zdGF0ZU5vZGV9fQpmdW5jdGlvbiBXaihhLGIsYyl7dmFyIGQ9YS50YWc7aWYoNT09PWR8fDY9PT1kKWE9YS5zdGF0ZU5vZGUsYj84PT09Yy5ub2RlVHlwZT9jLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsYik6Yy5pbnNlcnRCZWZvcmUoYSxiKTooOD09PWMubm9kZVR5cGU/KGI9Yy5wYXJlbnROb2RlLGIuaW5zZXJ0QmVmb3JlKGEsYykpOihiPWMsYi5hcHBlbmRDaGlsZChhKSksYz1jLl9yZWFjdFJvb3RDb250YWluZXIsbnVsbCE9PWMmJnZvaWQgMCE9PWN8fG51bGwhPT1iLm9uY2xpY2t8fChiLm9uY2xpY2s9QmYpKTtlbHNlIGlmKDQhPT1kJiYoYT1hLmNoaWxkLG51bGwhPT1hKSlmb3IoV2ooYSxiLGMpLGE9YS5zaWJsaW5nO251bGwhPT1hOylXaihhLGIsYyksYT1hLnNpYmxpbmd9CmZ1bmN0aW9uIFhqKGEsYixjKXt2YXIgZD1hLnRhZztpZig1PT09ZHx8Nj09PWQpYT1hLnN0YXRlTm9kZSxiP2MuaW5zZXJ0QmVmb3JlKGEsYik6Yy5hcHBlbmRDaGlsZChhKTtlbHNlIGlmKDQhPT1kJiYoYT1hLmNoaWxkLG51bGwhPT1hKSlmb3IoWGooYSxiLGMpLGE9YS5zaWJsaW5nO251bGwhPT1hOylYaihhLGIsYyksYT1hLnNpYmxpbmd9dmFyIFg9bnVsbCxZaj0hMTtmdW5jdGlvbiBaaihhLGIsYyl7Zm9yKGM9Yy5jaGlsZDtudWxsIT09YzspYWsoYSxiLGMpLGM9Yy5zaWJsaW5nfQpmdW5jdGlvbiBhayhhLGIsYyl7aWYobGMmJiJmdW5jdGlvbiI9PT10eXBlb2YgbGMub25Db21taXRGaWJlclVubW91bnQpdHJ5e2xjLm9uQ29tbWl0RmliZXJVbm1vdW50KGtjLGMpfWNhdGNoKGgpe31zd2l0Y2goYy50YWcpe2Nhc2UgNTpVfHxNaihjLGIpO2Nhc2UgNjp2YXIgZD1YLGU9WWo7WD1udWxsO1pqKGEsYixjKTtYPWQ7WWo9ZTtudWxsIT09WCYmKFlqPyhhPVgsYz1jLnN0YXRlTm9kZSw4PT09YS5ub2RlVHlwZT9hLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoYyk6YS5yZW1vdmVDaGlsZChjKSk6WC5yZW1vdmVDaGlsZChjLnN0YXRlTm9kZSkpO2JyZWFrO2Nhc2UgMTg6bnVsbCE9PVgmJihZaj8oYT1YLGM9Yy5zdGF0ZU5vZGUsOD09PWEubm9kZVR5cGU/S2YoYS5wYXJlbnROb2RlLGMpOjE9PT1hLm5vZGVUeXBlJiZLZihhLGMpLGJkKGEpKTpLZihYLGMuc3RhdGVOb2RlKSk7YnJlYWs7Y2FzZSA0OmQ9WDtlPVlqO1g9Yy5zdGF0ZU5vZGUuY29udGFpbmVySW5mbztZaj0hMDsKWmooYSxiLGMpO1g9ZDtZaj1lO2JyZWFrO2Nhc2UgMDpjYXNlIDExOmNhc2UgMTQ6Y2FzZSAxNTppZighVSYmKGQ9Yy51cGRhdGVRdWV1ZSxudWxsIT09ZCYmKGQ9ZC5sYXN0RWZmZWN0LG51bGwhPT1kKSkpe2U9ZD1kLm5leHQ7ZG97dmFyIGY9ZSxnPWYuZGVzdHJveTtmPWYudGFnO3ZvaWQgMCE9PWcmJigwIT09KGYmMik/TmooYyxiLGcpOjAhPT0oZiY0KSYmTmooYyxiLGcpKTtlPWUubmV4dH13aGlsZShlIT09ZCl9WmooYSxiLGMpO2JyZWFrO2Nhc2UgMTppZighVSYmKE1qKGMsYiksZD1jLnN0YXRlTm9kZSwiZnVuY3Rpb24iPT09dHlwZW9mIGQuY29tcG9uZW50V2lsbFVubW91bnQpKXRyeXtkLnByb3BzPWMubWVtb2l6ZWRQcm9wcyxkLnN0YXRlPWMubWVtb2l6ZWRTdGF0ZSxkLmNvbXBvbmVudFdpbGxVbm1vdW50KCl9Y2F0Y2goaCl7VyhjLGIsaCl9WmooYSxiLGMpO2JyZWFrO2Nhc2UgMjE6WmooYSxiLGMpO2JyZWFrO2Nhc2UgMjI6Yy5tb2RlJjE/KFU9KGQ9VSl8fG51bGwhPT0KYy5tZW1vaXplZFN0YXRlLFpqKGEsYixjKSxVPWQpOlpqKGEsYixjKTticmVhaztkZWZhdWx0OlpqKGEsYixjKX19ZnVuY3Rpb24gYmsoYSl7dmFyIGI9YS51cGRhdGVRdWV1ZTtpZihudWxsIT09Yil7YS51cGRhdGVRdWV1ZT1udWxsO3ZhciBjPWEuc3RhdGVOb2RlO251bGw9PT1jJiYoYz1hLnN0YXRlTm9kZT1uZXcgTGopO2IuZm9yRWFjaChmdW5jdGlvbihiKXt2YXIgZD1jay5iaW5kKG51bGwsYSxiKTtjLmhhcyhiKXx8KGMuYWRkKGIpLGIudGhlbihkLGQpKX0pfX0KZnVuY3Rpb24gZGsoYSxiKXt2YXIgYz1iLmRlbGV0aW9ucztpZihudWxsIT09Yylmb3IodmFyIGQ9MDtkPGMubGVuZ3RoO2QrKyl7dmFyIGU9Y1tkXTt0cnl7dmFyIGY9YSxnPWIsaD1nO2E6Zm9yKDtudWxsIT09aDspe3N3aXRjaChoLnRhZyl7Y2FzZSA1Olg9aC5zdGF0ZU5vZGU7WWo9ITE7YnJlYWsgYTtjYXNlIDM6WD1oLnN0YXRlTm9kZS5jb250YWluZXJJbmZvO1lqPSEwO2JyZWFrIGE7Y2FzZSA0Olg9aC5zdGF0ZU5vZGUuY29udGFpbmVySW5mbztZaj0hMDticmVhayBhfWg9aC5yZXR1cm59aWYobnVsbD09PVgpdGhyb3cgRXJyb3IocCgxNjApKTthayhmLGcsZSk7WD1udWxsO1lqPSExO3ZhciBrPWUuYWx0ZXJuYXRlO251bGwhPT1rJiYoay5yZXR1cm49bnVsbCk7ZS5yZXR1cm49bnVsbH1jYXRjaChsKXtXKGUsYixsKX19aWYoYi5zdWJ0cmVlRmxhZ3MmMTI4NTQpZm9yKGI9Yi5jaGlsZDtudWxsIT09YjspZWsoYixhKSxiPWIuc2libGluZ30KZnVuY3Rpb24gZWsoYSxiKXt2YXIgYz1hLmFsdGVybmF0ZSxkPWEuZmxhZ3M7c3dpdGNoKGEudGFnKXtjYXNlIDA6Y2FzZSAxMTpjYXNlIDE0OmNhc2UgMTU6ZGsoYixhKTtmayhhKTtpZihkJjQpe3RyeXtRaigzLGEsYS5yZXR1cm4pLFJqKDMsYSl9Y2F0Y2godCl7VyhhLGEucmV0dXJuLHQpfXRyeXtRaig1LGEsYS5yZXR1cm4pfWNhdGNoKHQpe1coYSxhLnJldHVybix0KX19YnJlYWs7Y2FzZSAxOmRrKGIsYSk7ZmsoYSk7ZCY1MTImJm51bGwhPT1jJiZNaihjLGMucmV0dXJuKTticmVhaztjYXNlIDU6ZGsoYixhKTtmayhhKTtkJjUxMiYmbnVsbCE9PWMmJk1qKGMsYy5yZXR1cm4pO2lmKGEuZmxhZ3MmMzIpe3ZhciBlPWEuc3RhdGVOb2RlO3RyeXtvYihlLCIiKX1jYXRjaCh0KXtXKGEsYS5yZXR1cm4sdCl9fWlmKGQmNCYmKGU9YS5zdGF0ZU5vZGUsbnVsbCE9ZSkpe3ZhciBmPWEubWVtb2l6ZWRQcm9wcyxnPW51bGwhPT1jP2MubWVtb2l6ZWRQcm9wczpmLGg9YS50eXBlLGs9YS51cGRhdGVRdWV1ZTsKYS51cGRhdGVRdWV1ZT1udWxsO2lmKG51bGwhPT1rKXRyeXsiaW5wdXQiPT09aCYmInJhZGlvIj09PWYudHlwZSYmbnVsbCE9Zi5uYW1lJiZhYihlLGYpO3ZiKGgsZyk7dmFyIGw9dmIoaCxmKTtmb3IoZz0wO2c8ay5sZW5ndGg7Zys9Mil7dmFyIG09a1tnXSxxPWtbZysxXTsic3R5bGUiPT09bT9zYihlLHEpOiJkYW5nZXJvdXNseVNldElubmVySFRNTCI9PT1tP25iKGUscSk6ImNoaWxkcmVuIj09PW0/b2IoZSxxKTp0YShlLG0scSxsKX1zd2l0Y2goaCl7Y2FzZSAiaW5wdXQiOmJiKGUsZik7YnJlYWs7Y2FzZSAidGV4dGFyZWEiOmliKGUsZik7YnJlYWs7Y2FzZSAic2VsZWN0Ijp2YXIgcj1lLl93cmFwcGVyU3RhdGUud2FzTXVsdGlwbGU7ZS5fd3JhcHBlclN0YXRlLndhc011bHRpcGxlPSEhZi5tdWx0aXBsZTt2YXIgeT1mLnZhbHVlO251bGwhPXk/ZmIoZSwhIWYubXVsdGlwbGUseSwhMSk6ciE9PSEhZi5tdWx0aXBsZSYmKG51bGwhPWYuZGVmYXVsdFZhbHVlP2ZiKGUsISFmLm11bHRpcGxlLApmLmRlZmF1bHRWYWx1ZSwhMCk6ZmIoZSwhIWYubXVsdGlwbGUsZi5tdWx0aXBsZT9bXToiIiwhMSkpfWVbUGZdPWZ9Y2F0Y2godCl7VyhhLGEucmV0dXJuLHQpfX1icmVhaztjYXNlIDY6ZGsoYixhKTtmayhhKTtpZihkJjQpe2lmKG51bGw9PT1hLnN0YXRlTm9kZSl0aHJvdyBFcnJvcihwKDE2MikpO2U9YS5zdGF0ZU5vZGU7Zj1hLm1lbW9pemVkUHJvcHM7dHJ5e2Uubm9kZVZhbHVlPWZ9Y2F0Y2godCl7VyhhLGEucmV0dXJuLHQpfX1icmVhaztjYXNlIDM6ZGsoYixhKTtmayhhKTtpZihkJjQmJm51bGwhPT1jJiZjLm1lbW9pemVkU3RhdGUuaXNEZWh5ZHJhdGVkKXRyeXtiZChiLmNvbnRhaW5lckluZm8pfWNhdGNoKHQpe1coYSxhLnJldHVybix0KX1icmVhaztjYXNlIDQ6ZGsoYixhKTtmayhhKTticmVhaztjYXNlIDEzOmRrKGIsYSk7ZmsoYSk7ZT1hLmNoaWxkO2UuZmxhZ3MmODE5MiYmKGY9bnVsbCE9PWUubWVtb2l6ZWRTdGF0ZSxlLnN0YXRlTm9kZS5pc0hpZGRlbj1mLCFmfHwKbnVsbCE9PWUuYWx0ZXJuYXRlJiZudWxsIT09ZS5hbHRlcm5hdGUubWVtb2l6ZWRTdGF0ZXx8KGdrPUIoKSkpO2QmNCYmYmsoYSk7YnJlYWs7Y2FzZSAyMjptPW51bGwhPT1jJiZudWxsIT09Yy5tZW1vaXplZFN0YXRlO2EubW9kZSYxPyhVPShsPVUpfHxtLGRrKGIsYSksVT1sKTpkayhiLGEpO2ZrKGEpO2lmKGQmODE5Mil7bD1udWxsIT09YS5tZW1vaXplZFN0YXRlO2lmKChhLnN0YXRlTm9kZS5pc0hpZGRlbj1sKSYmIW0mJjAhPT0oYS5tb2RlJjEpKWZvcihWPWEsbT1hLmNoaWxkO251bGwhPT1tOyl7Zm9yKHE9Vj1tO251bGwhPT1WOyl7cj1WO3k9ci5jaGlsZDtzd2l0Y2goci50YWcpe2Nhc2UgMDpjYXNlIDExOmNhc2UgMTQ6Y2FzZSAxNTpRaig0LHIsci5yZXR1cm4pO2JyZWFrO2Nhc2UgMTpNaihyLHIucmV0dXJuKTt2YXIgbj1yLnN0YXRlTm9kZTtpZigiZnVuY3Rpb24iPT09dHlwZW9mIG4uY29tcG9uZW50V2lsbFVubW91bnQpe2Q9cjtjPXIucmV0dXJuO3RyeXtiPWQsbi5wcm9wcz0KYi5tZW1vaXplZFByb3BzLG4uc3RhdGU9Yi5tZW1vaXplZFN0YXRlLG4uY29tcG9uZW50V2lsbFVubW91bnQoKX1jYXRjaCh0KXtXKGQsYyx0KX19YnJlYWs7Y2FzZSA1Ok1qKHIsci5yZXR1cm4pO2JyZWFrO2Nhc2UgMjI6aWYobnVsbCE9PXIubWVtb2l6ZWRTdGF0ZSl7aGsocSk7Y29udGludWV9fW51bGwhPT15Pyh5LnJldHVybj1yLFY9eSk6aGsocSl9bT1tLnNpYmxpbmd9YTpmb3IobT1udWxsLHE9YTs7KXtpZig1PT09cS50YWcpe2lmKG51bGw9PT1tKXttPXE7dHJ5e2U9cS5zdGF0ZU5vZGUsbD8oZj1lLnN0eWxlLCJmdW5jdGlvbiI9PT10eXBlb2YgZi5zZXRQcm9wZXJ0eT9mLnNldFByb3BlcnR5KCJkaXNwbGF5Iiwibm9uZSIsImltcG9ydGFudCIpOmYuZGlzcGxheT0ibm9uZSIpOihoPXEuc3RhdGVOb2RlLGs9cS5tZW1vaXplZFByb3BzLnN0eWxlLGc9dm9pZCAwIT09ayYmbnVsbCE9PWsmJmsuaGFzT3duUHJvcGVydHkoImRpc3BsYXkiKT9rLmRpc3BsYXk6bnVsbCxoLnN0eWxlLmRpc3BsYXk9CnJiKCJkaXNwbGF5IixnKSl9Y2F0Y2godCl7VyhhLGEucmV0dXJuLHQpfX19ZWxzZSBpZig2PT09cS50YWcpe2lmKG51bGw9PT1tKXRyeXtxLnN0YXRlTm9kZS5ub2RlVmFsdWU9bD8iIjpxLm1lbW9pemVkUHJvcHN9Y2F0Y2godCl7VyhhLGEucmV0dXJuLHQpfX1lbHNlIGlmKCgyMiE9PXEudGFnJiYyMyE9PXEudGFnfHxudWxsPT09cS5tZW1vaXplZFN0YXRlfHxxPT09YSkmJm51bGwhPT1xLmNoaWxkKXtxLmNoaWxkLnJldHVybj1xO3E9cS5jaGlsZDtjb250aW51ZX1pZihxPT09YSlicmVhayBhO2Zvcig7bnVsbD09PXEuc2libGluZzspe2lmKG51bGw9PT1xLnJldHVybnx8cS5yZXR1cm49PT1hKWJyZWFrIGE7bT09PXEmJihtPW51bGwpO3E9cS5yZXR1cm59bT09PXEmJihtPW51bGwpO3Euc2libGluZy5yZXR1cm49cS5yZXR1cm47cT1xLnNpYmxpbmd9fWJyZWFrO2Nhc2UgMTk6ZGsoYixhKTtmayhhKTtkJjQmJmJrKGEpO2JyZWFrO2Nhc2UgMjE6YnJlYWs7ZGVmYXVsdDpkayhiLAphKSxmayhhKX19ZnVuY3Rpb24gZmsoYSl7dmFyIGI9YS5mbGFncztpZihiJjIpe3RyeXthOntmb3IodmFyIGM9YS5yZXR1cm47bnVsbCE9PWM7KXtpZihVaihjKSl7dmFyIGQ9YzticmVhayBhfWM9Yy5yZXR1cm59dGhyb3cgRXJyb3IocCgxNjApKTt9c3dpdGNoKGQudGFnKXtjYXNlIDU6dmFyIGU9ZC5zdGF0ZU5vZGU7ZC5mbGFncyYzMiYmKG9iKGUsIiIpLGQuZmxhZ3MmPS0zMyk7dmFyIGY9VmooYSk7WGooYSxmLGUpO2JyZWFrO2Nhc2UgMzpjYXNlIDQ6dmFyIGc9ZC5zdGF0ZU5vZGUuY29udGFpbmVySW5mbyxoPVZqKGEpO1dqKGEsaCxnKTticmVhaztkZWZhdWx0OnRocm93IEVycm9yKHAoMTYxKSk7fX1jYXRjaChrKXtXKGEsYS5yZXR1cm4sayl9YS5mbGFncyY9LTN9YiY0MDk2JiYoYS5mbGFncyY9LTQwOTcpfWZ1bmN0aW9uIGlrKGEsYixjKXtWPWE7amsoYSxiLGMpfQpmdW5jdGlvbiBqayhhLGIsYyl7Zm9yKHZhciBkPTAhPT0oYS5tb2RlJjEpO251bGwhPT1WOyl7dmFyIGU9VixmPWUuY2hpbGQ7aWYoMjI9PT1lLnRhZyYmZCl7dmFyIGc9bnVsbCE9PWUubWVtb2l6ZWRTdGF0ZXx8S2o7aWYoIWcpe3ZhciBoPWUuYWx0ZXJuYXRlLGs9bnVsbCE9PWgmJm51bGwhPT1oLm1lbW9pemVkU3RhdGV8fFU7aD1Lajt2YXIgbD1VO0tqPWc7aWYoKFU9aykmJiFsKWZvcihWPWU7bnVsbCE9PVY7KWc9VixrPWcuY2hpbGQsMjI9PT1nLnRhZyYmbnVsbCE9PWcubWVtb2l6ZWRTdGF0ZT9rayhlKTpudWxsIT09az8oay5yZXR1cm49ZyxWPWspOmtrKGUpO2Zvcig7bnVsbCE9PWY7KVY9ZixqayhmLGIsYyksZj1mLnNpYmxpbmc7Vj1lO0tqPWg7VT1sfWxrKGEsYixjKX1lbHNlIDAhPT0oZS5zdWJ0cmVlRmxhZ3MmODc3MikmJm51bGwhPT1mPyhmLnJldHVybj1lLFY9Zik6bGsoYSxiLGMpfX0KZnVuY3Rpb24gbGsoYSl7Zm9yKDtudWxsIT09Vjspe3ZhciBiPVY7aWYoMCE9PShiLmZsYWdzJjg3NzIpKXt2YXIgYz1iLmFsdGVybmF0ZTt0cnl7aWYoMCE9PShiLmZsYWdzJjg3NzIpKXN3aXRjaChiLnRhZyl7Y2FzZSAwOmNhc2UgMTE6Y2FzZSAxNTpVfHxSaig1LGIpO2JyZWFrO2Nhc2UgMTp2YXIgZD1iLnN0YXRlTm9kZTtpZihiLmZsYWdzJjQmJiFVKWlmKG51bGw9PT1jKWQuY29tcG9uZW50RGlkTW91bnQoKTtlbHNle3ZhciBlPWIuZWxlbWVudFR5cGU9PT1iLnR5cGU/Yy5tZW1vaXplZFByb3BzOkxnKGIudHlwZSxjLm1lbW9pemVkUHJvcHMpO2QuY29tcG9uZW50RGlkVXBkYXRlKGUsYy5tZW1vaXplZFN0YXRlLGQuX19yZWFjdEludGVybmFsU25hcHNob3RCZWZvcmVVcGRhdGUpfXZhciBmPWIudXBkYXRlUXVldWU7bnVsbCE9PWYmJmloKGIsZixkKTticmVhaztjYXNlIDM6dmFyIGc9Yi51cGRhdGVRdWV1ZTtpZihudWxsIT09Zyl7Yz1udWxsO2lmKG51bGwhPT1iLmNoaWxkKXN3aXRjaChiLmNoaWxkLnRhZyl7Y2FzZSA1OmM9CmIuY2hpbGQuc3RhdGVOb2RlO2JyZWFrO2Nhc2UgMTpjPWIuY2hpbGQuc3RhdGVOb2RlfWloKGIsZyxjKX1icmVhaztjYXNlIDU6dmFyIGg9Yi5zdGF0ZU5vZGU7aWYobnVsbD09PWMmJmIuZmxhZ3MmNCl7Yz1oO3ZhciBrPWIubWVtb2l6ZWRQcm9wcztzd2l0Y2goYi50eXBlKXtjYXNlICJidXR0b24iOmNhc2UgImlucHV0IjpjYXNlICJzZWxlY3QiOmNhc2UgInRleHRhcmVhIjprLmF1dG9Gb2N1cyYmYy5mb2N1cygpO2JyZWFrO2Nhc2UgImltZyI6ay5zcmMmJihjLnNyYz1rLnNyYyl9fWJyZWFrO2Nhc2UgNjpicmVhaztjYXNlIDQ6YnJlYWs7Y2FzZSAxMjpicmVhaztjYXNlIDEzOmlmKG51bGw9PT1iLm1lbW9pemVkU3RhdGUpe3ZhciBsPWIuYWx0ZXJuYXRlO2lmKG51bGwhPT1sKXt2YXIgbT1sLm1lbW9pemVkU3RhdGU7aWYobnVsbCE9PW0pe3ZhciBxPW0uZGVoeWRyYXRlZDtudWxsIT09cSYmYmQocSl9fX1icmVhaztjYXNlIDE5OmNhc2UgMTc6Y2FzZSAyMTpjYXNlIDIyOmNhc2UgMjM6Y2FzZSAyNTpicmVhazsKZGVmYXVsdDp0aHJvdyBFcnJvcihwKDE2MykpO31VfHxiLmZsYWdzJjUxMiYmU2ooYil9Y2F0Y2gocil7VyhiLGIucmV0dXJuLHIpfX1pZihiPT09YSl7Vj1udWxsO2JyZWFrfWM9Yi5zaWJsaW5nO2lmKG51bGwhPT1jKXtjLnJldHVybj1iLnJldHVybjtWPWM7YnJlYWt9Vj1iLnJldHVybn19ZnVuY3Rpb24gaGsoYSl7Zm9yKDtudWxsIT09Vjspe3ZhciBiPVY7aWYoYj09PWEpe1Y9bnVsbDticmVha312YXIgYz1iLnNpYmxpbmc7aWYobnVsbCE9PWMpe2MucmV0dXJuPWIucmV0dXJuO1Y9YzticmVha31WPWIucmV0dXJufX0KZnVuY3Rpb24ga2soYSl7Zm9yKDtudWxsIT09Vjspe3ZhciBiPVY7dHJ5e3N3aXRjaChiLnRhZyl7Y2FzZSAwOmNhc2UgMTE6Y2FzZSAxNTp2YXIgYz1iLnJldHVybjt0cnl7UmooNCxiKX1jYXRjaChrKXtXKGIsYyxrKX1icmVhaztjYXNlIDE6dmFyIGQ9Yi5zdGF0ZU5vZGU7aWYoImZ1bmN0aW9uIj09PXR5cGVvZiBkLmNvbXBvbmVudERpZE1vdW50KXt2YXIgZT1iLnJldHVybjt0cnl7ZC5jb21wb25lbnREaWRNb3VudCgpfWNhdGNoKGspe1coYixlLGspfX12YXIgZj1iLnJldHVybjt0cnl7U2ooYil9Y2F0Y2goayl7VyhiLGYsayl9YnJlYWs7Y2FzZSA1OnZhciBnPWIucmV0dXJuO3RyeXtTaihiKX1jYXRjaChrKXtXKGIsZyxrKX19fWNhdGNoKGspe1coYixiLnJldHVybixrKX1pZihiPT09YSl7Vj1udWxsO2JyZWFrfXZhciBoPWIuc2libGluZztpZihudWxsIT09aCl7aC5yZXR1cm49Yi5yZXR1cm47Vj1oO2JyZWFrfVY9Yi5yZXR1cm59fQp2YXIgbWs9TWF0aC5jZWlsLG5rPXVhLlJlYWN0Q3VycmVudERpc3BhdGNoZXIsb2s9dWEuUmVhY3RDdXJyZW50T3duZXIscGs9dWEuUmVhY3RDdXJyZW50QmF0Y2hDb25maWcsSz0wLFI9bnVsbCxZPW51bGwsWj0wLGdqPTAsZmo9VWYoMCksVD0wLHFrPW51bGwsaGg9MCxyaz0wLHNrPTAsdGs9bnVsbCx1az1udWxsLGdrPTAsSGo9SW5maW5pdHksdms9bnVsbCxQaT0hMSxRaT1udWxsLFNpPW51bGwsd2s9ITEseGs9bnVsbCx5az0wLHprPTAsQWs9bnVsbCxCaz0tMSxDaz0wO2Z1bmN0aW9uIEwoKXtyZXR1cm4gMCE9PShLJjYpP0IoKTotMSE9PUJrP0JrOkJrPUIoKX0KZnVuY3Rpb24gbGgoYSl7aWYoMD09PShhLm1vZGUmMSkpcmV0dXJuIDE7aWYoMCE9PShLJjIpJiYwIT09WilyZXR1cm4gWiYtWjtpZihudWxsIT09S2cudHJhbnNpdGlvbilyZXR1cm4gMD09PUNrJiYoQ2s9eWMoKSksQ2s7YT1DO2lmKDAhPT1hKXJldHVybiBhO2E9d2luZG93LmV2ZW50O2E9dm9pZCAwPT09YT8xNjpqZChhLnR5cGUpO3JldHVybiBhfWZ1bmN0aW9uIG1oKGEsYixjLGQpe2lmKDUwPHprKXRocm93IHprPTAsQWs9bnVsbCxFcnJvcihwKDE4NSkpO0FjKGEsYyxkKTtpZigwPT09KEsmMil8fGEhPT1SKWE9PT1SJiYoMD09PShLJjIpJiYocmt8PWMpLDQ9PT1UJiZEayhhLFopKSxFayhhLGQpLDE9PT1jJiYwPT09SyYmMD09PShiLm1vZGUmMSkmJihIaj1CKCkrNTAwLGZnJiZqZygpKX0KZnVuY3Rpb24gRWsoYSxiKXt2YXIgYz1hLmNhbGxiYWNrTm9kZTt3YyhhLGIpO3ZhciBkPXVjKGEsYT09PVI/WjowKTtpZigwPT09ZCludWxsIT09YyYmYmMoYyksYS5jYWxsYmFja05vZGU9bnVsbCxhLmNhbGxiYWNrUHJpb3JpdHk9MDtlbHNlIGlmKGI9ZCYtZCxhLmNhbGxiYWNrUHJpb3JpdHkhPT1iKXtudWxsIT1jJiZiYyhjKTtpZigxPT09YikwPT09YS50YWc/aWcoRmsuYmluZChudWxsLGEpKTpoZyhGay5iaW5kKG51bGwsYSkpLEpmKGZ1bmN0aW9uKCl7MD09PShLJjYpJiZqZygpfSksYz1udWxsO2Vsc2V7c3dpdGNoKERjKGQpKXtjYXNlIDE6Yz1mYzticmVhaztjYXNlIDQ6Yz1nYzticmVhaztjYXNlIDE2OmM9aGM7YnJlYWs7Y2FzZSA1MzY4NzA5MTI6Yz1qYzticmVhaztkZWZhdWx0OmM9aGN9Yz1HayhjLEhrLmJpbmQobnVsbCxhKSl9YS5jYWxsYmFja1ByaW9yaXR5PWI7YS5jYWxsYmFja05vZGU9Y319CmZ1bmN0aW9uIEhrKGEsYil7Qms9LTE7Q2s9MDtpZigwIT09KEsmNikpdGhyb3cgRXJyb3IocCgzMjcpKTt2YXIgYz1hLmNhbGxiYWNrTm9kZTtpZihJaygpJiZhLmNhbGxiYWNrTm9kZSE9PWMpcmV0dXJuIG51bGw7dmFyIGQ9dWMoYSxhPT09Uj9aOjApO2lmKDA9PT1kKXJldHVybiBudWxsO2lmKDAhPT0oZCYzMCl8fDAhPT0oZCZhLmV4cGlyZWRMYW5lcyl8fGIpYj1KayhhLGQpO2Vsc2V7Yj1kO3ZhciBlPUs7S3w9Mjt2YXIgZj1LaygpO2lmKFIhPT1hfHxaIT09Yil2az1udWxsLEhqPUIoKSs1MDAsTGsoYSxiKTtkbyB0cnl7TWsoKTticmVha31jYXRjaChoKXtOayhhLGgpfXdoaWxlKDEpO1FnKCk7bmsuY3VycmVudD1mO0s9ZTtudWxsIT09WT9iPTA6KFI9bnVsbCxaPTAsYj1UKX1pZigwIT09Yil7Mj09PWImJihlPXhjKGEpLDAhPT1lJiYoZD1lLGI9T2soYSxlKSkpO2lmKDE9PT1iKXRocm93IGM9cWssTGsoYSwwKSxEayhhLGQpLEVrKGEsQigpKSxjO2lmKDY9PT1iKURrKGEsZCk7CmVsc2V7ZT1hLmN1cnJlbnQuYWx0ZXJuYXRlO2lmKDA9PT0oZCYzMCkmJiFQayhlKSYmKGI9SmsoYSxkKSwyPT09YiYmKGY9eGMoYSksMCE9PWYmJihkPWYsYj1PayhhLGYpKSksMT09PWIpKXRocm93IGM9cWssTGsoYSwwKSxEayhhLGQpLEVrKGEsQigpKSxjO2EuZmluaXNoZWRXb3JrPWU7YS5maW5pc2hlZExhbmVzPWQ7c3dpdGNoKGIpe2Nhc2UgMDpjYXNlIDE6dGhyb3cgRXJyb3IocCgzNDUpKTtjYXNlIDI6UWsoYSx1ayx2ayk7YnJlYWs7Y2FzZSAzOkRrKGEsZCk7aWYoKGQmMTMwMDIzNDI0KT09PWQmJihiPWdrKzUwMC1CKCksMTA8Yikpe2lmKDAhPT11YyhhLDApKWJyZWFrO2U9YS5zdXNwZW5kZWRMYW5lcztpZigoZSZkKSE9PWQpe0woKTthLnBpbmdlZExhbmVzfD1hLnN1c3BlbmRlZExhbmVzJmU7YnJlYWt9YS50aW1lb3V0SGFuZGxlPUZmKFFrLmJpbmQobnVsbCxhLHVrLHZrKSxiKTticmVha31RayhhLHVrLHZrKTticmVhaztjYXNlIDQ6RGsoYSxkKTtpZigoZCY0MTk0MjQwKT09PQpkKWJyZWFrO2I9YS5ldmVudFRpbWVzO2ZvcihlPS0xOzA8ZDspe3ZhciBnPTMxLW9jKGQpO2Y9MTw8ZztnPWJbZ107Zz5lJiYoZT1nKTtkJj1+Zn1kPWU7ZD1CKCktZDtkPSgxMjA+ZD8xMjA6NDgwPmQ/NDgwOjEwODA+ZD8xMDgwOjE5MjA+ZD8xOTIwOjNFMz5kPzNFMzo0MzIwPmQ/NDMyMDoxOTYwKm1rKGQvMTk2MCkpLWQ7aWYoMTA8ZCl7YS50aW1lb3V0SGFuZGxlPUZmKFFrLmJpbmQobnVsbCxhLHVrLHZrKSxkKTticmVha31RayhhLHVrLHZrKTticmVhaztjYXNlIDU6UWsoYSx1ayx2ayk7YnJlYWs7ZGVmYXVsdDp0aHJvdyBFcnJvcihwKDMyOSkpO319fUVrKGEsQigpKTtyZXR1cm4gYS5jYWxsYmFja05vZGU9PT1jP0hrLmJpbmQobnVsbCxhKTpudWxsfQpmdW5jdGlvbiBPayhhLGIpe3ZhciBjPXRrO2EuY3VycmVudC5tZW1vaXplZFN0YXRlLmlzRGVoeWRyYXRlZCYmKExrKGEsYikuZmxhZ3N8PTI1Nik7YT1KayhhLGIpOzIhPT1hJiYoYj11ayx1az1jLG51bGwhPT1iJiZHaihiKSk7cmV0dXJuIGF9ZnVuY3Rpb24gR2ooYSl7bnVsbD09PXVrP3VrPWE6dWsucHVzaC5hcHBseSh1ayxhKX0KZnVuY3Rpb24gUGsoYSl7Zm9yKHZhciBiPWE7Oyl7aWYoYi5mbGFncyYxNjM4NCl7dmFyIGM9Yi51cGRhdGVRdWV1ZTtpZihudWxsIT09YyYmKGM9Yy5zdG9yZXMsbnVsbCE9PWMpKWZvcih2YXIgZD0wO2Q8Yy5sZW5ndGg7ZCsrKXt2YXIgZT1jW2RdLGY9ZS5nZXRTbmFwc2hvdDtlPWUudmFsdWU7dHJ5e2lmKCFIZShmKCksZSkpcmV0dXJuITF9Y2F0Y2goZyl7cmV0dXJuITF9fX1jPWIuY2hpbGQ7aWYoYi5zdWJ0cmVlRmxhZ3MmMTYzODQmJm51bGwhPT1jKWMucmV0dXJuPWIsYj1jO2Vsc2V7aWYoYj09PWEpYnJlYWs7Zm9yKDtudWxsPT09Yi5zaWJsaW5nOyl7aWYobnVsbD09PWIucmV0dXJufHxiLnJldHVybj09PWEpcmV0dXJuITA7Yj1iLnJldHVybn1iLnNpYmxpbmcucmV0dXJuPWIucmV0dXJuO2I9Yi5zaWJsaW5nfX1yZXR1cm4hMH0KZnVuY3Rpb24gRGsoYSxiKXtiJj1+c2s7YiY9fnJrO2Euc3VzcGVuZGVkTGFuZXN8PWI7YS5waW5nZWRMYW5lcyY9fmI7Zm9yKGE9YS5leHBpcmF0aW9uVGltZXM7MDxiOyl7dmFyIGM9MzEtb2MoYiksZD0xPDxjO2FbY109LTE7YiY9fmR9fWZ1bmN0aW9uIEZrKGEpe2lmKDAhPT0oSyY2KSl0aHJvdyBFcnJvcihwKDMyNykpO0lrKCk7dmFyIGI9dWMoYSwwKTtpZigwPT09KGImMSkpcmV0dXJuIEVrKGEsQigpKSxudWxsO3ZhciBjPUprKGEsYik7aWYoMCE9PWEudGFnJiYyPT09Yyl7dmFyIGQ9eGMoYSk7MCE9PWQmJihiPWQsYz1PayhhLGQpKX1pZigxPT09Yyl0aHJvdyBjPXFrLExrKGEsMCksRGsoYSxiKSxFayhhLEIoKSksYztpZig2PT09Yyl0aHJvdyBFcnJvcihwKDM0NSkpO2EuZmluaXNoZWRXb3JrPWEuY3VycmVudC5hbHRlcm5hdGU7YS5maW5pc2hlZExhbmVzPWI7UWsoYSx1ayx2ayk7RWsoYSxCKCkpO3JldHVybiBudWxsfQpmdW5jdGlvbiBSayhhLGIpe3ZhciBjPUs7S3w9MTt0cnl7cmV0dXJuIGEoYil9ZmluYWxseXtLPWMsMD09PUsmJihIaj1CKCkrNTAwLGZnJiZqZygpKX19ZnVuY3Rpb24gU2soYSl7bnVsbCE9PXhrJiYwPT09eGsudGFnJiYwPT09KEsmNikmJklrKCk7dmFyIGI9SztLfD0xO3ZhciBjPXBrLnRyYW5zaXRpb24sZD1DO3RyeXtpZihway50cmFuc2l0aW9uPW51bGwsQz0xLGEpcmV0dXJuIGEoKX1maW5hbGx5e0M9ZCxway50cmFuc2l0aW9uPWMsSz1iLDA9PT0oSyY2KSYmamcoKX19ZnVuY3Rpb24gSWooKXtnaj1mai5jdXJyZW50O0UoZmopfQpmdW5jdGlvbiBMayhhLGIpe2EuZmluaXNoZWRXb3JrPW51bGw7YS5maW5pc2hlZExhbmVzPTA7dmFyIGM9YS50aW1lb3V0SGFuZGxlOy0xIT09YyYmKGEudGltZW91dEhhbmRsZT0tMSxHZihjKSk7aWYobnVsbCE9PVkpZm9yKGM9WS5yZXR1cm47bnVsbCE9PWM7KXt2YXIgZD1jO3dnKGQpO3N3aXRjaChkLnRhZyl7Y2FzZSAxOmQ9ZC50eXBlLmNoaWxkQ29udGV4dFR5cGVzO251bGwhPT1kJiZ2b2lkIDAhPT1kJiYkZigpO2JyZWFrO2Nhc2UgMzpKaCgpO0UoV2YpO0UoSCk7T2goKTticmVhaztjYXNlIDU6TGgoZCk7YnJlYWs7Y2FzZSA0OkpoKCk7YnJlYWs7Y2FzZSAxMzpFKE0pO2JyZWFrO2Nhc2UgMTk6RShNKTticmVhaztjYXNlIDEwOlJnKGQudHlwZS5fY29udGV4dCk7YnJlYWs7Y2FzZSAyMjpjYXNlIDIzOklqKCl9Yz1jLnJldHVybn1SPWE7WT1hPXdoKGEuY3VycmVudCxudWxsKTtaPWdqPWI7VD0wO3FrPW51bGw7c2s9cms9aGg9MDt1az10az1udWxsO2lmKG51bGwhPT1XZyl7Zm9yKGI9CjA7YjxXZy5sZW5ndGg7YisrKWlmKGM9V2dbYl0sZD1jLmludGVybGVhdmVkLG51bGwhPT1kKXtjLmludGVybGVhdmVkPW51bGw7dmFyIGU9ZC5uZXh0LGY9Yy5wZW5kaW5nO2lmKG51bGwhPT1mKXt2YXIgZz1mLm5leHQ7Zi5uZXh0PWU7ZC5uZXh0PWd9Yy5wZW5kaW5nPWR9V2c9bnVsbH1yZXR1cm4gYX0KZnVuY3Rpb24gTmsoYSxiKXtkb3t2YXIgYz1ZO3RyeXtRZygpO1BoLmN1cnJlbnQ9YWk7aWYoU2gpe2Zvcih2YXIgZD1OLm1lbW9pemVkU3RhdGU7bnVsbCE9PWQ7KXt2YXIgZT1kLnF1ZXVlO251bGwhPT1lJiYoZS5wZW5kaW5nPW51bGwpO2Q9ZC5uZXh0fVNoPSExfVJoPTA7UD1PPU49bnVsbDtUaD0hMTtVaD0wO29rLmN1cnJlbnQ9bnVsbDtpZihudWxsPT09Y3x8bnVsbD09PWMucmV0dXJuKXtUPTE7cWs9YjtZPW51bGw7YnJlYWt9YTp7dmFyIGY9YSxnPWMucmV0dXJuLGg9YyxrPWI7Yj1aO2guZmxhZ3N8PTMyNzY4O2lmKG51bGwhPT1rJiYib2JqZWN0Ij09PXR5cGVvZiBrJiYiZnVuY3Rpb24iPT09dHlwZW9mIGsudGhlbil7dmFyIGw9ayxtPWgscT1tLnRhZztpZigwPT09KG0ubW9kZSYxKSYmKDA9PT1xfHwxMT09PXF8fDE1PT09cSkpe3ZhciByPW0uYWx0ZXJuYXRlO3I/KG0udXBkYXRlUXVldWU9ci51cGRhdGVRdWV1ZSxtLm1lbW9pemVkU3RhdGU9ci5tZW1vaXplZFN0YXRlLAptLmxhbmVzPXIubGFuZXMpOihtLnVwZGF0ZVF1ZXVlPW51bGwsbS5tZW1vaXplZFN0YXRlPW51bGwpfXZhciB5PVZpKGcpO2lmKG51bGwhPT15KXt5LmZsYWdzJj0tMjU3O1dpKHksZyxoLGYsYik7eS5tb2RlJjEmJlRpKGYsbCxiKTtiPXk7az1sO3ZhciBuPWIudXBkYXRlUXVldWU7aWYobnVsbD09PW4pe3ZhciB0PW5ldyBTZXQ7dC5hZGQoayk7Yi51cGRhdGVRdWV1ZT10fWVsc2Ugbi5hZGQoayk7YnJlYWsgYX1lbHNle2lmKDA9PT0oYiYxKSl7VGkoZixsLGIpO3VqKCk7YnJlYWsgYX1rPUVycm9yKHAoNDI2KSl9fWVsc2UgaWYoSSYmaC5tb2RlJjEpe3ZhciBKPVZpKGcpO2lmKG51bGwhPT1KKXswPT09KEouZmxhZ3MmNjU1MzYpJiYoSi5mbGFnc3w9MjU2KTtXaShKLGcsaCxmLGIpO0pnKEtpKGssaCkpO2JyZWFrIGF9fWY9az1LaShrLGgpOzQhPT1UJiYoVD0yKTtudWxsPT09dGs/dGs9W2ZdOnRrLnB1c2goZik7Zj1nO2Rve3N3aXRjaChmLnRhZyl7Y2FzZSAzOmYuZmxhZ3N8PTY1NTM2OwpiJj0tYjtmLmxhbmVzfD1iO3ZhciB4PU9pKGYsayxiKTtmaChmLHgpO2JyZWFrIGE7Y2FzZSAxOmg9azt2YXIgdz1mLnR5cGUsdT1mLnN0YXRlTm9kZTtpZigwPT09KGYuZmxhZ3MmMTI4KSYmKCJmdW5jdGlvbiI9PT10eXBlb2Ygdy5nZXREZXJpdmVkU3RhdGVGcm9tRXJyb3J8fG51bGwhPT11JiYiZnVuY3Rpb24iPT09dHlwZW9mIHUuY29tcG9uZW50RGlkQ2F0Y2gmJihudWxsPT09U2l8fCFTaS5oYXModSkpKSl7Zi5mbGFnc3w9NjU1MzY7YiY9LWI7Zi5sYW5lc3w9Yjt2YXIgRj1SaShmLGgsYik7ZmgoZixGKTticmVhayBhfX1mPWYucmV0dXJufXdoaWxlKG51bGwhPT1mKX1UayhjKX1jYXRjaChuYSl7Yj1uYTtZPT09YyYmbnVsbCE9PWMmJihZPWM9Yy5yZXR1cm4pO2NvbnRpbnVlfWJyZWFrfXdoaWxlKDEpfWZ1bmN0aW9uIEtrKCl7dmFyIGE9bmsuY3VycmVudDtuay5jdXJyZW50PWFpO3JldHVybiBudWxsPT09YT9haTphfQpmdW5jdGlvbiB1aigpe2lmKDA9PT1UfHwzPT09VHx8Mj09PVQpVD00O251bGw9PT1SfHwwPT09KGhoJjI2ODQzNTQ1NSkmJjA9PT0ocmsmMjY4NDM1NDU1KXx8RGsoUixaKX1mdW5jdGlvbiBKayhhLGIpe3ZhciBjPUs7S3w9Mjt2YXIgZD1LaygpO2lmKFIhPT1hfHxaIT09Yil2az1udWxsLExrKGEsYik7ZG8gdHJ5e1VrKCk7YnJlYWt9Y2F0Y2goZSl7TmsoYSxlKX13aGlsZSgxKTtRZygpO0s9Yztuay5jdXJyZW50PWQ7aWYobnVsbCE9PVkpdGhyb3cgRXJyb3IocCgyNjEpKTtSPW51bGw7Wj0wO3JldHVybiBUfWZ1bmN0aW9uIFVrKCl7Zm9yKDtudWxsIT09WTspVmsoWSl9ZnVuY3Rpb24gTWsoKXtmb3IoO251bGwhPT1ZJiYhY2MoKTspVmsoWSl9ZnVuY3Rpb24gVmsoYSl7dmFyIGI9V2soYS5hbHRlcm5hdGUsYSxnaik7YS5tZW1vaXplZFByb3BzPWEucGVuZGluZ1Byb3BzO251bGw9PT1iP1RrKGEpOlk9Yjtvay5jdXJyZW50PW51bGx9CmZ1bmN0aW9uIFRrKGEpe3ZhciBiPWE7ZG97dmFyIGM9Yi5hbHRlcm5hdGU7YT1iLnJldHVybjtpZigwPT09KGIuZmxhZ3MmMzI3NjgpKXtpZihjPUZqKGMsYixnaiksbnVsbCE9PWMpe1k9YztyZXR1cm59fWVsc2V7Yz1KaihjLGIpO2lmKG51bGwhPT1jKXtjLmZsYWdzJj0zMjc2NztZPWM7cmV0dXJufWlmKG51bGwhPT1hKWEuZmxhZ3N8PTMyNzY4LGEuc3VidHJlZUZsYWdzPTAsYS5kZWxldGlvbnM9bnVsbDtlbHNle1Q9NjtZPW51bGw7cmV0dXJufX1iPWIuc2libGluZztpZihudWxsIT09Yil7WT1iO3JldHVybn1ZPWI9YX13aGlsZShudWxsIT09Yik7MD09PVQmJihUPTUpfWZ1bmN0aW9uIFFrKGEsYixjKXt2YXIgZD1DLGU9cGsudHJhbnNpdGlvbjt0cnl7cGsudHJhbnNpdGlvbj1udWxsLEM9MSxYayhhLGIsYyxkKX1maW5hbGx5e3BrLnRyYW5zaXRpb249ZSxDPWR9cmV0dXJuIG51bGx9CmZ1bmN0aW9uIFhrKGEsYixjLGQpe2RvIElrKCk7d2hpbGUobnVsbCE9PXhrKTtpZigwIT09KEsmNikpdGhyb3cgRXJyb3IocCgzMjcpKTtjPWEuZmluaXNoZWRXb3JrO3ZhciBlPWEuZmluaXNoZWRMYW5lcztpZihudWxsPT09YylyZXR1cm4gbnVsbDthLmZpbmlzaGVkV29yaz1udWxsO2EuZmluaXNoZWRMYW5lcz0wO2lmKGM9PT1hLmN1cnJlbnQpdGhyb3cgRXJyb3IocCgxNzcpKTthLmNhbGxiYWNrTm9kZT1udWxsO2EuY2FsbGJhY2tQcmlvcml0eT0wO3ZhciBmPWMubGFuZXN8Yy5jaGlsZExhbmVzO0JjKGEsZik7YT09PVImJihZPVI9bnVsbCxaPTApOzA9PT0oYy5zdWJ0cmVlRmxhZ3MmMjA2NCkmJjA9PT0oYy5mbGFncyYyMDY0KXx8d2t8fCh3az0hMCxHayhoYyxmdW5jdGlvbigpe0lrKCk7cmV0dXJuIG51bGx9KSk7Zj0wIT09KGMuZmxhZ3MmMTU5OTApO2lmKDAhPT0oYy5zdWJ0cmVlRmxhZ3MmMTU5OTApfHxmKXtmPXBrLnRyYW5zaXRpb247cGsudHJhbnNpdGlvbj1udWxsOwp2YXIgZz1DO0M9MTt2YXIgaD1LO0t8PTQ7b2suY3VycmVudD1udWxsO1BqKGEsYyk7ZWsoYyxhKTtPZShEZik7ZGQ9ISFDZjtEZj1DZj1udWxsO2EuY3VycmVudD1jO2lrKGMsYSxlKTtkYygpO0s9aDtDPWc7cGsudHJhbnNpdGlvbj1mfWVsc2UgYS5jdXJyZW50PWM7d2smJih3az0hMSx4az1hLHlrPWUpO2Y9YS5wZW5kaW5nTGFuZXM7MD09PWYmJihTaT1udWxsKTttYyhjLnN0YXRlTm9kZSxkKTtFayhhLEIoKSk7aWYobnVsbCE9PWIpZm9yKGQ9YS5vblJlY292ZXJhYmxlRXJyb3IsYz0wO2M8Yi5sZW5ndGg7YysrKWU9YltjXSxkKGUudmFsdWUse2NvbXBvbmVudFN0YWNrOmUuc3RhY2ssZGlnZXN0OmUuZGlnZXN0fSk7aWYoUGkpdGhyb3cgUGk9ITEsYT1RaSxRaT1udWxsLGE7MCE9PSh5ayYxKSYmMCE9PWEudGFnJiZJaygpO2Y9YS5wZW5kaW5nTGFuZXM7MCE9PShmJjEpP2E9PT1Baz96aysrOih6az0wLEFrPWEpOnprPTA7amcoKTtyZXR1cm4gbnVsbH0KZnVuY3Rpb24gSWsoKXtpZihudWxsIT09eGspe3ZhciBhPURjKHlrKSxiPXBrLnRyYW5zaXRpb24sYz1DO3RyeXtway50cmFuc2l0aW9uPW51bGw7Qz0xNj5hPzE2OmE7aWYobnVsbD09PXhrKXZhciBkPSExO2Vsc2V7YT14azt4az1udWxsO3lrPTA7aWYoMCE9PShLJjYpKXRocm93IEVycm9yKHAoMzMxKSk7dmFyIGU9SztLfD00O2ZvcihWPWEuY3VycmVudDtudWxsIT09Vjspe3ZhciBmPVYsZz1mLmNoaWxkO2lmKDAhPT0oVi5mbGFncyYxNikpe3ZhciBoPWYuZGVsZXRpb25zO2lmKG51bGwhPT1oKXtmb3IodmFyIGs9MDtrPGgubGVuZ3RoO2srKyl7dmFyIGw9aFtrXTtmb3IoVj1sO251bGwhPT1WOyl7dmFyIG09Vjtzd2l0Y2gobS50YWcpe2Nhc2UgMDpjYXNlIDExOmNhc2UgMTU6UWooOCxtLGYpfXZhciBxPW0uY2hpbGQ7aWYobnVsbCE9PXEpcS5yZXR1cm49bSxWPXE7ZWxzZSBmb3IoO251bGwhPT1WOyl7bT1WO3ZhciByPW0uc2libGluZyx5PW0ucmV0dXJuO1RqKG0pO2lmKG09PT0KbCl7Vj1udWxsO2JyZWFrfWlmKG51bGwhPT1yKXtyLnJldHVybj15O1Y9cjticmVha31WPXl9fX12YXIgbj1mLmFsdGVybmF0ZTtpZihudWxsIT09bil7dmFyIHQ9bi5jaGlsZDtpZihudWxsIT09dCl7bi5jaGlsZD1udWxsO2Rve3ZhciBKPXQuc2libGluZzt0LnNpYmxpbmc9bnVsbDt0PUp9d2hpbGUobnVsbCE9PXQpfX1WPWZ9fWlmKDAhPT0oZi5zdWJ0cmVlRmxhZ3MmMjA2NCkmJm51bGwhPT1nKWcucmV0dXJuPWYsVj1nO2Vsc2UgYjpmb3IoO251bGwhPT1WOyl7Zj1WO2lmKDAhPT0oZi5mbGFncyYyMDQ4KSlzd2l0Y2goZi50YWcpe2Nhc2UgMDpjYXNlIDExOmNhc2UgMTU6UWooOSxmLGYucmV0dXJuKX12YXIgeD1mLnNpYmxpbmc7aWYobnVsbCE9PXgpe3gucmV0dXJuPWYucmV0dXJuO1Y9eDticmVhayBifVY9Zi5yZXR1cm59fXZhciB3PWEuY3VycmVudDtmb3IoVj13O251bGwhPT1WOyl7Zz1WO3ZhciB1PWcuY2hpbGQ7aWYoMCE9PShnLnN1YnRyZWVGbGFncyYyMDY0KSYmbnVsbCE9PQp1KXUucmV0dXJuPWcsVj11O2Vsc2UgYjpmb3IoZz13O251bGwhPT1WOyl7aD1WO2lmKDAhPT0oaC5mbGFncyYyMDQ4KSl0cnl7c3dpdGNoKGgudGFnKXtjYXNlIDA6Y2FzZSAxMTpjYXNlIDE1OlJqKDksaCl9fWNhdGNoKG5hKXtXKGgsaC5yZXR1cm4sbmEpfWlmKGg9PT1nKXtWPW51bGw7YnJlYWsgYn12YXIgRj1oLnNpYmxpbmc7aWYobnVsbCE9PUYpe0YucmV0dXJuPWgucmV0dXJuO1Y9RjticmVhayBifVY9aC5yZXR1cm59fUs9ZTtqZygpO2lmKGxjJiYiZnVuY3Rpb24iPT09dHlwZW9mIGxjLm9uUG9zdENvbW1pdEZpYmVyUm9vdCl0cnl7bGMub25Qb3N0Q29tbWl0RmliZXJSb290KGtjLGEpfWNhdGNoKG5hKXt9ZD0hMH1yZXR1cm4gZH1maW5hbGx5e0M9Yyxway50cmFuc2l0aW9uPWJ9fXJldHVybiExfWZ1bmN0aW9uIFlrKGEsYixjKXtiPUtpKGMsYik7Yj1PaShhLGIsMSk7YT1kaChhLGIsMSk7Yj1MKCk7bnVsbCE9PWEmJihBYyhhLDEsYiksRWsoYSxiKSl9CmZ1bmN0aW9uIFcoYSxiLGMpe2lmKDM9PT1hLnRhZylZayhhLGEsYyk7ZWxzZSBmb3IoO251bGwhPT1iOyl7aWYoMz09PWIudGFnKXtZayhiLGEsYyk7YnJlYWt9ZWxzZSBpZigxPT09Yi50YWcpe3ZhciBkPWIuc3RhdGVOb2RlO2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgYi50eXBlLmdldERlcml2ZWRTdGF0ZUZyb21FcnJvcnx8ImZ1bmN0aW9uIj09PXR5cGVvZiBkLmNvbXBvbmVudERpZENhdGNoJiYobnVsbD09PVNpfHwhU2kuaGFzKGQpKSl7YT1LaShjLGEpO2E9UmkoYixhLDEpO2I9ZGgoYixhLDEpO2E9TCgpO251bGwhPT1iJiYoQWMoYiwxLGEpLEVrKGIsYSkpO2JyZWFrfX1iPWIucmV0dXJufX0KZnVuY3Rpb24gVWkoYSxiLGMpe3ZhciBkPWEucGluZ0NhY2hlO251bGwhPT1kJiZkLmRlbGV0ZShiKTtiPUwoKTthLnBpbmdlZExhbmVzfD1hLnN1c3BlbmRlZExhbmVzJmM7Uj09PWEmJihaJmMpPT09YyYmKDQ9PT1UfHwzPT09VCYmKFomMTMwMDIzNDI0KT09PVomJjUwMD5CKCktZ2s/TGsoYSwwKTpza3w9Yyk7RWsoYSxiKX1mdW5jdGlvbiBaayhhLGIpezA9PT1iJiYoMD09PShhLm1vZGUmMSk/Yj0xOihiPXNjLHNjPDw9MSwwPT09KHNjJjEzMDAyMzQyNCkmJihzYz00MTk0MzA0KSkpO3ZhciBjPUwoKTthPVpnKGEsYik7bnVsbCE9PWEmJihBYyhhLGIsYyksRWsoYSxjKSl9ZnVuY3Rpb24gdmooYSl7dmFyIGI9YS5tZW1vaXplZFN0YXRlLGM9MDtudWxsIT09YiYmKGM9Yi5yZXRyeUxhbmUpO1prKGEsYyl9CmZ1bmN0aW9uIGNrKGEsYil7dmFyIGM9MDtzd2l0Y2goYS50YWcpe2Nhc2UgMTM6dmFyIGQ9YS5zdGF0ZU5vZGU7dmFyIGU9YS5tZW1vaXplZFN0YXRlO251bGwhPT1lJiYoYz1lLnJldHJ5TGFuZSk7YnJlYWs7Y2FzZSAxOTpkPWEuc3RhdGVOb2RlO2JyZWFrO2RlZmF1bHQ6dGhyb3cgRXJyb3IocCgzMTQpKTt9bnVsbCE9PWQmJmQuZGVsZXRlKGIpO1prKGEsYyl9dmFyIFdrOwpXaz1mdW5jdGlvbihhLGIsYyl7aWYobnVsbCE9PWEpaWYoYS5tZW1vaXplZFByb3BzIT09Yi5wZW5kaW5nUHJvcHN8fFdmLmN1cnJlbnQpVWc9ITA7ZWxzZXtpZigwPT09KGEubGFuZXMmYykmJjA9PT0oYi5mbGFncyYxMjgpKXJldHVybiBVZz0hMSx6aihhLGIsYyk7VWc9MCE9PShhLmZsYWdzJjEzMTA3Mik/ITA6ITF9ZWxzZSBVZz0hMSxJJiYwIT09KGIuZmxhZ3MmMTA0ODU3NikmJnVnKGIsbmcsYi5pbmRleCk7Yi5sYW5lcz0wO3N3aXRjaChiLnRhZyl7Y2FzZSAyOnZhciBkPWIudHlwZTtqaihhLGIpO2E9Yi5wZW5kaW5nUHJvcHM7dmFyIGU9WWYoYixILmN1cnJlbnQpO1RnKGIsYyk7ZT1YaChudWxsLGIsZCxhLGUsYyk7dmFyIGY9YmkoKTtiLmZsYWdzfD0xOyJvYmplY3QiPT09dHlwZW9mIGUmJm51bGwhPT1lJiYiZnVuY3Rpb24iPT09dHlwZW9mIGUucmVuZGVyJiZ2b2lkIDA9PT1lLiQkdHlwZW9mPyhiLnRhZz0xLGIubWVtb2l6ZWRTdGF0ZT1udWxsLGIudXBkYXRlUXVldWU9Cm51bGwsWmYoZCk/KGY9ITAsY2coYikpOmY9ITEsYi5tZW1vaXplZFN0YXRlPW51bGwhPT1lLnN0YXRlJiZ2b2lkIDAhPT1lLnN0YXRlP2Uuc3RhdGU6bnVsbCxhaChiKSxlLnVwZGF0ZXI9bmgsYi5zdGF0ZU5vZGU9ZSxlLl9yZWFjdEludGVybmFscz1iLHJoKGIsZCxhLGMpLGI9a2oobnVsbCxiLGQsITAsZixjKSk6KGIudGFnPTAsSSYmZiYmdmcoYiksWWkobnVsbCxiLGUsYyksYj1iLmNoaWxkKTtyZXR1cm4gYjtjYXNlIDE2OmQ9Yi5lbGVtZW50VHlwZTthOntqaihhLGIpO2E9Yi5wZW5kaW5nUHJvcHM7ZT1kLl9pbml0O2Q9ZShkLl9wYXlsb2FkKTtiLnR5cGU9ZDtlPWIudGFnPSRrKGQpO2E9TGcoZCxhKTtzd2l0Y2goZSl7Y2FzZSAwOmI9ZGoobnVsbCxiLGQsYSxjKTticmVhayBhO2Nhc2UgMTpiPWlqKG51bGwsYixkLGEsYyk7YnJlYWsgYTtjYXNlIDExOmI9WmkobnVsbCxiLGQsYSxjKTticmVhayBhO2Nhc2UgMTQ6Yj1haihudWxsLGIsZCxMZyhkLnR5cGUsYSksYyk7YnJlYWsgYX10aHJvdyBFcnJvcihwKDMwNiwKZCwiIikpO31yZXR1cm4gYjtjYXNlIDA6cmV0dXJuIGQ9Yi50eXBlLGU9Yi5wZW5kaW5nUHJvcHMsZT1iLmVsZW1lbnRUeXBlPT09ZD9lOkxnKGQsZSksZGooYSxiLGQsZSxjKTtjYXNlIDE6cmV0dXJuIGQ9Yi50eXBlLGU9Yi5wZW5kaW5nUHJvcHMsZT1iLmVsZW1lbnRUeXBlPT09ZD9lOkxnKGQsZSksaWooYSxiLGQsZSxjKTtjYXNlIDM6YTp7bGooYik7aWYobnVsbD09PWEpdGhyb3cgRXJyb3IocCgzODcpKTtkPWIucGVuZGluZ1Byb3BzO2Y9Yi5tZW1vaXplZFN0YXRlO2U9Zi5lbGVtZW50O2JoKGEsYik7Z2goYixkLG51bGwsYyk7dmFyIGc9Yi5tZW1vaXplZFN0YXRlO2Q9Zy5lbGVtZW50O2lmKGYuaXNEZWh5ZHJhdGVkKWlmKGY9e2VsZW1lbnQ6ZCxpc0RlaHlkcmF0ZWQ6ITEsY2FjaGU6Zy5jYWNoZSxwZW5kaW5nU3VzcGVuc2VCb3VuZGFyaWVzOmcucGVuZGluZ1N1c3BlbnNlQm91bmRhcmllcyx0cmFuc2l0aW9uczpnLnRyYW5zaXRpb25zfSxiLnVwZGF0ZVF1ZXVlLmJhc2VTdGF0ZT0KZixiLm1lbW9pemVkU3RhdGU9ZixiLmZsYWdzJjI1Nil7ZT1LaShFcnJvcihwKDQyMykpLGIpO2I9bWooYSxiLGQsYyxlKTticmVhayBhfWVsc2UgaWYoZCE9PWUpe2U9S2koRXJyb3IocCg0MjQpKSxiKTtiPW1qKGEsYixkLGMsZSk7YnJlYWsgYX1lbHNlIGZvcih5Zz1MZihiLnN0YXRlTm9kZS5jb250YWluZXJJbmZvLmZpcnN0Q2hpbGQpLHhnPWIsST0hMCx6Zz1udWxsLGM9Q2goYixudWxsLGQsYyksYi5jaGlsZD1jO2M7KWMuZmxhZ3M9Yy5mbGFncyYtM3w0MDk2LGM9Yy5zaWJsaW5nO2Vsc2V7SWcoKTtpZihkPT09ZSl7Yj0kaShhLGIsYyk7YnJlYWsgYX1ZaShhLGIsZCxjKX1iPWIuY2hpbGR9cmV0dXJuIGI7Y2FzZSA1OnJldHVybiBLaChiKSxudWxsPT09YSYmRWcoYiksZD1iLnR5cGUsZT1iLnBlbmRpbmdQcm9wcyxmPW51bGwhPT1hP2EubWVtb2l6ZWRQcm9wczpudWxsLGc9ZS5jaGlsZHJlbixFZihkLGUpP2c9bnVsbDpudWxsIT09ZiYmRWYoZCxmKSYmKGIuZmxhZ3N8PTMyKSwKaGooYSxiKSxZaShhLGIsZyxjKSxiLmNoaWxkO2Nhc2UgNjpyZXR1cm4gbnVsbD09PWEmJkVnKGIpLG51bGw7Y2FzZSAxMzpyZXR1cm4gcGooYSxiLGMpO2Nhc2UgNDpyZXR1cm4gSWgoYixiLnN0YXRlTm9kZS5jb250YWluZXJJbmZvKSxkPWIucGVuZGluZ1Byb3BzLG51bGw9PT1hP2IuY2hpbGQ9QmgoYixudWxsLGQsYyk6WWkoYSxiLGQsYyksYi5jaGlsZDtjYXNlIDExOnJldHVybiBkPWIudHlwZSxlPWIucGVuZGluZ1Byb3BzLGU9Yi5lbGVtZW50VHlwZT09PWQ/ZTpMZyhkLGUpLFppKGEsYixkLGUsYyk7Y2FzZSA3OnJldHVybiBZaShhLGIsYi5wZW5kaW5nUHJvcHMsYyksYi5jaGlsZDtjYXNlIDg6cmV0dXJuIFlpKGEsYixiLnBlbmRpbmdQcm9wcy5jaGlsZHJlbixjKSxiLmNoaWxkO2Nhc2UgMTI6cmV0dXJuIFlpKGEsYixiLnBlbmRpbmdQcm9wcy5jaGlsZHJlbixjKSxiLmNoaWxkO2Nhc2UgMTA6YTp7ZD1iLnR5cGUuX2NvbnRleHQ7ZT1iLnBlbmRpbmdQcm9wcztmPWIubWVtb2l6ZWRQcm9wczsKZz1lLnZhbHVlO0coTWcsZC5fY3VycmVudFZhbHVlKTtkLl9jdXJyZW50VmFsdWU9ZztpZihudWxsIT09ZilpZihIZShmLnZhbHVlLGcpKXtpZihmLmNoaWxkcmVuPT09ZS5jaGlsZHJlbiYmIVdmLmN1cnJlbnQpe2I9JGkoYSxiLGMpO2JyZWFrIGF9fWVsc2UgZm9yKGY9Yi5jaGlsZCxudWxsIT09ZiYmKGYucmV0dXJuPWIpO251bGwhPT1mOyl7dmFyIGg9Zi5kZXBlbmRlbmNpZXM7aWYobnVsbCE9PWgpe2c9Zi5jaGlsZDtmb3IodmFyIGs9aC5maXJzdENvbnRleHQ7bnVsbCE9PWs7KXtpZihrLmNvbnRleHQ9PT1kKXtpZigxPT09Zi50YWcpe2s9Y2goLTEsYyYtYyk7ay50YWc9Mjt2YXIgbD1mLnVwZGF0ZVF1ZXVlO2lmKG51bGwhPT1sKXtsPWwuc2hhcmVkO3ZhciBtPWwucGVuZGluZztudWxsPT09bT9rLm5leHQ9azooay5uZXh0PW0ubmV4dCxtLm5leHQ9ayk7bC5wZW5kaW5nPWt9fWYubGFuZXN8PWM7az1mLmFsdGVybmF0ZTtudWxsIT09ayYmKGsubGFuZXN8PWMpO1NnKGYucmV0dXJuLApjLGIpO2gubGFuZXN8PWM7YnJlYWt9az1rLm5leHR9fWVsc2UgaWYoMTA9PT1mLnRhZylnPWYudHlwZT09PWIudHlwZT9udWxsOmYuY2hpbGQ7ZWxzZSBpZigxOD09PWYudGFnKXtnPWYucmV0dXJuO2lmKG51bGw9PT1nKXRocm93IEVycm9yKHAoMzQxKSk7Zy5sYW5lc3w9YztoPWcuYWx0ZXJuYXRlO251bGwhPT1oJiYoaC5sYW5lc3w9Yyk7U2coZyxjLGIpO2c9Zi5zaWJsaW5nfWVsc2UgZz1mLmNoaWxkO2lmKG51bGwhPT1nKWcucmV0dXJuPWY7ZWxzZSBmb3IoZz1mO251bGwhPT1nOyl7aWYoZz09PWIpe2c9bnVsbDticmVha31mPWcuc2libGluZztpZihudWxsIT09Zil7Zi5yZXR1cm49Zy5yZXR1cm47Zz1mO2JyZWFrfWc9Zy5yZXR1cm59Zj1nfVlpKGEsYixlLmNoaWxkcmVuLGMpO2I9Yi5jaGlsZH1yZXR1cm4gYjtjYXNlIDk6cmV0dXJuIGU9Yi50eXBlLGQ9Yi5wZW5kaW5nUHJvcHMuY2hpbGRyZW4sVGcoYixjKSxlPVZnKGUpLGQ9ZChlKSxiLmZsYWdzfD0xLFlpKGEsYixkLGMpLApiLmNoaWxkO2Nhc2UgMTQ6cmV0dXJuIGQ9Yi50eXBlLGU9TGcoZCxiLnBlbmRpbmdQcm9wcyksZT1MZyhkLnR5cGUsZSksYWooYSxiLGQsZSxjKTtjYXNlIDE1OnJldHVybiBjaihhLGIsYi50eXBlLGIucGVuZGluZ1Byb3BzLGMpO2Nhc2UgMTc6cmV0dXJuIGQ9Yi50eXBlLGU9Yi5wZW5kaW5nUHJvcHMsZT1iLmVsZW1lbnRUeXBlPT09ZD9lOkxnKGQsZSksamooYSxiKSxiLnRhZz0xLFpmKGQpPyhhPSEwLGNnKGIpKTphPSExLFRnKGIsYykscGgoYixkLGUpLHJoKGIsZCxlLGMpLGtqKG51bGwsYixkLCEwLGEsYyk7Y2FzZSAxOTpyZXR1cm4geWooYSxiLGMpO2Nhc2UgMjI6cmV0dXJuIGVqKGEsYixjKX10aHJvdyBFcnJvcihwKDE1NixiLnRhZykpO307ZnVuY3Rpb24gR2soYSxiKXtyZXR1cm4gYWMoYSxiKX0KZnVuY3Rpb24gYWwoYSxiLGMsZCl7dGhpcy50YWc9YTt0aGlzLmtleT1jO3RoaXMuc2libGluZz10aGlzLmNoaWxkPXRoaXMucmV0dXJuPXRoaXMuc3RhdGVOb2RlPXRoaXMudHlwZT10aGlzLmVsZW1lbnRUeXBlPW51bGw7dGhpcy5pbmRleD0wO3RoaXMucmVmPW51bGw7dGhpcy5wZW5kaW5nUHJvcHM9Yjt0aGlzLmRlcGVuZGVuY2llcz10aGlzLm1lbW9pemVkU3RhdGU9dGhpcy51cGRhdGVRdWV1ZT10aGlzLm1lbW9pemVkUHJvcHM9bnVsbDt0aGlzLm1vZGU9ZDt0aGlzLnN1YnRyZWVGbGFncz10aGlzLmZsYWdzPTA7dGhpcy5kZWxldGlvbnM9bnVsbDt0aGlzLmNoaWxkTGFuZXM9dGhpcy5sYW5lcz0wO3RoaXMuYWx0ZXJuYXRlPW51bGx9ZnVuY3Rpb24gQmcoYSxiLGMsZCl7cmV0dXJuIG5ldyBhbChhLGIsYyxkKX1mdW5jdGlvbiBiaihhKXthPWEucHJvdG90eXBlO3JldHVybiEoIWF8fCFhLmlzUmVhY3RDb21wb25lbnQpfQpmdW5jdGlvbiAkayhhKXtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGEpcmV0dXJuIGJqKGEpPzE6MDtpZih2b2lkIDAhPT1hJiZudWxsIT09YSl7YT1hLiQkdHlwZW9mO2lmKGE9PT1EYSlyZXR1cm4gMTE7aWYoYT09PUdhKXJldHVybiAxNH1yZXR1cm4gMn0KZnVuY3Rpb24gd2goYSxiKXt2YXIgYz1hLmFsdGVybmF0ZTtudWxsPT09Yz8oYz1CZyhhLnRhZyxiLGEua2V5LGEubW9kZSksYy5lbGVtZW50VHlwZT1hLmVsZW1lbnRUeXBlLGMudHlwZT1hLnR5cGUsYy5zdGF0ZU5vZGU9YS5zdGF0ZU5vZGUsYy5hbHRlcm5hdGU9YSxhLmFsdGVybmF0ZT1jKTooYy5wZW5kaW5nUHJvcHM9YixjLnR5cGU9YS50eXBlLGMuZmxhZ3M9MCxjLnN1YnRyZWVGbGFncz0wLGMuZGVsZXRpb25zPW51bGwpO2MuZmxhZ3M9YS5mbGFncyYxNDY4MDA2NDtjLmNoaWxkTGFuZXM9YS5jaGlsZExhbmVzO2MubGFuZXM9YS5sYW5lcztjLmNoaWxkPWEuY2hpbGQ7Yy5tZW1vaXplZFByb3BzPWEubWVtb2l6ZWRQcm9wcztjLm1lbW9pemVkU3RhdGU9YS5tZW1vaXplZFN0YXRlO2MudXBkYXRlUXVldWU9YS51cGRhdGVRdWV1ZTtiPWEuZGVwZW5kZW5jaWVzO2MuZGVwZW5kZW5jaWVzPW51bGw9PT1iP251bGw6e2xhbmVzOmIubGFuZXMsZmlyc3RDb250ZXh0OmIuZmlyc3RDb250ZXh0fTsKYy5zaWJsaW5nPWEuc2libGluZztjLmluZGV4PWEuaW5kZXg7Yy5yZWY9YS5yZWY7cmV0dXJuIGN9CmZ1bmN0aW9uIHloKGEsYixjLGQsZSxmKXt2YXIgZz0yO2Q9YTtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGEpYmooYSkmJihnPTEpO2Vsc2UgaWYoInN0cmluZyI9PT10eXBlb2YgYSlnPTU7ZWxzZSBhOnN3aXRjaChhKXtjYXNlIHlhOnJldHVybiBBaChjLmNoaWxkcmVuLGUsZixiKTtjYXNlIHphOmc9ODtlfD04O2JyZWFrO2Nhc2UgQWE6cmV0dXJuIGE9QmcoMTIsYyxiLGV8MiksYS5lbGVtZW50VHlwZT1BYSxhLmxhbmVzPWYsYTtjYXNlIEVhOnJldHVybiBhPUJnKDEzLGMsYixlKSxhLmVsZW1lbnRUeXBlPUVhLGEubGFuZXM9ZixhO2Nhc2UgRmE6cmV0dXJuIGE9QmcoMTksYyxiLGUpLGEuZWxlbWVudFR5cGU9RmEsYS5sYW5lcz1mLGE7Y2FzZSBJYTpyZXR1cm4gcWooYyxlLGYsYik7ZGVmYXVsdDppZigib2JqZWN0Ij09PXR5cGVvZiBhJiZudWxsIT09YSlzd2l0Y2goYS4kJHR5cGVvZil7Y2FzZSBCYTpnPTEwO2JyZWFrIGE7Y2FzZSBDYTpnPTk7YnJlYWsgYTtjYXNlIERhOmc9MTE7CmJyZWFrIGE7Y2FzZSBHYTpnPTE0O2JyZWFrIGE7Y2FzZSBIYTpnPTE2O2Q9bnVsbDticmVhayBhfXRocm93IEVycm9yKHAoMTMwLG51bGw9PWE/YTp0eXBlb2YgYSwiIikpO31iPUJnKGcsYyxiLGUpO2IuZWxlbWVudFR5cGU9YTtiLnR5cGU9ZDtiLmxhbmVzPWY7cmV0dXJuIGJ9ZnVuY3Rpb24gQWgoYSxiLGMsZCl7YT1CZyg3LGEsZCxiKTthLmxhbmVzPWM7cmV0dXJuIGF9ZnVuY3Rpb24gcWooYSxiLGMsZCl7YT1CZygyMixhLGQsYik7YS5lbGVtZW50VHlwZT1JYTthLmxhbmVzPWM7YS5zdGF0ZU5vZGU9e2lzSGlkZGVuOiExfTtyZXR1cm4gYX1mdW5jdGlvbiB4aChhLGIsYyl7YT1CZyg2LGEsbnVsbCxiKTthLmxhbmVzPWM7cmV0dXJuIGF9CmZ1bmN0aW9uIHpoKGEsYixjKXtiPUJnKDQsbnVsbCE9PWEuY2hpbGRyZW4/YS5jaGlsZHJlbjpbXSxhLmtleSxiKTtiLmxhbmVzPWM7Yi5zdGF0ZU5vZGU9e2NvbnRhaW5lckluZm86YS5jb250YWluZXJJbmZvLHBlbmRpbmdDaGlsZHJlbjpudWxsLGltcGxlbWVudGF0aW9uOmEuaW1wbGVtZW50YXRpb259O3JldHVybiBifQpmdW5jdGlvbiBibChhLGIsYyxkLGUpe3RoaXMudGFnPWI7dGhpcy5jb250YWluZXJJbmZvPWE7dGhpcy5maW5pc2hlZFdvcms9dGhpcy5waW5nQ2FjaGU9dGhpcy5jdXJyZW50PXRoaXMucGVuZGluZ0NoaWxkcmVuPW51bGw7dGhpcy50aW1lb3V0SGFuZGxlPS0xO3RoaXMuY2FsbGJhY2tOb2RlPXRoaXMucGVuZGluZ0NvbnRleHQ9dGhpcy5jb250ZXh0PW51bGw7dGhpcy5jYWxsYmFja1ByaW9yaXR5PTA7dGhpcy5ldmVudFRpbWVzPXpjKDApO3RoaXMuZXhwaXJhdGlvblRpbWVzPXpjKC0xKTt0aGlzLmVudGFuZ2xlZExhbmVzPXRoaXMuZmluaXNoZWRMYW5lcz10aGlzLm11dGFibGVSZWFkTGFuZXM9dGhpcy5leHBpcmVkTGFuZXM9dGhpcy5waW5nZWRMYW5lcz10aGlzLnN1c3BlbmRlZExhbmVzPXRoaXMucGVuZGluZ0xhbmVzPTA7dGhpcy5lbnRhbmdsZW1lbnRzPXpjKDApO3RoaXMuaWRlbnRpZmllclByZWZpeD1kO3RoaXMub25SZWNvdmVyYWJsZUVycm9yPWU7dGhpcy5tdXRhYmxlU291cmNlRWFnZXJIeWRyYXRpb25EYXRhPQpudWxsfWZ1bmN0aW9uIGNsKGEsYixjLGQsZSxmLGcsaCxrKXthPW5ldyBibChhLGIsYyxoLGspOzE9PT1iPyhiPTEsITA9PT1mJiYoYnw9OCkpOmI9MDtmPUJnKDMsbnVsbCxudWxsLGIpO2EuY3VycmVudD1mO2Yuc3RhdGVOb2RlPWE7Zi5tZW1vaXplZFN0YXRlPXtlbGVtZW50OmQsaXNEZWh5ZHJhdGVkOmMsY2FjaGU6bnVsbCx0cmFuc2l0aW9uczpudWxsLHBlbmRpbmdTdXNwZW5zZUJvdW5kYXJpZXM6bnVsbH07YWgoZik7cmV0dXJuIGF9ZnVuY3Rpb24gZGwoYSxiLGMpe3ZhciBkPTM8YXJndW1lbnRzLmxlbmd0aCYmdm9pZCAwIT09YXJndW1lbnRzWzNdP2FyZ3VtZW50c1szXTpudWxsO3JldHVybnskJHR5cGVvZjp3YSxrZXk6bnVsbD09ZD9udWxsOiIiK2QsY2hpbGRyZW46YSxjb250YWluZXJJbmZvOmIsaW1wbGVtZW50YXRpb246Y319CmZ1bmN0aW9uIGVsKGEpe2lmKCFhKXJldHVybiBWZjthPWEuX3JlYWN0SW50ZXJuYWxzO2E6e2lmKFZiKGEpIT09YXx8MSE9PWEudGFnKXRocm93IEVycm9yKHAoMTcwKSk7dmFyIGI9YTtkb3tzd2l0Y2goYi50YWcpe2Nhc2UgMzpiPWIuc3RhdGVOb2RlLmNvbnRleHQ7YnJlYWsgYTtjYXNlIDE6aWYoWmYoYi50eXBlKSl7Yj1iLnN0YXRlTm9kZS5fX3JlYWN0SW50ZXJuYWxNZW1vaXplZE1lcmdlZENoaWxkQ29udGV4dDticmVhayBhfX1iPWIucmV0dXJufXdoaWxlKG51bGwhPT1iKTt0aHJvdyBFcnJvcihwKDE3MSkpO31pZigxPT09YS50YWcpe3ZhciBjPWEudHlwZTtpZihaZihjKSlyZXR1cm4gYmcoYSxjLGIpfXJldHVybiBifQpmdW5jdGlvbiBmbChhLGIsYyxkLGUsZixnLGgsayl7YT1jbChjLGQsITAsYSxlLGYsZyxoLGspO2EuY29udGV4dD1lbChudWxsKTtjPWEuY3VycmVudDtkPUwoKTtlPWxoKGMpO2Y9Y2goZCxlKTtmLmNhbGxiYWNrPXZvaWQgMCE9PWImJm51bGwhPT1iP2I6bnVsbDtkaChjLGYsZSk7YS5jdXJyZW50LmxhbmVzPWU7QWMoYSxlLGQpO0VrKGEsZCk7cmV0dXJuIGF9ZnVuY3Rpb24gZ2woYSxiLGMsZCl7dmFyIGU9Yi5jdXJyZW50LGY9TCgpLGc9bGgoZSk7Yz1lbChjKTtudWxsPT09Yi5jb250ZXh0P2IuY29udGV4dD1jOmIucGVuZGluZ0NvbnRleHQ9YztiPWNoKGYsZyk7Yi5wYXlsb2FkPXtlbGVtZW50OmF9O2Q9dm9pZCAwPT09ZD9udWxsOmQ7bnVsbCE9PWQmJihiLmNhbGxiYWNrPWQpO2E9ZGgoZSxiLGcpO251bGwhPT1hJiYobWgoYSxlLGcsZiksZWgoYSxlLGcpKTtyZXR1cm4gZ30KZnVuY3Rpb24gaGwoYSl7YT1hLmN1cnJlbnQ7aWYoIWEuY2hpbGQpcmV0dXJuIG51bGw7c3dpdGNoKGEuY2hpbGQudGFnKXtjYXNlIDU6cmV0dXJuIGEuY2hpbGQuc3RhdGVOb2RlO2RlZmF1bHQ6cmV0dXJuIGEuY2hpbGQuc3RhdGVOb2RlfX1mdW5jdGlvbiBpbChhLGIpe2E9YS5tZW1vaXplZFN0YXRlO2lmKG51bGwhPT1hJiZudWxsIT09YS5kZWh5ZHJhdGVkKXt2YXIgYz1hLnJldHJ5TGFuZTthLnJldHJ5TGFuZT0wIT09YyYmYzxiP2M6Yn19ZnVuY3Rpb24gamwoYSxiKXtpbChhLGIpOyhhPWEuYWx0ZXJuYXRlKSYmaWwoYSxiKX1mdW5jdGlvbiBrbCgpe3JldHVybiBudWxsfXZhciBsbD0iZnVuY3Rpb24iPT09dHlwZW9mIHJlcG9ydEVycm9yP3JlcG9ydEVycm9yOmZ1bmN0aW9uKGEpe2NvbnNvbGUuZXJyb3IoYSl9O2Z1bmN0aW9uIG1sKGEpe3RoaXMuX2ludGVybmFsUm9vdD1hfQpubC5wcm90b3R5cGUucmVuZGVyPW1sLnByb3RvdHlwZS5yZW5kZXI9ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5faW50ZXJuYWxSb290O2lmKG51bGw9PT1iKXRocm93IEVycm9yKHAoNDA5KSk7Z2woYSxiLG51bGwsbnVsbCl9O25sLnByb3RvdHlwZS51bm1vdW50PW1sLnByb3RvdHlwZS51bm1vdW50PWZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5faW50ZXJuYWxSb290O2lmKG51bGwhPT1hKXt0aGlzLl9pbnRlcm5hbFJvb3Q9bnVsbDt2YXIgYj1hLmNvbnRhaW5lckluZm87U2soZnVuY3Rpb24oKXtnbChudWxsLGEsbnVsbCxudWxsKX0pO2JbdWZdPW51bGx9fTtmdW5jdGlvbiBubChhKXt0aGlzLl9pbnRlcm5hbFJvb3Q9YX0KbmwucHJvdG90eXBlLnVuc3RhYmxlX3NjaGVkdWxlSHlkcmF0aW9uPWZ1bmN0aW9uKGEpe2lmKGEpe3ZhciBiPUhjKCk7YT17YmxvY2tlZE9uOm51bGwsdGFyZ2V0OmEscHJpb3JpdHk6Yn07Zm9yKHZhciBjPTA7YzxRYy5sZW5ndGgmJjAhPT1iJiZiPFFjW2NdLnByaW9yaXR5O2MrKyk7UWMuc3BsaWNlKGMsMCxhKTswPT09YyYmVmMoYSl9fTtmdW5jdGlvbiBvbChhKXtyZXR1cm4hKCFhfHwxIT09YS5ub2RlVHlwZSYmOSE9PWEubm9kZVR5cGUmJjExIT09YS5ub2RlVHlwZSl9ZnVuY3Rpb24gcGwoYSl7cmV0dXJuISghYXx8MSE9PWEubm9kZVR5cGUmJjkhPT1hLm5vZGVUeXBlJiYxMSE9PWEubm9kZVR5cGUmJig4IT09YS5ub2RlVHlwZXx8IiByZWFjdC1tb3VudC1wb2ludC11bnN0YWJsZSAiIT09YS5ub2RlVmFsdWUpKX1mdW5jdGlvbiBxbCgpe30KZnVuY3Rpb24gcmwoYSxiLGMsZCxlKXtpZihlKXtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGQpe3ZhciBmPWQ7ZD1mdW5jdGlvbigpe3ZhciBhPWhsKGcpO2YuY2FsbChhKX19dmFyIGc9ZmwoYixkLGEsMCxudWxsLCExLCExLCIiLHFsKTthLl9yZWFjdFJvb3RDb250YWluZXI9ZzthW3VmXT1nLmN1cnJlbnQ7c2YoOD09PWEubm9kZVR5cGU/YS5wYXJlbnROb2RlOmEpO1NrKCk7cmV0dXJuIGd9Zm9yKDtlPWEubGFzdENoaWxkOylhLnJlbW92ZUNoaWxkKGUpO2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgZCl7dmFyIGg9ZDtkPWZ1bmN0aW9uKCl7dmFyIGE9aGwoayk7aC5jYWxsKGEpfX12YXIgaz1jbChhLDAsITEsbnVsbCxudWxsLCExLCExLCIiLHFsKTthLl9yZWFjdFJvb3RDb250YWluZXI9azthW3VmXT1rLmN1cnJlbnQ7c2YoOD09PWEubm9kZVR5cGU/YS5wYXJlbnROb2RlOmEpO1NrKGZ1bmN0aW9uKCl7Z2woYixrLGMsZCl9KTtyZXR1cm4ga30KZnVuY3Rpb24gc2woYSxiLGMsZCxlKXt2YXIgZj1jLl9yZWFjdFJvb3RDb250YWluZXI7aWYoZil7dmFyIGc9ZjtpZigiZnVuY3Rpb24iPT09dHlwZW9mIGUpe3ZhciBoPWU7ZT1mdW5jdGlvbigpe3ZhciBhPWhsKGcpO2guY2FsbChhKX19Z2woYixnLGEsZSl9ZWxzZSBnPXJsKGMsYixhLGUsZCk7cmV0dXJuIGhsKGcpfUVjPWZ1bmN0aW9uKGEpe3N3aXRjaChhLnRhZyl7Y2FzZSAzOnZhciBiPWEuc3RhdGVOb2RlO2lmKGIuY3VycmVudC5tZW1vaXplZFN0YXRlLmlzRGVoeWRyYXRlZCl7dmFyIGM9dGMoYi5wZW5kaW5nTGFuZXMpOzAhPT1jJiYoQ2MoYixjfDEpLEVrKGIsQigpKSwwPT09KEsmNikmJihIaj1CKCkrNTAwLGpnKCkpKX1icmVhaztjYXNlIDEzOlNrKGZ1bmN0aW9uKCl7dmFyIGI9WmcoYSwxKTtpZihudWxsIT09Yil7dmFyIGM9TCgpO21oKGIsYSwxLGMpfX0pLGpsKGEsMSl9fTsKRmM9ZnVuY3Rpb24oYSl7aWYoMTM9PT1hLnRhZyl7dmFyIGI9WmcoYSwxMzQyMTc3MjgpO2lmKG51bGwhPT1iKXt2YXIgYz1MKCk7bWgoYixhLDEzNDIxNzcyOCxjKX1qbChhLDEzNDIxNzcyOCl9fTtHYz1mdW5jdGlvbihhKXtpZigxMz09PWEudGFnKXt2YXIgYj1saChhKSxjPVpnKGEsYik7aWYobnVsbCE9PWMpe3ZhciBkPUwoKTttaChjLGEsYixkKX1qbChhLGIpfX07SGM9ZnVuY3Rpb24oKXtyZXR1cm4gQ307SWM9ZnVuY3Rpb24oYSxiKXt2YXIgYz1DO3RyeXtyZXR1cm4gQz1hLGIoKX1maW5hbGx5e0M9Y319Owp5Yj1mdW5jdGlvbihhLGIsYyl7c3dpdGNoKGIpe2Nhc2UgImlucHV0IjpiYihhLGMpO2I9Yy5uYW1lO2lmKCJyYWRpbyI9PT1jLnR5cGUmJm51bGwhPWIpe2ZvcihjPWE7Yy5wYXJlbnROb2RlOyljPWMucGFyZW50Tm9kZTtjPWMucXVlcnlTZWxlY3RvckFsbCgiaW5wdXRbbmFtZT0iK0pTT04uc3RyaW5naWZ5KCIiK2IpKyddW3R5cGU9InJhZGlvIl0nKTtmb3IoYj0wO2I8Yy5sZW5ndGg7YisrKXt2YXIgZD1jW2JdO2lmKGQhPT1hJiZkLmZvcm09PT1hLmZvcm0pe3ZhciBlPURiKGQpO2lmKCFlKXRocm93IEVycm9yKHAoOTApKTtXYShkKTtiYihkLGUpfX19YnJlYWs7Y2FzZSAidGV4dGFyZWEiOmliKGEsYyk7YnJlYWs7Y2FzZSAic2VsZWN0IjpiPWMudmFsdWUsbnVsbCE9YiYmZmIoYSwhIWMubXVsdGlwbGUsYiwhMSl9fTtHYj1SaztIYj1TazsKdmFyIHRsPXt1c2luZ0NsaWVudEVudHJ5UG9pbnQ6ITEsRXZlbnRzOltDYix1ZSxEYixFYixGYixSa119LHVsPXtmaW5kRmliZXJCeUhvc3RJbnN0YW5jZTpXYyxidW5kbGVUeXBlOjAsdmVyc2lvbjoiMTguMi4wIixyZW5kZXJlclBhY2thZ2VOYW1lOiJyZWFjdC1kb20ifTsKdmFyIHZsPXtidW5kbGVUeXBlOnVsLmJ1bmRsZVR5cGUsdmVyc2lvbjp1bC52ZXJzaW9uLHJlbmRlcmVyUGFja2FnZU5hbWU6dWwucmVuZGVyZXJQYWNrYWdlTmFtZSxyZW5kZXJlckNvbmZpZzp1bC5yZW5kZXJlckNvbmZpZyxvdmVycmlkZUhvb2tTdGF0ZTpudWxsLG92ZXJyaWRlSG9va1N0YXRlRGVsZXRlUGF0aDpudWxsLG92ZXJyaWRlSG9va1N0YXRlUmVuYW1lUGF0aDpudWxsLG92ZXJyaWRlUHJvcHM6bnVsbCxvdmVycmlkZVByb3BzRGVsZXRlUGF0aDpudWxsLG92ZXJyaWRlUHJvcHNSZW5hbWVQYXRoOm51bGwsc2V0RXJyb3JIYW5kbGVyOm51bGwsc2V0U3VzcGVuc2VIYW5kbGVyOm51bGwsc2NoZWR1bGVVcGRhdGU6bnVsbCxjdXJyZW50RGlzcGF0Y2hlclJlZjp1YS5SZWFjdEN1cnJlbnREaXNwYXRjaGVyLGZpbmRIb3N0SW5zdGFuY2VCeUZpYmVyOmZ1bmN0aW9uKGEpe2E9WmIoYSk7cmV0dXJuIG51bGw9PT1hP251bGw6YS5zdGF0ZU5vZGV9LGZpbmRGaWJlckJ5SG9zdEluc3RhbmNlOnVsLmZpbmRGaWJlckJ5SG9zdEluc3RhbmNlfHwKa2wsZmluZEhvc3RJbnN0YW5jZXNGb3JSZWZyZXNoOm51bGwsc2NoZWR1bGVSZWZyZXNoOm51bGwsc2NoZWR1bGVSb290Om51bGwsc2V0UmVmcmVzaEhhbmRsZXI6bnVsbCxnZXRDdXJyZW50RmliZXI6bnVsbCxyZWNvbmNpbGVyVmVyc2lvbjoiMTguMi4wLW5leHQtOWUzYjc3MmI4LTIwMjIwNjA4In07aWYoInVuZGVmaW5lZCIhPT10eXBlb2YgX19SRUFDVF9ERVZUT09MU19HTE9CQUxfSE9PS19fKXt2YXIgd2w9X19SRUFDVF9ERVZUT09MU19HTE9CQUxfSE9PS19fO2lmKCF3bC5pc0Rpc2FibGVkJiZ3bC5zdXBwb3J0c0ZpYmVyKXRyeXtrYz13bC5pbmplY3QodmwpLGxjPXdsfWNhdGNoKGEpe319ZXhwb3J0cy5fX1NFQ1JFVF9JTlRFUk5BTFNfRE9fTk9UX1VTRV9PUl9ZT1VfV0lMTF9CRV9GSVJFRD10bDsKZXhwb3J0cy5jcmVhdGVQb3J0YWw9ZnVuY3Rpb24oYSxiKXt2YXIgYz0yPGFyZ3VtZW50cy5sZW5ndGgmJnZvaWQgMCE9PWFyZ3VtZW50c1syXT9hcmd1bWVudHNbMl06bnVsbDtpZighb2woYikpdGhyb3cgRXJyb3IocCgyMDApKTtyZXR1cm4gZGwoYSxiLG51bGwsYyl9O2V4cG9ydHMuY3JlYXRlUm9vdD1mdW5jdGlvbihhLGIpe2lmKCFvbChhKSl0aHJvdyBFcnJvcihwKDI5OSkpO3ZhciBjPSExLGQ9IiIsZT1sbDtudWxsIT09YiYmdm9pZCAwIT09YiYmKCEwPT09Yi51bnN0YWJsZV9zdHJpY3RNb2RlJiYoYz0hMCksdm9pZCAwIT09Yi5pZGVudGlmaWVyUHJlZml4JiYoZD1iLmlkZW50aWZpZXJQcmVmaXgpLHZvaWQgMCE9PWIub25SZWNvdmVyYWJsZUVycm9yJiYoZT1iLm9uUmVjb3ZlcmFibGVFcnJvcikpO2I9Y2woYSwxLCExLG51bGwsbnVsbCxjLCExLGQsZSk7YVt1Zl09Yi5jdXJyZW50O3NmKDg9PT1hLm5vZGVUeXBlP2EucGFyZW50Tm9kZTphKTtyZXR1cm4gbmV3IG1sKGIpfTsKZXhwb3J0cy5maW5kRE9NTm9kZT1mdW5jdGlvbihhKXtpZihudWxsPT1hKXJldHVybiBudWxsO2lmKDE9PT1hLm5vZGVUeXBlKXJldHVybiBhO3ZhciBiPWEuX3JlYWN0SW50ZXJuYWxzO2lmKHZvaWQgMD09PWIpe2lmKCJmdW5jdGlvbiI9PT10eXBlb2YgYS5yZW5kZXIpdGhyb3cgRXJyb3IocCgxODgpKTthPU9iamVjdC5rZXlzKGEpLmpvaW4oIiwiKTt0aHJvdyBFcnJvcihwKDI2OCxhKSk7fWE9WmIoYik7YT1udWxsPT09YT9udWxsOmEuc3RhdGVOb2RlO3JldHVybiBhfTtleHBvcnRzLmZsdXNoU3luYz1mdW5jdGlvbihhKXtyZXR1cm4gU2soYSl9O2V4cG9ydHMuaHlkcmF0ZT1mdW5jdGlvbihhLGIsYyl7aWYoIXBsKGIpKXRocm93IEVycm9yKHAoMjAwKSk7cmV0dXJuIHNsKG51bGwsYSxiLCEwLGMpfTsKZXhwb3J0cy5oeWRyYXRlUm9vdD1mdW5jdGlvbihhLGIsYyl7aWYoIW9sKGEpKXRocm93IEVycm9yKHAoNDA1KSk7dmFyIGQ9bnVsbCE9YyYmYy5oeWRyYXRlZFNvdXJjZXN8fG51bGwsZT0hMSxmPSIiLGc9bGw7bnVsbCE9PWMmJnZvaWQgMCE9PWMmJighMD09PWMudW5zdGFibGVfc3RyaWN0TW9kZSYmKGU9ITApLHZvaWQgMCE9PWMuaWRlbnRpZmllclByZWZpeCYmKGY9Yy5pZGVudGlmaWVyUHJlZml4KSx2b2lkIDAhPT1jLm9uUmVjb3ZlcmFibGVFcnJvciYmKGc9Yy5vblJlY292ZXJhYmxlRXJyb3IpKTtiPWZsKGIsbnVsbCxhLDEsbnVsbCE9Yz9jOm51bGwsZSwhMSxmLGcpO2FbdWZdPWIuY3VycmVudDtzZihhKTtpZihkKWZvcihhPTA7YTxkLmxlbmd0aDthKyspYz1kW2FdLGU9Yy5fZ2V0VmVyc2lvbixlPWUoYy5fc291cmNlKSxudWxsPT1iLm11dGFibGVTb3VyY2VFYWdlckh5ZHJhdGlvbkRhdGE/Yi5tdXRhYmxlU291cmNlRWFnZXJIeWRyYXRpb25EYXRhPVtjLGVdOmIubXV0YWJsZVNvdXJjZUVhZ2VySHlkcmF0aW9uRGF0YS5wdXNoKGMsCmUpO3JldHVybiBuZXcgbmwoYil9O2V4cG9ydHMucmVuZGVyPWZ1bmN0aW9uKGEsYixjKXtpZighcGwoYikpdGhyb3cgRXJyb3IocCgyMDApKTtyZXR1cm4gc2wobnVsbCxhLGIsITEsYyl9O2V4cG9ydHMudW5tb3VudENvbXBvbmVudEF0Tm9kZT1mdW5jdGlvbihhKXtpZighcGwoYSkpdGhyb3cgRXJyb3IocCg0MCkpO3JldHVybiBhLl9yZWFjdFJvb3RDb250YWluZXI/KFNrKGZ1bmN0aW9uKCl7c2wobnVsbCxudWxsLGEsITEsZnVuY3Rpb24oKXthLl9yZWFjdFJvb3RDb250YWluZXI9bnVsbDthW3VmXT1udWxsfSl9KSwhMCk6ITF9O2V4cG9ydHMudW5zdGFibGVfYmF0Y2hlZFVwZGF0ZXM9Ums7CmV4cG9ydHMudW5zdGFibGVfcmVuZGVyU3VidHJlZUludG9Db250YWluZXI9ZnVuY3Rpb24oYSxiLGMsZCl7aWYoIXBsKGMpKXRocm93IEVycm9yKHAoMjAwKSk7aWYobnVsbD09YXx8dm9pZCAwPT09YS5fcmVhY3RJbnRlcm5hbHMpdGhyb3cgRXJyb3IocCgzOCkpO3JldHVybiBzbChhLGIsYywhMSxkKX07ZXhwb3J0cy52ZXJzaW9uPSIxOC4yLjAtbmV4dC05ZTNiNzcyYjgtMjAyMjA2MDgiOwoKCi8qKiovIH0pLAoKLyoqKi8gNzQ1OgovKioqLyAoKF9fdW51c2VkX3dlYnBhY2tfbW9kdWxlLCBleHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSA9PiB7Cgp2YXIgX193ZWJwYWNrX3VudXNlZF9leHBvcnRfXzsKCgp2YXIgbSA9IF9fd2VicGFja19yZXF1aXJlX18oOTM1KTsKaWYgKHRydWUpIHsKICBleHBvcnRzLnMgPSBtLmNyZWF0ZVJvb3Q7CiAgX193ZWJwYWNrX3VudXNlZF9leHBvcnRfXyA9IG0uaHlkcmF0ZVJvb3Q7Cn0gZWxzZSB7IHZhciBpOyB9CgoKLyoqKi8gfSksCgovKioqLyA5MzU6Ci8qKiovICgobW9kdWxlLCBfX3VudXNlZF93ZWJwYWNrX2V4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pID0+IHsKCgoKZnVuY3Rpb24gY2hlY2tEQ0UoKSB7CiAgLyogZ2xvYmFsIF9fUkVBQ1RfREVWVE9PTFNfR0xPQkFMX0hPT0tfXyAqLwogIGlmICgKICAgIHR5cGVvZiBfX1JFQUNUX0RFVlRPT0xTX0dMT0JBTF9IT09LX18gPT09ICd1bmRlZmluZWQnIHx8CiAgICB0eXBlb2YgX19SRUFDVF9ERVZUT09MU19HTE9CQUxfSE9PS19fLmNoZWNrRENFICE9PSAnZnVuY3Rpb24nCiAgKSB7CiAgICByZXR1cm47CiAgfQogIGlmIChmYWxzZSkge30KICB0cnkgewogICAgLy8gVmVyaWZ5IHRoYXQgdGhlIGNvZGUgYWJvdmUgaGFzIGJlZW4gZGVhZCBjb2RlIGVsaW1pbmF0ZWQgKERDRSdkKS4KICAgIF9fUkVBQ1RfREVWVE9PTFNfR0xPQkFMX0hPT0tfXy5jaGVja0RDRShjaGVja0RDRSk7CiAgfSBjYXRjaCAoZXJyKSB7CiAgICAvLyBEZXZUb29scyBzaG91bGRuJ3QgY3Jhc2ggUmVhY3QsIG5vIG1hdHRlciB3aGF0LgogICAgLy8gV2Ugc2hvdWxkIHN0aWxsIHJlcG9ydCBpbiBjYXNlIHdlIGJyZWFrIHRoaXMgY29kZS4KICAgIGNvbnNvbGUuZXJyb3IoZXJyKTsKICB9Cn0KCmlmICh0cnVlKSB7CiAgLy8gRENFIGNoZWNrIHNob3VsZCBoYXBwZW4gYmVmb3JlIFJlYWN0RE9NIGJ1bmRsZSBleGVjdXRlcyBzbyB0aGF0CiAgLy8gRGV2VG9vbHMgY2FuIHJlcG9ydCBiYWQgbWluaWZpY2F0aW9uIGR1cmluZyBpbmplY3Rpb24uCiAgY2hlY2tEQ0UoKTsKICBtb2R1bGUuZXhwb3J0cyA9IF9fd2VicGFja19yZXF1aXJlX18oNDQ4KTsKfSBlbHNlIHt9CgoKLyoqKi8gfSksCgovKioqLyA0MDg6Ci8qKiovICgoX191bnVzZWRfd2VicGFja19tb2R1bGUsIGV4cG9ydHMpID0+IHsKCi8qKgogKiBAbGljZW5zZSBSZWFjdAogKiByZWFjdC5wcm9kdWN0aW9uLm1pbi5qcwogKgogKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy4KICoKICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlCiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS4KICovCnZhciBsPVN5bWJvbC5mb3IoInJlYWN0LmVsZW1lbnQiKSxuPVN5bWJvbC5mb3IoInJlYWN0LnBvcnRhbCIpLHA9U3ltYm9sLmZvcigicmVhY3QuZnJhZ21lbnQiKSxxPVN5bWJvbC5mb3IoInJlYWN0LnN0cmljdF9tb2RlIikscj1TeW1ib2wuZm9yKCJyZWFjdC5wcm9maWxlciIpLHQ9U3ltYm9sLmZvcigicmVhY3QucHJvdmlkZXIiKSx1PVN5bWJvbC5mb3IoInJlYWN0LmNvbnRleHQiKSx2PVN5bWJvbC5mb3IoInJlYWN0LmZvcndhcmRfcmVmIiksdz1TeW1ib2wuZm9yKCJyZWFjdC5zdXNwZW5zZSIpLHg9U3ltYm9sLmZvcigicmVhY3QubWVtbyIpLHk9U3ltYm9sLmZvcigicmVhY3QubGF6eSIpLHo9U3ltYm9sLml0ZXJhdG9yO2Z1bmN0aW9uIEEoYSl7aWYobnVsbD09PWF8fCJvYmplY3QiIT09dHlwZW9mIGEpcmV0dXJuIG51bGw7YT16JiZhW3pdfHxhWyJAQGl0ZXJhdG9yIl07cmV0dXJuImZ1bmN0aW9uIj09PXR5cGVvZiBhP2E6bnVsbH0KdmFyIEI9e2lzTW91bnRlZDpmdW5jdGlvbigpe3JldHVybiExfSxlbnF1ZXVlRm9yY2VVcGRhdGU6ZnVuY3Rpb24oKXt9LGVucXVldWVSZXBsYWNlU3RhdGU6ZnVuY3Rpb24oKXt9LGVucXVldWVTZXRTdGF0ZTpmdW5jdGlvbigpe319LEM9T2JqZWN0LmFzc2lnbixEPXt9O2Z1bmN0aW9uIEUoYSxiLGUpe3RoaXMucHJvcHM9YTt0aGlzLmNvbnRleHQ9Yjt0aGlzLnJlZnM9RDt0aGlzLnVwZGF0ZXI9ZXx8Qn1FLnByb3RvdHlwZS5pc1JlYWN0Q29tcG9uZW50PXt9OwpFLnByb3RvdHlwZS5zZXRTdGF0ZT1mdW5jdGlvbihhLGIpe2lmKCJvYmplY3QiIT09dHlwZW9mIGEmJiJmdW5jdGlvbiIhPT10eXBlb2YgYSYmbnVsbCE9YSl0aHJvdyBFcnJvcigic2V0U3RhdGUoLi4uKTogdGFrZXMgYW4gb2JqZWN0IG9mIHN0YXRlIHZhcmlhYmxlcyB0byB1cGRhdGUgb3IgYSBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGFuIG9iamVjdCBvZiBzdGF0ZSB2YXJpYWJsZXMuIik7dGhpcy51cGRhdGVyLmVucXVldWVTZXRTdGF0ZSh0aGlzLGEsYiwic2V0U3RhdGUiKX07RS5wcm90b3R5cGUuZm9yY2VVcGRhdGU9ZnVuY3Rpb24oYSl7dGhpcy51cGRhdGVyLmVucXVldWVGb3JjZVVwZGF0ZSh0aGlzLGEsImZvcmNlVXBkYXRlIil9O2Z1bmN0aW9uIEYoKXt9Ri5wcm90b3R5cGU9RS5wcm90b3R5cGU7ZnVuY3Rpb24gRyhhLGIsZSl7dGhpcy5wcm9wcz1hO3RoaXMuY29udGV4dD1iO3RoaXMucmVmcz1EO3RoaXMudXBkYXRlcj1lfHxCfXZhciBIPUcucHJvdG90eXBlPW5ldyBGOwpILmNvbnN0cnVjdG9yPUc7QyhILEUucHJvdG90eXBlKTtILmlzUHVyZVJlYWN0Q29tcG9uZW50PSEwO3ZhciBJPUFycmF5LmlzQXJyYXksSj1PYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LEs9e2N1cnJlbnQ6bnVsbH0sTD17a2V5OiEwLHJlZjohMCxfX3NlbGY6ITAsX19zb3VyY2U6ITB9OwpmdW5jdGlvbiBNKGEsYixlKXt2YXIgZCxjPXt9LGs9bnVsbCxoPW51bGw7aWYobnVsbCE9Yilmb3IoZCBpbiB2b2lkIDAhPT1iLnJlZiYmKGg9Yi5yZWYpLHZvaWQgMCE9PWIua2V5JiYoaz0iIitiLmtleSksYilKLmNhbGwoYixkKSYmIUwuaGFzT3duUHJvcGVydHkoZCkmJihjW2RdPWJbZF0pO3ZhciBnPWFyZ3VtZW50cy5sZW5ndGgtMjtpZigxPT09ZyljLmNoaWxkcmVuPWU7ZWxzZSBpZigxPGcpe2Zvcih2YXIgZj1BcnJheShnKSxtPTA7bTxnO20rKylmW21dPWFyZ3VtZW50c1ttKzJdO2MuY2hpbGRyZW49Zn1pZihhJiZhLmRlZmF1bHRQcm9wcylmb3IoZCBpbiBnPWEuZGVmYXVsdFByb3BzLGcpdm9pZCAwPT09Y1tkXSYmKGNbZF09Z1tkXSk7cmV0dXJueyQkdHlwZW9mOmwsdHlwZTphLGtleTprLHJlZjpoLHByb3BzOmMsX293bmVyOksuY3VycmVudH19CmZ1bmN0aW9uIE4oYSxiKXtyZXR1cm57JCR0eXBlb2Y6bCx0eXBlOmEudHlwZSxrZXk6YixyZWY6YS5yZWYscHJvcHM6YS5wcm9wcyxfb3duZXI6YS5fb3duZXJ9fWZ1bmN0aW9uIE8oYSl7cmV0dXJuIm9iamVjdCI9PT10eXBlb2YgYSYmbnVsbCE9PWEmJmEuJCR0eXBlb2Y9PT1sfWZ1bmN0aW9uIGVzY2FwZShhKXt2YXIgYj17Ij0iOiI9MCIsIjoiOiI9MiJ9O3JldHVybiIkIithLnJlcGxhY2UoL1s9Ol0vZyxmdW5jdGlvbihhKXtyZXR1cm4gYlthXX0pfXZhciBQPS9cLysvZztmdW5jdGlvbiBRKGEsYil7cmV0dXJuIm9iamVjdCI9PT10eXBlb2YgYSYmbnVsbCE9PWEmJm51bGwhPWEua2V5P2VzY2FwZSgiIithLmtleSk6Yi50b1N0cmluZygzNil9CmZ1bmN0aW9uIFIoYSxiLGUsZCxjKXt2YXIgaz10eXBlb2YgYTtpZigidW5kZWZpbmVkIj09PWt8fCJib29sZWFuIj09PWspYT1udWxsO3ZhciBoPSExO2lmKG51bGw9PT1hKWg9ITA7ZWxzZSBzd2l0Y2goayl7Y2FzZSAic3RyaW5nIjpjYXNlICJudW1iZXIiOmg9ITA7YnJlYWs7Y2FzZSAib2JqZWN0Ijpzd2l0Y2goYS4kJHR5cGVvZil7Y2FzZSBsOmNhc2UgbjpoPSEwfX1pZihoKXJldHVybiBoPWEsYz1jKGgpLGE9IiI9PT1kPyIuIitRKGgsMCk6ZCxJKGMpPyhlPSIiLG51bGwhPWEmJihlPWEucmVwbGFjZShQLCIkJi8iKSsiLyIpLFIoYyxiLGUsIiIsZnVuY3Rpb24oYSl7cmV0dXJuIGF9KSk6bnVsbCE9YyYmKE8oYykmJihjPU4oYyxlKyghYy5rZXl8fGgmJmgua2V5PT09Yy5rZXk/IiI6KCIiK2Mua2V5KS5yZXBsYWNlKFAsIiQmLyIpKyIvIikrYSkpLGIucHVzaChjKSksMTtoPTA7ZD0iIj09PWQ/Ii4iOmQrIjoiO2lmKEkoYSkpZm9yKHZhciBnPTA7ZzxhLmxlbmd0aDtnKyspe2s9CmFbZ107dmFyIGY9ZCtRKGssZyk7aCs9UihrLGIsZSxmLGMpfWVsc2UgaWYoZj1BKGEpLCJmdW5jdGlvbiI9PT10eXBlb2YgZilmb3IoYT1mLmNhbGwoYSksZz0wOyEoaz1hLm5leHQoKSkuZG9uZTspaz1rLnZhbHVlLGY9ZCtRKGssZysrKSxoKz1SKGssYixlLGYsYyk7ZWxzZSBpZigib2JqZWN0Ij09PWspdGhyb3cgYj1TdHJpbmcoYSksRXJyb3IoIk9iamVjdHMgYXJlIG5vdCB2YWxpZCBhcyBhIFJlYWN0IGNoaWxkIChmb3VuZDogIisoIltvYmplY3QgT2JqZWN0XSI9PT1iPyJvYmplY3Qgd2l0aCBrZXlzIHsiK09iamVjdC5rZXlzKGEpLmpvaW4oIiwgIikrIn0iOmIpKyIpLiBJZiB5b3UgbWVhbnQgdG8gcmVuZGVyIGEgY29sbGVjdGlvbiBvZiBjaGlsZHJlbiwgdXNlIGFuIGFycmF5IGluc3RlYWQuIik7cmV0dXJuIGh9CmZ1bmN0aW9uIFMoYSxiLGUpe2lmKG51bGw9PWEpcmV0dXJuIGE7dmFyIGQ9W10sYz0wO1IoYSxkLCIiLCIiLGZ1bmN0aW9uKGEpe3JldHVybiBiLmNhbGwoZSxhLGMrKyl9KTtyZXR1cm4gZH1mdW5jdGlvbiBUKGEpe2lmKC0xPT09YS5fc3RhdHVzKXt2YXIgYj1hLl9yZXN1bHQ7Yj1iKCk7Yi50aGVuKGZ1bmN0aW9uKGIpe2lmKDA9PT1hLl9zdGF0dXN8fC0xPT09YS5fc3RhdHVzKWEuX3N0YXR1cz0xLGEuX3Jlc3VsdD1ifSxmdW5jdGlvbihiKXtpZigwPT09YS5fc3RhdHVzfHwtMT09PWEuX3N0YXR1cylhLl9zdGF0dXM9MixhLl9yZXN1bHQ9Yn0pOy0xPT09YS5fc3RhdHVzJiYoYS5fc3RhdHVzPTAsYS5fcmVzdWx0PWIpfWlmKDE9PT1hLl9zdGF0dXMpcmV0dXJuIGEuX3Jlc3VsdC5kZWZhdWx0O3Rocm93IGEuX3Jlc3VsdDt9CnZhciBVPXtjdXJyZW50Om51bGx9LFY9e3RyYW5zaXRpb246bnVsbH0sVz17UmVhY3RDdXJyZW50RGlzcGF0Y2hlcjpVLFJlYWN0Q3VycmVudEJhdGNoQ29uZmlnOlYsUmVhY3RDdXJyZW50T3duZXI6S307ZXhwb3J0cy5DaGlsZHJlbj17bWFwOlMsZm9yRWFjaDpmdW5jdGlvbihhLGIsZSl7UyhhLGZ1bmN0aW9uKCl7Yi5hcHBseSh0aGlzLGFyZ3VtZW50cyl9LGUpfSxjb3VudDpmdW5jdGlvbihhKXt2YXIgYj0wO1MoYSxmdW5jdGlvbigpe2IrK30pO3JldHVybiBifSx0b0FycmF5OmZ1bmN0aW9uKGEpe3JldHVybiBTKGEsZnVuY3Rpb24oYSl7cmV0dXJuIGF9KXx8W119LG9ubHk6ZnVuY3Rpb24oYSl7aWYoIU8oYSkpdGhyb3cgRXJyb3IoIlJlYWN0LkNoaWxkcmVuLm9ubHkgZXhwZWN0ZWQgdG8gcmVjZWl2ZSBhIHNpbmdsZSBSZWFjdCBlbGVtZW50IGNoaWxkLiIpO3JldHVybiBhfX07ZXhwb3J0cy5Db21wb25lbnQ9RTtleHBvcnRzLkZyYWdtZW50PXA7CmV4cG9ydHMuUHJvZmlsZXI9cjtleHBvcnRzLlB1cmVDb21wb25lbnQ9RztleHBvcnRzLlN0cmljdE1vZGU9cTtleHBvcnRzLlN1c3BlbnNlPXc7ZXhwb3J0cy5fX1NFQ1JFVF9JTlRFUk5BTFNfRE9fTk9UX1VTRV9PUl9ZT1VfV0lMTF9CRV9GSVJFRD1XOwpleHBvcnRzLmNsb25lRWxlbWVudD1mdW5jdGlvbihhLGIsZSl7aWYobnVsbD09PWF8fHZvaWQgMD09PWEpdGhyb3cgRXJyb3IoIlJlYWN0LmNsb25lRWxlbWVudCguLi4pOiBUaGUgYXJndW1lbnQgbXVzdCBiZSBhIFJlYWN0IGVsZW1lbnQsIGJ1dCB5b3UgcGFzc2VkICIrYSsiLiIpO3ZhciBkPUMoe30sYS5wcm9wcyksYz1hLmtleSxrPWEucmVmLGg9YS5fb3duZXI7aWYobnVsbCE9Yil7dm9pZCAwIT09Yi5yZWYmJihrPWIucmVmLGg9Sy5jdXJyZW50KTt2b2lkIDAhPT1iLmtleSYmKGM9IiIrYi5rZXkpO2lmKGEudHlwZSYmYS50eXBlLmRlZmF1bHRQcm9wcyl2YXIgZz1hLnR5cGUuZGVmYXVsdFByb3BzO2ZvcihmIGluIGIpSi5jYWxsKGIsZikmJiFMLmhhc093blByb3BlcnR5KGYpJiYoZFtmXT12b2lkIDA9PT1iW2ZdJiZ2b2lkIDAhPT1nP2dbZl06YltmXSl9dmFyIGY9YXJndW1lbnRzLmxlbmd0aC0yO2lmKDE9PT1mKWQuY2hpbGRyZW49ZTtlbHNlIGlmKDE8Zil7Zz1BcnJheShmKTsKZm9yKHZhciBtPTA7bTxmO20rKylnW21dPWFyZ3VtZW50c1ttKzJdO2QuY2hpbGRyZW49Z31yZXR1cm57JCR0eXBlb2Y6bCx0eXBlOmEudHlwZSxrZXk6YyxyZWY6ayxwcm9wczpkLF9vd25lcjpofX07ZXhwb3J0cy5jcmVhdGVDb250ZXh0PWZ1bmN0aW9uKGEpe2E9eyQkdHlwZW9mOnUsX2N1cnJlbnRWYWx1ZTphLF9jdXJyZW50VmFsdWUyOmEsX3RocmVhZENvdW50OjAsUHJvdmlkZXI6bnVsbCxDb25zdW1lcjpudWxsLF9kZWZhdWx0VmFsdWU6bnVsbCxfZ2xvYmFsTmFtZTpudWxsfTthLlByb3ZpZGVyPXskJHR5cGVvZjp0LF9jb250ZXh0OmF9O3JldHVybiBhLkNvbnN1bWVyPWF9O2V4cG9ydHMuY3JlYXRlRWxlbWVudD1NO2V4cG9ydHMuY3JlYXRlRmFjdG9yeT1mdW5jdGlvbihhKXt2YXIgYj1NLmJpbmQobnVsbCxhKTtiLnR5cGU9YTtyZXR1cm4gYn07ZXhwb3J0cy5jcmVhdGVSZWY9ZnVuY3Rpb24oKXtyZXR1cm57Y3VycmVudDpudWxsfX07CmV4cG9ydHMuZm9yd2FyZFJlZj1mdW5jdGlvbihhKXtyZXR1cm57JCR0eXBlb2Y6dixyZW5kZXI6YX19O2V4cG9ydHMuaXNWYWxpZEVsZW1lbnQ9TztleHBvcnRzLmxhenk9ZnVuY3Rpb24oYSl7cmV0dXJueyQkdHlwZW9mOnksX3BheWxvYWQ6e19zdGF0dXM6LTEsX3Jlc3VsdDphfSxfaW5pdDpUfX07ZXhwb3J0cy5tZW1vPWZ1bmN0aW9uKGEsYil7cmV0dXJueyQkdHlwZW9mOngsdHlwZTphLGNvbXBhcmU6dm9pZCAwPT09Yj9udWxsOmJ9fTtleHBvcnRzLnN0YXJ0VHJhbnNpdGlvbj1mdW5jdGlvbihhKXt2YXIgYj1WLnRyYW5zaXRpb247Vi50cmFuc2l0aW9uPXt9O3RyeXthKCl9ZmluYWxseXtWLnRyYW5zaXRpb249Yn19O2V4cG9ydHMudW5zdGFibGVfYWN0PWZ1bmN0aW9uKCl7dGhyb3cgRXJyb3IoImFjdCguLi4pIGlzIG5vdCBzdXBwb3J0ZWQgaW4gcHJvZHVjdGlvbiBidWlsZHMgb2YgUmVhY3QuIik7fTsKZXhwb3J0cy51c2VDYWxsYmFjaz1mdW5jdGlvbihhLGIpe3JldHVybiBVLmN1cnJlbnQudXNlQ2FsbGJhY2soYSxiKX07ZXhwb3J0cy51c2VDb250ZXh0PWZ1bmN0aW9uKGEpe3JldHVybiBVLmN1cnJlbnQudXNlQ29udGV4dChhKX07ZXhwb3J0cy51c2VEZWJ1Z1ZhbHVlPWZ1bmN0aW9uKCl7fTtleHBvcnRzLnVzZURlZmVycmVkVmFsdWU9ZnVuY3Rpb24oYSl7cmV0dXJuIFUuY3VycmVudC51c2VEZWZlcnJlZFZhbHVlKGEpfTtleHBvcnRzLnVzZUVmZmVjdD1mdW5jdGlvbihhLGIpe3JldHVybiBVLmN1cnJlbnQudXNlRWZmZWN0KGEsYil9O2V4cG9ydHMudXNlSWQ9ZnVuY3Rpb24oKXtyZXR1cm4gVS5jdXJyZW50LnVzZUlkKCl9O2V4cG9ydHMudXNlSW1wZXJhdGl2ZUhhbmRsZT1mdW5jdGlvbihhLGIsZSl7cmV0dXJuIFUuY3VycmVudC51c2VJbXBlcmF0aXZlSGFuZGxlKGEsYixlKX07CmV4cG9ydHMudXNlSW5zZXJ0aW9uRWZmZWN0PWZ1bmN0aW9uKGEsYil7cmV0dXJuIFUuY3VycmVudC51c2VJbnNlcnRpb25FZmZlY3QoYSxiKX07ZXhwb3J0cy51c2VMYXlvdXRFZmZlY3Q9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gVS5jdXJyZW50LnVzZUxheW91dEVmZmVjdChhLGIpfTtleHBvcnRzLnVzZU1lbW89ZnVuY3Rpb24oYSxiKXtyZXR1cm4gVS5jdXJyZW50LnVzZU1lbW8oYSxiKX07ZXhwb3J0cy51c2VSZWR1Y2VyPWZ1bmN0aW9uKGEsYixlKXtyZXR1cm4gVS5jdXJyZW50LnVzZVJlZHVjZXIoYSxiLGUpfTtleHBvcnRzLnVzZVJlZj1mdW5jdGlvbihhKXtyZXR1cm4gVS5jdXJyZW50LnVzZVJlZihhKX07ZXhwb3J0cy51c2VTdGF0ZT1mdW5jdGlvbihhKXtyZXR1cm4gVS5jdXJyZW50LnVzZVN0YXRlKGEpfTtleHBvcnRzLnVzZVN5bmNFeHRlcm5hbFN0b3JlPWZ1bmN0aW9uKGEsYixlKXtyZXR1cm4gVS5jdXJyZW50LnVzZVN5bmNFeHRlcm5hbFN0b3JlKGEsYixlKX07CmV4cG9ydHMudXNlVHJhbnNpdGlvbj1mdW5jdGlvbigpe3JldHVybiBVLmN1cnJlbnQudXNlVHJhbnNpdGlvbigpfTtleHBvcnRzLnZlcnNpb249IjE4LjIuMCI7CgoKLyoqKi8gfSksCgovKioqLyAyOTQ6Ci8qKiovICgobW9kdWxlLCBfX3VudXNlZF93ZWJwYWNrX2V4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pID0+IHsKCgoKaWYgKHRydWUpIHsKICBtb2R1bGUuZXhwb3J0cyA9IF9fd2VicGFja19yZXF1aXJlX18oNDA4KTsKfSBlbHNlIHt9CgoKLyoqKi8gfSksCgovKioqLyA1MzoKLyoqKi8gKChfX3VudXNlZF93ZWJwYWNrX21vZHVsZSwgZXhwb3J0cykgPT4gewoKLyoqCiAqIEBsaWNlbnNlIFJlYWN0CiAqIHNjaGVkdWxlci5wcm9kdWN0aW9uLm1pbi5qcwogKgogKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy4KICoKICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlCiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS4KICovCmZ1bmN0aW9uIGYoYSxiKXt2YXIgYz1hLmxlbmd0aDthLnB1c2goYik7YTpmb3IoOzA8Yzspe3ZhciBkPWMtMT4+PjEsZT1hW2RdO2lmKDA8ZyhlLGIpKWFbZF09YixhW2NdPWUsYz1kO2Vsc2UgYnJlYWsgYX19ZnVuY3Rpb24gaChhKXtyZXR1cm4gMD09PWEubGVuZ3RoP251bGw6YVswXX1mdW5jdGlvbiBrKGEpe2lmKDA9PT1hLmxlbmd0aClyZXR1cm4gbnVsbDt2YXIgYj1hWzBdLGM9YS5wb3AoKTtpZihjIT09Yil7YVswXT1jO2E6Zm9yKHZhciBkPTAsZT1hLmxlbmd0aCx3PWU+Pj4xO2Q8dzspe3ZhciBtPTIqKGQrMSktMSxDPWFbbV0sbj1tKzEseD1hW25dO2lmKDA+ZyhDLGMpKW48ZSYmMD5nKHgsQyk/KGFbZF09eCxhW25dPWMsZD1uKTooYVtkXT1DLGFbbV09YyxkPW0pO2Vsc2UgaWYobjxlJiYwPmcoeCxjKSlhW2RdPXgsYVtuXT1jLGQ9bjtlbHNlIGJyZWFrIGF9fXJldHVybiBifQpmdW5jdGlvbiBnKGEsYil7dmFyIGM9YS5zb3J0SW5kZXgtYi5zb3J0SW5kZXg7cmV0dXJuIDAhPT1jP2M6YS5pZC1iLmlkfWlmKCJvYmplY3QiPT09dHlwZW9mIHBlcmZvcm1hbmNlJiYiZnVuY3Rpb24iPT09dHlwZW9mIHBlcmZvcm1hbmNlLm5vdyl7dmFyIGw9cGVyZm9ybWFuY2U7ZXhwb3J0cy51bnN0YWJsZV9ub3c9ZnVuY3Rpb24oKXtyZXR1cm4gbC5ub3coKX19ZWxzZXt2YXIgcD1EYXRlLHE9cC5ub3coKTtleHBvcnRzLnVuc3RhYmxlX25vdz1mdW5jdGlvbigpe3JldHVybiBwLm5vdygpLXF9fXZhciByPVtdLHQ9W10sdT0xLHY9bnVsbCx5PTMsej0hMSxBPSExLEI9ITEsRD0iZnVuY3Rpb24iPT09dHlwZW9mIHNldFRpbWVvdXQ/c2V0VGltZW91dDpudWxsLEU9ImZ1bmN0aW9uIj09PXR5cGVvZiBjbGVhclRpbWVvdXQ/Y2xlYXJUaW1lb3V0Om51bGwsRj0idW5kZWZpbmVkIiE9PXR5cGVvZiBzZXRJbW1lZGlhdGU/c2V0SW1tZWRpYXRlOm51bGw7CiJ1bmRlZmluZWQiIT09dHlwZW9mIG5hdmlnYXRvciYmdm9pZCAwIT09bmF2aWdhdG9yLnNjaGVkdWxpbmcmJnZvaWQgMCE9PW5hdmlnYXRvci5zY2hlZHVsaW5nLmlzSW5wdXRQZW5kaW5nJiZuYXZpZ2F0b3Iuc2NoZWR1bGluZy5pc0lucHV0UGVuZGluZy5iaW5kKG5hdmlnYXRvci5zY2hlZHVsaW5nKTtmdW5jdGlvbiBHKGEpe2Zvcih2YXIgYj1oKHQpO251bGwhPT1iOyl7aWYobnVsbD09PWIuY2FsbGJhY2spayh0KTtlbHNlIGlmKGIuc3RhcnRUaW1lPD1hKWsodCksYi5zb3J0SW5kZXg9Yi5leHBpcmF0aW9uVGltZSxmKHIsYik7ZWxzZSBicmVhaztiPWgodCl9fWZ1bmN0aW9uIEgoYSl7Qj0hMTtHKGEpO2lmKCFBKWlmKG51bGwhPT1oKHIpKUE9ITAsSShKKTtlbHNle3ZhciBiPWgodCk7bnVsbCE9PWImJksoSCxiLnN0YXJ0VGltZS1hKX19CmZ1bmN0aW9uIEooYSxiKXtBPSExO0ImJihCPSExLEUoTCksTD0tMSk7ej0hMDt2YXIgYz15O3RyeXtHKGIpO2Zvcih2PWgocik7bnVsbCE9PXYmJighKHYuZXhwaXJhdGlvblRpbWU+Yil8fGEmJiFNKCkpOyl7dmFyIGQ9di5jYWxsYmFjaztpZigiZnVuY3Rpb24iPT09dHlwZW9mIGQpe3YuY2FsbGJhY2s9bnVsbDt5PXYucHJpb3JpdHlMZXZlbDt2YXIgZT1kKHYuZXhwaXJhdGlvblRpbWU8PWIpO2I9ZXhwb3J0cy51bnN0YWJsZV9ub3coKTsiZnVuY3Rpb24iPT09dHlwZW9mIGU/di5jYWxsYmFjaz1lOnY9PT1oKHIpJiZrKHIpO0coYil9ZWxzZSBrKHIpO3Y9aChyKX1pZihudWxsIT09dil2YXIgdz0hMDtlbHNle3ZhciBtPWgodCk7bnVsbCE9PW0mJksoSCxtLnN0YXJ0VGltZS1iKTt3PSExfXJldHVybiB3fWZpbmFsbHl7dj1udWxsLHk9Yyx6PSExfX12YXIgTj0hMSxPPW51bGwsTD0tMSxQPTUsUT0tMTsKZnVuY3Rpb24gTSgpe3JldHVybiBleHBvcnRzLnVuc3RhYmxlX25vdygpLVE8UD8hMTohMH1mdW5jdGlvbiBSKCl7aWYobnVsbCE9PU8pe3ZhciBhPWV4cG9ydHMudW5zdGFibGVfbm93KCk7UT1hO3ZhciBiPSEwO3RyeXtiPU8oITAsYSl9ZmluYWxseXtiP1MoKTooTj0hMSxPPW51bGwpfX1lbHNlIE49ITF9dmFyIFM7aWYoImZ1bmN0aW9uIj09PXR5cGVvZiBGKVM9ZnVuY3Rpb24oKXtGKFIpfTtlbHNlIGlmKCJ1bmRlZmluZWQiIT09dHlwZW9mIE1lc3NhZ2VDaGFubmVsKXt2YXIgVD1uZXcgTWVzc2FnZUNoYW5uZWwsVT1ULnBvcnQyO1QucG9ydDEub25tZXNzYWdlPVI7Uz1mdW5jdGlvbigpe1UucG9zdE1lc3NhZ2UobnVsbCl9fWVsc2UgUz1mdW5jdGlvbigpe0QoUiwwKX07ZnVuY3Rpb24gSShhKXtPPWE7Tnx8KE49ITAsUygpKX1mdW5jdGlvbiBLKGEsYil7TD1EKGZ1bmN0aW9uKCl7YShleHBvcnRzLnVuc3RhYmxlX25vdygpKX0sYil9CmV4cG9ydHMudW5zdGFibGVfSWRsZVByaW9yaXR5PTU7ZXhwb3J0cy51bnN0YWJsZV9JbW1lZGlhdGVQcmlvcml0eT0xO2V4cG9ydHMudW5zdGFibGVfTG93UHJpb3JpdHk9NDtleHBvcnRzLnVuc3RhYmxlX05vcm1hbFByaW9yaXR5PTM7ZXhwb3J0cy51bnN0YWJsZV9Qcm9maWxpbmc9bnVsbDtleHBvcnRzLnVuc3RhYmxlX1VzZXJCbG9ja2luZ1ByaW9yaXR5PTI7ZXhwb3J0cy51bnN0YWJsZV9jYW5jZWxDYWxsYmFjaz1mdW5jdGlvbihhKXthLmNhbGxiYWNrPW51bGx9O2V4cG9ydHMudW5zdGFibGVfY29udGludWVFeGVjdXRpb249ZnVuY3Rpb24oKXtBfHx6fHwoQT0hMCxJKEopKX07CmV4cG9ydHMudW5zdGFibGVfZm9yY2VGcmFtZVJhdGU9ZnVuY3Rpb24oYSl7MD5hfHwxMjU8YT9jb25zb2xlLmVycm9yKCJmb3JjZUZyYW1lUmF0ZSB0YWtlcyBhIHBvc2l0aXZlIGludCBiZXR3ZWVuIDAgYW5kIDEyNSwgZm9yY2luZyBmcmFtZSByYXRlcyBoaWdoZXIgdGhhbiAxMjUgZnBzIGlzIG5vdCBzdXBwb3J0ZWQiKTpQPTA8YT9NYXRoLmZsb29yKDFFMy9hKTo1fTtleHBvcnRzLnVuc3RhYmxlX2dldEN1cnJlbnRQcmlvcml0eUxldmVsPWZ1bmN0aW9uKCl7cmV0dXJuIHl9O2V4cG9ydHMudW5zdGFibGVfZ2V0Rmlyc3RDYWxsYmFja05vZGU9ZnVuY3Rpb24oKXtyZXR1cm4gaChyKX07ZXhwb3J0cy51bnN0YWJsZV9uZXh0PWZ1bmN0aW9uKGEpe3N3aXRjaCh5KXtjYXNlIDE6Y2FzZSAyOmNhc2UgMzp2YXIgYj0zO2JyZWFrO2RlZmF1bHQ6Yj15fXZhciBjPXk7eT1iO3RyeXtyZXR1cm4gYSgpfWZpbmFsbHl7eT1jfX07ZXhwb3J0cy51bnN0YWJsZV9wYXVzZUV4ZWN1dGlvbj1mdW5jdGlvbigpe307CmV4cG9ydHMudW5zdGFibGVfcmVxdWVzdFBhaW50PWZ1bmN0aW9uKCl7fTtleHBvcnRzLnVuc3RhYmxlX3J1bldpdGhQcmlvcml0eT1mdW5jdGlvbihhLGIpe3N3aXRjaChhKXtjYXNlIDE6Y2FzZSAyOmNhc2UgMzpjYXNlIDQ6Y2FzZSA1OmJyZWFrO2RlZmF1bHQ6YT0zfXZhciBjPXk7eT1hO3RyeXtyZXR1cm4gYigpfWZpbmFsbHl7eT1jfX07CmV4cG9ydHMudW5zdGFibGVfc2NoZWR1bGVDYWxsYmFjaz1mdW5jdGlvbihhLGIsYyl7dmFyIGQ9ZXhwb3J0cy51bnN0YWJsZV9ub3coKTsib2JqZWN0Ij09PXR5cGVvZiBjJiZudWxsIT09Yz8oYz1jLmRlbGF5LGM9Im51bWJlciI9PT10eXBlb2YgYyYmMDxjP2QrYzpkKTpjPWQ7c3dpdGNoKGEpe2Nhc2UgMTp2YXIgZT0tMTticmVhaztjYXNlIDI6ZT0yNTA7YnJlYWs7Y2FzZSA1OmU9MTA3Mzc0MTgyMzticmVhaztjYXNlIDQ6ZT0xRTQ7YnJlYWs7ZGVmYXVsdDplPTVFM31lPWMrZTthPXtpZDp1KyssY2FsbGJhY2s6Yixwcmlvcml0eUxldmVsOmEsc3RhcnRUaW1lOmMsZXhwaXJhdGlvblRpbWU6ZSxzb3J0SW5kZXg6LTF9O2M+ZD8oYS5zb3J0SW5kZXg9YyxmKHQsYSksbnVsbD09PWgocikmJmE9PT1oKHQpJiYoQj8oRShMKSxMPS0xKTpCPSEwLEsoSCxjLWQpKSk6KGEuc29ydEluZGV4PWUsZihyLGEpLEF8fHp8fChBPSEwLEkoSikpKTtyZXR1cm4gYX07CmV4cG9ydHMudW5zdGFibGVfc2hvdWxkWWllbGQ9TTtleHBvcnRzLnVuc3RhYmxlX3dyYXBDYWxsYmFjaz1mdW5jdGlvbihhKXt2YXIgYj15O3JldHVybiBmdW5jdGlvbigpe3ZhciBjPXk7eT1iO3RyeXtyZXR1cm4gYS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9ZmluYWxseXt5PWN9fX07CgoKLyoqKi8gfSksCgovKioqLyA4NDA6Ci8qKiovICgobW9kdWxlLCBfX3VudXNlZF93ZWJwYWNrX2V4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pID0+IHsKCgoKaWYgKHRydWUpIHsKICBtb2R1bGUuZXhwb3J0cyA9IF9fd2VicGFja19yZXF1aXJlX18oNTMpOwp9IGVsc2Uge30KCgovKioqLyB9KQoKLyoqKioqKi8gCX0pOwovKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqLwovKioqKioqLyAJLy8gVGhlIG1vZHVsZSBjYWNoZQovKioqKioqLyAJdmFyIF9fd2VicGFja19tb2R1bGVfY2FjaGVfXyA9IHt9OwovKioqKioqLyAJCi8qKioqKiovIAkvLyBUaGUgcmVxdWlyZSBmdW5jdGlvbgovKioqKioqLyAJZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkgewovKioqKioqLyAJCS8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZQovKioqKioqLyAJCXZhciBjYWNoZWRNb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdOwovKioqKioqLyAJCWlmIChjYWNoZWRNb2R1bGUgIT09IHVuZGVmaW5lZCkgewovKioqKioqLyAJCQlyZXR1cm4gY2FjaGVkTW9kdWxlLmV4cG9ydHM7Ci8qKioqKiovIAkJfQovKioqKioqLyAJCS8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpCi8qKioqKiovIAkJdmFyIG1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0gPSB7Ci8qKioqKiovIAkJCS8vIG5vIG1vZHVsZS5pZCBuZWVkZWQKLyoqKioqKi8gCQkJLy8gbm8gbW9kdWxlLmxvYWRlZCBuZWVkZWQKLyoqKioqKi8gCQkJZXhwb3J0czoge30KLyoqKioqKi8gCQl9OwovKioqKioqLyAJCi8qKioqKiovIAkJLy8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uCi8qKioqKiovIAkJX193ZWJwYWNrX21vZHVsZXNfX1ttb2R1bGVJZF0obW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7Ci8qKioqKiovIAkKLyoqKioqKi8gCQkvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZQovKioqKioqLyAJCXJldHVybiBtb2R1bGUuZXhwb3J0czsKLyoqKioqKi8gCX0KLyoqKioqKi8gCQovKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqLwovKioqKioqLyAJLyogd2VicGFjay9ydW50aW1lL2NyZWF0ZSBmYWtlIG5hbWVzcGFjZSBvYmplY3QgKi8KLyoqKioqKi8gCSgoKSA9PiB7Ci8qKioqKiovIAkJdmFyIGdldFByb3RvID0gT2JqZWN0LmdldFByb3RvdHlwZU9mID8gKG9iaikgPT4gKE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmopKSA6IChvYmopID0+IChvYmouX19wcm90b19fKTsKLyoqKioqKi8gCQl2YXIgbGVhZlByb3RvdHlwZXM7Ci8qKioqKiovIAkJLy8gY3JlYXRlIGEgZmFrZSBuYW1lc3BhY2Ugb2JqZWN0Ci8qKioqKiovIAkJLy8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0Ci8qKioqKiovIAkJLy8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zCi8qKioqKiovIAkJLy8gbW9kZSAmIDQ6IHJldHVybiB2YWx1ZSB3aGVuIGFscmVhZHkgbnMgb2JqZWN0Ci8qKioqKiovIAkJLy8gbW9kZSAmIDE2OiByZXR1cm4gdmFsdWUgd2hlbiBpdCdzIFByb21pc2UtbGlrZQovKioqKioqLyAJCS8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmUKLyoqKioqKi8gCQlfX3dlYnBhY2tfcmVxdWlyZV9fLnQgPSBmdW5jdGlvbih2YWx1ZSwgbW9kZSkgewovKioqKioqLyAJCQlpZihtb2RlICYgMSkgdmFsdWUgPSB0aGlzKHZhbHVlKTsKLyoqKioqKi8gCQkJaWYobW9kZSAmIDgpIHJldHVybiB2YWx1ZTsKLyoqKioqKi8gCQkJaWYodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSkgewovKioqKioqLyAJCQkJaWYoKG1vZGUgJiA0KSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7Ci8qKioqKiovIAkJCQlpZigobW9kZSAmIDE2KSAmJiB0eXBlb2YgdmFsdWUudGhlbiA9PT0gJ2Z1bmN0aW9uJykgcmV0dXJuIHZhbHVlOwovKioqKioqLyAJCQl9Ci8qKioqKiovIAkJCXZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7Ci8qKioqKiovIAkJCV9fd2VicGFja19yZXF1aXJlX18ucihucyk7Ci8qKioqKiovIAkJCXZhciBkZWYgPSB7fTsKLyoqKioqKi8gCQkJbGVhZlByb3RvdHlwZXMgPSBsZWFmUHJvdG90eXBlcyB8fCBbbnVsbCwgZ2V0UHJvdG8oe30pLCBnZXRQcm90byhbXSksIGdldFByb3RvKGdldFByb3RvKV07Ci8qKioqKiovIAkJCWZvcih2YXIgY3VycmVudCA9IG1vZGUgJiAyICYmIHZhbHVlOyB0eXBlb2YgY3VycmVudCA9PSAnb2JqZWN0JyAmJiAhfmxlYWZQcm90b3R5cGVzLmluZGV4T2YoY3VycmVudCk7IGN1cnJlbnQgPSBnZXRQcm90byhjdXJyZW50KSkgewovKioqKioqLyAJCQkJT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMoY3VycmVudCkuZm9yRWFjaCgoa2V5KSA9PiAoZGVmW2tleV0gPSAoKSA9PiAodmFsdWVba2V5XSkpKTsKLyoqKioqKi8gCQkJfQovKioqKioqLyAJCQlkZWZbJ2RlZmF1bHQnXSA9ICgpID0+ICh2YWx1ZSk7Ci8qKioqKiovIAkJCV9fd2VicGFja19yZXF1aXJlX18uZChucywgZGVmKTsKLyoqKioqKi8gCQkJcmV0dXJuIG5zOwovKioqKioqLyAJCX07Ci8qKioqKiovIAl9KSgpOwovKioqKioqLyAJCi8qKioqKiovIAkvKiB3ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMgKi8KLyoqKioqKi8gCSgoKSA9PiB7Ci8qKioqKiovIAkJLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbnMgZm9yIGhhcm1vbnkgZXhwb3J0cwovKioqKioqLyAJCV9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7Ci8qKioqKiovIAkJCWZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHsKLyoqKioqKi8gCQkJCWlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkgewovKioqKioqLyAJCQkJCU9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7Ci8qKioqKiovIAkJCQl9Ci8qKioqKiovIAkJCX0KLyoqKioqKi8gCQl9OwovKioqKioqLyAJfSkoKTsKLyoqKioqKi8gCQovKioqKioqLyAJLyogd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCAqLwovKioqKioqLyAJKCgpID0+IHsKLyoqKioqKi8gCQlfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSAob2JqLCBwcm9wKSA9PiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwgcHJvcCkpCi8qKioqKiovIAl9KSgpOwovKioqKioqLyAJCi8qKioqKiovIAkvKiB3ZWJwYWNrL3J1bnRpbWUvbWFrZSBuYW1lc3BhY2Ugb2JqZWN0ICovCi8qKioqKiovIAkoKCkgPT4gewovKioqKioqLyAJCS8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHMKLyoqKioqKi8gCQlfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4gewovKioqKioqLyAJCQlpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHsKLyoqKioqKi8gCQkJCU9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pOwovKioqKioqLyAJCQl9Ci8qKioqKiovIAkJCU9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7Ci8qKioqKiovIAkJfTsKLyoqKioqKi8gCX0pKCk7Ci8qKioqKiovIAkKLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi8KdmFyIF9fd2VicGFja19leHBvcnRzX18gPSB7fTsKLy8gVGhpcyBlbnRyeSBuZWVkIHRvIGJlIHdyYXBwZWQgaW4gYW4gSUlGRSBiZWNhdXNlIGl0IG5lZWQgdG8gYmUgaXNvbGF0ZWQgYWdhaW5zdCBvdGhlciBtb2R1bGVzIGluIHRoZSBjaHVuay4KKCgpID0+IHsKX193ZWJwYWNrX3JlcXVpcmVfXy5yKF9fd2VicGFja19leHBvcnRzX18pOwovKiBoYXJtb255IGltcG9ydCAqLyB2YXIgX0FwcF9fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMF9fID0gX193ZWJwYWNrX3JlcXVpcmVfXygyNDIpOwovKiBoYXJtb255IGltcG9ydCAqLyB2YXIgcmVhY3RfZG9tX2NsaWVudF9fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMV9fID0gX193ZWJwYWNrX3JlcXVpcmVfXyg3NDUpOwovKiBoYXJtb255IGltcG9ydCAqLyB2YXIgcmVhY3RfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzJfXyA9IF9fd2VicGFja19yZXF1aXJlX18oMjk0KTsKZnVuY3Rpb24gX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIobywgYWxsb3dBcnJheUxpa2UpIHt2YXIgaXQgPSB0eXBlb2YgU3ltYm9sICE9PSAidW5kZWZpbmVkIiAmJiBvW1N5bWJvbC5pdGVyYXRvcl0gfHwgb1siQEBpdGVyYXRvciJdO2lmICghaXQpIHtpZiAoQXJyYXkuaXNBcnJheShvKSB8fCAoaXQgPSBfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkobykpIHx8IGFsbG93QXJyYXlMaWtlICYmIG8gJiYgdHlwZW9mIG8ubGVuZ3RoID09PSAibnVtYmVyIikge2lmIChpdCkgbyA9IGl0O3ZhciBpID0gMDt2YXIgRiA9IGZ1bmN0aW9uIEYoKSB7fTtyZXR1cm4geyBzOiBGLCBuOiBmdW5jdGlvbiBuKCkge2lmIChpID49IG8ubGVuZ3RoKSByZXR1cm4geyBkb25lOiB0cnVlIH07cmV0dXJuIHsgZG9uZTogZmFsc2UsIHZhbHVlOiBvW2krK10gfTt9LCBlOiBmdW5jdGlvbiBlKF9lKSB7dGhyb3cgX2U7fSwgZjogRiB9O310aHJvdyBuZXcgVHlwZUVycm9yKCJJbnZhbGlkIGF0dGVtcHQgdG8gaXRlcmF0ZSBub24taXRlcmFibGUgaW5zdGFuY2UuXG5JbiBvcmRlciB0byBiZSBpdGVyYWJsZSwgbm9uLWFycmF5IG9iamVjdHMgbXVzdCBoYXZlIGEgW1N5bWJvbC5pdGVyYXRvcl0oKSBtZXRob2QuIik7fXZhciBub3JtYWxDb21wbGV0aW9uID0gdHJ1ZSxkaWRFcnIgPSBmYWxzZSxlcnI7cmV0dXJuIHsgczogZnVuY3Rpb24gcygpIHtpdCA9IGl0LmNhbGwobyk7fSwgbjogZnVuY3Rpb24gbigpIHt2YXIgc3RlcCA9IGl0Lm5leHQoKTtub3JtYWxDb21wbGV0aW9uID0gc3RlcC5kb25lO3JldHVybiBzdGVwO30sIGU6IGZ1bmN0aW9uIGUoX2UyKSB7ZGlkRXJyID0gdHJ1ZTtlcnIgPSBfZTI7fSwgZjogZnVuY3Rpb24gZigpIHt0cnkge2lmICghbm9ybWFsQ29tcGxldGlvbiAmJiBpdFsicmV0dXJuIl0gIT0gbnVsbCkgaXRbInJldHVybiJdKCk7fSBmaW5hbGx5IHtpZiAoZGlkRXJyKSB0aHJvdyBlcnI7fX0gfTt9ZnVuY3Rpb24gX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KG8sIG1pbkxlbikge2lmICghbykgcmV0dXJuO2lmICh0eXBlb2YgbyA9PT0gInN0cmluZyIpIHJldHVybiBfYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO3ZhciBuID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG8pLnNsaWNlKDgsIC0xKTtpZiAobiA9PT0gIk9iamVjdCIgJiYgby5jb25zdHJ1Y3RvcikgbiA9IG8uY29uc3RydWN0b3IubmFtZTtpZiAobiA9PT0gIk1hcCIgfHwgbiA9PT0gIlNldCIpIHJldHVybiBBcnJheS5mcm9tKG8pO2lmIChuID09PSAiQXJndW1lbnRzIiB8fCAvXig/OlVpfEkpbnQoPzo4fDE2fDMyKSg/OkNsYW1wZWQpP0FycmF5JC8udGVzdChuKSkgcmV0dXJuIF9hcnJheUxpa2VUb0FycmF5KG8sIG1pbkxlbik7fWZ1bmN0aW9uIF9hcnJheUxpa2VUb0FycmF5KGFyciwgbGVuKSB7aWYgKGxlbiA9PSBudWxsIHx8IGxlbiA+IGFyci5sZW5ndGgpIGxlbiA9IGFyci5sZW5ndGg7Zm9yICh2YXIgaSA9IDAsIGFycjIgPSBuZXcgQXJyYXkobGVuKTsgaSA8IGxlbjsgaSsrKSB7YXJyMltpXSA9IGFycltpXTt9cmV0dXJuIGFycjI7fQoKCgoKCmdldERhdGEoZnVuY3Rpb24gKHBhZ2VzKSB7dmFyIF9pdGVyYXRvciA9IF9jcmVhdGVGb3JPZkl0ZXJhdG9ySGVscGVyKAogICAgcGFnZXMpLF9zdGVwO3RyeSB7Zm9yIChfaXRlcmF0b3IucygpOyAhKF9zdGVwID0gX2l0ZXJhdG9yLm4oKSkuZG9uZTspIHt2YXIgcGFnZSA9IF9zdGVwLnZhbHVlOwogICAgICBmb3IgKHZhciBfaSA9IDAsIF9hcnIgPSBwYWdlLmNvbXBvbmVudHM7IF9pIDwgX2Fyci5sZW5ndGg7IF9pKyspIHt2YXIgY29tcG9uZW50ID0gX2FycltfaV07CiAgICAgICAgY29tcG9uZW50LnByZXZpb3VzVmFsdWUgPSBjb21wb25lbnQudmFsdWU7CgogICAgICAgIGlmIChjb21wb25lbnQudmFsaWRhdGUgIT0gbnVsbCkgewogICAgICAgICAgY29tcG9uZW50LnZhbGlkYXRlID0gZXZhbChjb21wb25lbnQudmFsaWRhdGUpOwogICAgICAgIH0KICAgICAgfQogICAgfX0gY2F0Y2ggKGVycikge19pdGVyYXRvci5lKGVycik7fSBmaW5hbGx5IHtfaXRlcmF0b3IuZigpO30KCiAgdmFyIGNvbnRhaW5lciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJyb290Iik7CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24KICB2YXIgcm9vdCA9ICgwLHJlYWN0X2RvbV9jbGllbnRfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzFfXy8qIC5jcmVhdGVSb290ICovIC5zKShjb250YWluZXIpOwogIHJvb3QucmVuZGVyKCAvKiNfX1BVUkVfXyovcmVhY3RfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzJfXy5jcmVhdGVFbGVtZW50KF9BcHBfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzBfXy8qIFsiZGVmYXVsdCJdICovIC5aLCB7IHBhZ2VzOiBwYWdlcyB9KSk7Cn0pOwp9KSgpOwoKLyoqKioqKi8gCXJldHVybiBfX3dlYnBhY2tfZXhwb3J0c19fOwovKioqKioqLyB9KSgpCjsKfSk7";/***/},/***/171:/***/module=>{module.exports="#relayContainer{margin:1rem;font-family:Arial,Helvetica,sans-serif}#notificationsContainer{pointer-events:none;position:fixed;top:5%;left:70%;transform:translateX(-50%);z-index:999}img{display:block;margin:auto}input.stringcontainer{background-color:#fff;border-radius:5px;border:1px solid #121943;height:24px;margin:auto;display:flex;padding-left:5px}input.interrupt{width:100%;border-color:darkred;background-color:#f1948a;color:darkred;margin:10px 0px;cursor:pointer}input.interrupt:hover{background-color:#f5867a}input.interrupt:active{background-color:#f77163}input.save,input.interrupt{margin:auto;display:block;font-size:x-large;padding:5px 25px;border-radius:10px}.notification{border:3px solid #00529b;background-color:#bde5f8;width:fit-content;margin-right:auto;margin-left:auto;border-radius:40px;color:#00529b;padding:7px 60px;animation:2s fadeOut 5s 1 forwards;cursor:default;pointer-events:auto}@keyframes fadeOut{0%{opacity:1}99%{height:100%}100%{opacity:0;height:0}}td{padding:.5rem}.toggle{align-items:center;border-radius:100px;display:flex;font-weight:700;margin-bottom:16px}.toggle:last-of-type{margin:0}.toggle__input{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.toggle__input:not([disabled]):active+.toggle-track,.toggle__input:not([disabled]):focus+.toggle-track{border:1px solid rgba(0,0,0,0);box-shadow:0px 0px 0px 2px #121943}.toggle__input:disabled+.toggle-track{cursor:not-allowed;opacity:.7}.toggle-track{background:#fff;border:1px solid #5a72b5;border-radius:100px;cursor:pointer;display:flex;height:30px;margin:auto;position:relative;width:60px}.toggle-indicator{align-items:center;background:#121943;border-radius:24px;bottom:2px;display:flex;height:24px;justify-content:center;left:2px;outline:solid 2px rgba(0,0,0,0);position:absolute;transition:.4s;width:24px}.checkMark{fill:#fff;height:20px;width:20px;opacity:0;transition:opacity .4s ease-in-out}.dropdowncontainer{padding:8px 16px;border:1px solid rgba(0,0,0,0);border-color:#000}input[value=true]+.toggle-track .toggle-indicator{background:#121943;transform:translateX(30px)}input[value=true]+.toggle-track .toggle-indicator .checkMark{opacity:1;transition:opacity .4s ease-in-out}@media screen and (-ms-high-contrast: active){.toggle-track{border-radius:0;background-color:red}}.topBar{display:flex}.tabEntry{margin-right:10px;border-radius:10px;background-color:#333}.tabEntry a{display:block;color:#fff;text-align:center;padding:6px;text-decoration:none}.tabEntry a[aria-current=page]{background-color:#00529b;border-radius:10px}.tabEntry a:hover[aria-current=page]{background-color:#002c53;border-radius:10px}.tabEntry a:hover{background-color:#111;border-radius:10px}.setting{position:relative}.setting .settingNameHover{visibility:hidden;background-color:#cacaca;color:#000;text-align:center;border-radius:6px;border-color:#000;position:absolute;z-index:1}.setting:hover .settingNameHover{visibility:visible;padding:5px;margin-left:15px}.hoverBox{position:relative;white-space:nowrap}.settingInput .settingDefaultHover{visibility:hidden;color:#666;position:absolute;z-index:1;left:50%;transform:translateX(-50%)}.settingInput:hover .settingDefaultHover{visibility:visible}.invalid-setting input,.invalid-setting select,.invalid-setting .toggle-track{background:pink !important}.invalid-reason{text-align:center}.invalid-reason small{padding:0px 5px;border-radius:10px;color:red}\n";/***/},/***/530:/***/module=>{module.exports=__webpack_require__(530);/***/}/******/};/************************************************************************/ /******/ // The module cache +/******/var __webpack_module_cache__={};/******/ /******/ // The require function +/******/function __nested_webpack_require_494289__(moduleId){/******/ // Check if module is in cache +/******/var cachedModule=__webpack_module_cache__[moduleId];/******/if(cachedModule!==undefined){/******/return cachedModule.exports;/******/}/******/ // Create a new module (and put it into the cache) +/******/var module=__webpack_module_cache__[moduleId]={/******/ // no module.id needed +/******/ // no module.loaded needed +/******/exports:{}/******/};/******/ /******/ // Execute the module function +/******/__webpack_modules__[moduleId](module,module.exports,__nested_webpack_require_494289__);/******/ /******/ // Return the exports of the module +/******/return module.exports;/******/}/******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/(()=>{/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/__nested_webpack_require_494289__.n=module=>{/******/var getter=module&&module.__esModule?/******/()=>module['default']:/******/()=>module;/******/__nested_webpack_require_494289__.d(getter,{a:getter});/******/return getter;/******/};/******/})();/******/ /******/ /* webpack/runtime/define property getters */ /******/(()=>{/******/ // define getter functions for harmony exports +/******/__nested_webpack_require_494289__.d=(exports,definition)=>{/******/for(var key in definition){/******/if(__nested_webpack_require_494289__.o(definition,key)&&!__nested_webpack_require_494289__.o(exports,key)){/******/Object.defineProperty(exports,key,{enumerable:true,get:definition[key]});/******/}/******/}/******/};/******/})();/******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/(()=>{/******/__nested_webpack_require_494289__.o=(obj,prop)=>Object.prototype.hasOwnProperty.call(obj,prop);/******/})();/******/ /******/ /* webpack/runtime/make namespace object */ /******/(()=>{/******/ // define __esModule on exports +/******/__nested_webpack_require_494289__.r=exports=>{/******/if(typeof Symbol!=='undefined'&&Symbol.toStringTag){/******/Object.defineProperty(exports,Symbol.toStringTag,{value:'Module'});/******/}/******/Object.defineProperty(exports,'__esModule',{value:true});/******/};/******/})();/******/ /************************************************************************/var __nested_webpack_exports__={};// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(()=>{__nested_webpack_require_494289__.r(__nested_webpack_exports__);/* harmony export */__nested_webpack_require_494289__.d(__nested_webpack_exports__,{/* harmony export */"generateHTML":()=>/* binding */_generateHTML,/* harmony export */"getPagePath":()=>/* binding */_getPagePath,/* harmony export */"handleApiRequest":()=>/* binding */_handleApiRequest,/* harmony export */"parseCssFromFile":()=>/* binding */_parseCssFromFile,/* harmony export */"parsePageFromFile":()=>/* binding */_parsePageFromFile,/* harmony export */"parsePageFromJson":()=>/* binding */_parsePageFromJson/* harmony export */});/* harmony import */var kolmafia__WEBPACK_IMPORTED_MODULE_0__=__nested_webpack_require_494289__(530);/* harmony import */var kolmafia__WEBPACK_IMPORTED_MODULE_0___default=/*#__PURE__*/__nested_webpack_require_494289__.n(kolmafia__WEBPACK_IMPORTED_MODULE_0__);function _toConsumableArray(arr){return _arrayWithoutHoles(arr)||_iterableToArray(arr)||_unsupportedIterableToArray(arr)||_nonIterableSpread();}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _iterableToArray(iter){if(typeof Symbol!=="undefined"&&iter[Symbol.iterator]!=null||iter["@@iterator"]!=null)return Array.from(iter);}function _arrayWithoutHoles(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr);}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_unsupportedIterableToArray(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _iterableToArrayLimit(arr,i){var _i=arr==null?null:typeof Symbol!=="undefined"&&arr[Symbol.iterator]||arr["@@iterator"];if(_i==null)return;var _arr=[];var _n=true;var _d=false;var _s,_e;try{for(_i=_i.call(arr);!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}function _createForOfIteratorHelper(o,allowArrayLike){var it=typeof Symbol!=="undefined"&&o[Symbol.iterator]||o["@@iterator"];if(!it){if(Array.isArray(o)||(it=_unsupportedIterableToArray(o))||allowArrayLike&&o&&typeof o.length==="number"){if(it)o=it;var i=0;var F=function F(){};return{s:F,n:function n(){if(i>=o.length)return{done:true};return{done:false,value:o[i++]};},e:function e(_e2){throw _e2;},f:F};}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}var normalCompletion=true,didErr=false,err;return{s:function s(){it=it.call(o);},n:function n(){var step=it.next();normalCompletion=step.done;return step;},e:function e(_e3){didErr=true;err=_e3;},f:function f(){try{if(!normalCompletion&&it.return!=null)it.return();}finally{if(didErr)throw err;}}};}function _unsupportedIterableToArray(o,minLen){if(!o)return;if(typeof o==="string")return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);if(n==="Object"&&o.constructor)n=o.constructor.name;if(n==="Map"||n==="Set")return Array.from(o);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen);}function _arrayLikeToArray(arr,len){if(len==null||len>arr.length)len=arr.length;for(var i=0,arr2=new Array(len);ip!=null);var _iterator3=_createForOfIteratorHelper(pages),_step3;try{for(_iterator3.s();!(_step3=_iterator3.n()).done;){var _page$file;var page=_step3.value;page.file=(_page$file=page.file)!==null&&_page$file!==void 0?_page$file:page.page;validateComponents(page.components);}}catch(err){_iterator3.e(err);}finally{_iterator3.f();}var buffer=[];var cssFiles=[];if(extraHtml&&extraHtml.cssFiles){cssFiles.push.apply(cssFiles,_toConsumableArray(extraHtml.cssFiles));}buffer.push("");cssFiles.forEach(s=>{buffer.push(""));});buffer.push("");buffer.push("");buffer.push('
');buffer.push("");// include react script +buffer.push(""));return buffer.join("\n");}function _getPagePath(file){var fileName=file.includes("/")?file:"relay/shared_relay/pages/"+file+".json";if(!fileName.endsWith(".json")&&(0,kolmafia__WEBPACK_IMPORTED_MODULE_0__.fileToBuffer)(fileName).length==0){fileName+=".json";}return fileName;}/** + * Parses from relay/shared_relay/pages/ if no slashes are in the name, otherwise expects a valid json file + */function _parsePageFromFile(file){var data=(0,kolmafia__WEBPACK_IMPORTED_MODULE_0__.fileToBuffer)(_getPagePath(file));return _parsePageFromJson(file,data);}function _parseCssFromFile(file){var fileName=file.includes("/")?file:"shared_relay/pages/"+file+".css";if(!fileName.endsWith(".css")){fileName+=".css";}var data=(0,kolmafia__WEBPACK_IMPORTED_MODULE_0__.fileToBuffer)(fileName);if(data.length==0){return null;}return fileName;}function _parsePageFromJson(id,jsonData){if(jsonData.length==0){return null;}var subpage=JSON.parse(jsonData);subpage.file=id;for(var _i2=0,_arr2=subpage.components;_i2<_arr2.length;_i2++){var _button$dropdown;var button=_arr2[_i2];if(button.type!="dropdown"){continue;}if(button.dropdown==null){button.dropdown=[];}else if(typeof button.dropdown[0]=="string"){button.dropdown=button.dropdown.map(s=>{return{display:s,value:s};});}if(button.dropdownFiller==null){continue;}var data=eval(button.dropdownFiller).map(_ref=>{var _ref2=_slicedToArray(_ref,2),display=_ref2[0],value=_ref2[1];return{display:display,value:value};});(_button$dropdown=button.dropdown).push.apply(_button$dropdown,_toConsumableArray(data));}return subpage;}})();var __webpack_export_target__=exports;for(var i in __nested_webpack_exports__)__webpack_export_target__[i]=__nested_webpack_exports__[i];if(__nested_webpack_exports__.__esModule)Object.defineProperty(__webpack_export_target__,"__esModule",{value:true});/******/})(); + +/***/ }), + +/***/ 530: +/***/ ((module) => { + +"use strict"; +module.exports = require("kolmafia"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +// ESM COMPAT FLAG +__webpack_require__.r(__webpack_exports__); + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + main: () => (/* binding */ main) +}); + +// EXTERNAL MODULE: external "kolmafia" +var external_kolmafia_ = __webpack_require__(530); +;// CONCATENATED MODULE: ./node_modules/grimoire-kolmafia/dist/args.js +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +/* eslint-disable @typescript-eslint/no-explicit-any */ + +var Args = /*#__PURE__*/function () { + function Args() { + _classCallCheck(this, Args); + } + _createClass(Args, null, [{ + key: "custom", + value: function custom(spec, _parser, valueHelpName) { + var _a, _b; + var raw_options = (_a = spec.options) === null || _a === void 0 ? void 0 : _a.map(option => option[0]); + // Check that the default value actually appears in the options. + if ("default" in spec && raw_options) { + if (!raw_options.includes(spec.default)) { + throw "Invalid default value ".concat(spec.default); + } + } + return _objectSpread(_objectSpread({}, spec), {}, { + valueHelpName: valueHelpName, + parser: value => { + var parsed_value = _parser(value); + if (parsed_value === undefined || parsed_value instanceof ParseError) return parsed_value; + if (raw_options) { + if (!raw_options.includes(parsed_value)) { + return new ParseError("received ".concat(value, " which was not in the allowed options")); + } + } + return parsed_value; + }, + options: (_b = spec.options) === null || _b === void 0 ? void 0 : _b.map(a => ["".concat(a[0]), a[1]]) + }); + } + }, { + key: "arrayFromArg", + value: function arrayFromArg(spec, argFromSpec) { + var _a, _b, _c; + // First, construct a non-array version of this argument. + // We do this by calling argFromSpec in order to extract the parser and + // valueHelpName (to make it easier to define the functions below). + // + // The default argument of an ArraySpec is of type T[], which causes + // problems, so we must remove it. + var spec_without_default = _objectSpread({}, spec); // Avoid "the operand of a 'delete' operator must be optional" + if ("default" in spec_without_default) delete spec_without_default["default"]; + var arg = argFromSpec.call(this, spec_without_default); + // Next, check that all default values actually appear in the options. + var raw_options = (_a = spec.options) === null || _a === void 0 ? void 0 : _a.map(option => option[0]); + if ("default" in spec && raw_options) { + var _iterator = _createForOfIteratorHelper(spec.default), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var default_entry = _step.value; + if (!raw_options.includes(default_entry)) throw "Invalid default value ".concat(spec.default); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } + var separator = (_b = spec.separator) !== null && _b !== void 0 ? _b : ","; + var arrayParser = value => { + // Split the array + var values = value.split(separator); + if (!spec.noTrim) values = values.map(v => v.trim()); + // Parse all values, return the first error found if any + var result = values.map(v => arg.parser(v)); + var error = result.find(v => v instanceof ParseError); + if (error) return error; + var failure_index = result.indexOf(undefined); + if (failure_index !== -1) return new ParseError("components expected ".concat(arg.valueHelpName, " but could not parse ").concat(values[failure_index])); + // Otherwise, all values are good + return result; + }; + return _objectSpread(_objectSpread({}, spec), {}, { + valueHelpName: "".concat(arg.valueHelpName).concat(separator, " ").concat(arg.valueHelpName).concat(separator, " ..."), + parser: arrayParser, + options: (_c = spec.options) === null || _c === void 0 ? void 0 : _c.map(a => ["".concat(a[0]), a[1]]) + }); + } + }, { + key: "string", + value: function string(spec) { + return this.custom(spec, value => value, "TEXT"); + } + }, { + key: "strings", + value: function strings(spec) { + return this.arrayFromArg(spec, this.string); + } + }, { + key: "number", + value: function number(spec) { + return this.custom(spec, value => isNaN(Number(value)) ? undefined : Number(value), "NUMBER"); + } + }, { + key: "numbers", + value: function numbers(spec) { + return this.arrayFromArg(spec, this.number); + } + }, { + key: "boolean", + value: function boolean(spec) { + return this.custom(spec, value => { + if (value.toLowerCase() === "true") return true; + if (value.toLowerCase() === "false") return false; + return undefined; + }, "BOOLEAN"); + } + }, { + key: "booleans", + value: function booleans(spec) { + return this.arrayFromArg(spec, this.boolean); + } + }, { + key: "flag", + value: function flag(spec) { + return this.custom(spec, value => { + if (value.toLowerCase() === "true") return true; + if (value.toLowerCase() === "false") return false; + return undefined; + }, "FLAG"); + } + }, { + key: "class", + value: function _class(spec) { + return this.custom(spec, value => { + var match = external_kolmafia_.Class.get(value); + // Class.get does fuzzy matching: + // e.g. Class.get("sc") returns disco bandit. + // To avoid this foot-gun, only return exact matches or id lookups. + if (match.toString().toUpperCase() === value.toString().toUpperCase()) return match; + if (!isNaN(Number(value))) return match; + return undefined; + }, "CLASS"); + } + }, { + key: "classes", + value: function classes(spec) { + return this.arrayFromArg(spec, this.class); + } + }, { + key: "effect", + value: function effect(spec) { + return this.custom(spec, external_kolmafia_.Effect.get, "EFFECT"); + } + }, { + key: "effects", + value: function effects(spec) { + return this.arrayFromArg(spec, this.effect); + } + }, { + key: "familiar", + value: function familiar(spec) { + return this.custom(spec, external_kolmafia_.Familiar.get, "FAMILIAR"); + } + }, { + key: "familiars", + value: function familiars(spec) { + return this.arrayFromArg(spec, this.familiar); + } + }, { + key: "item", + value: function item(spec) { + return this.custom(spec, external_kolmafia_.Item.get, "ITEM"); + } + }, { + key: "items", + value: function items(spec) { + return this.arrayFromArg(spec, this.item); + } + }, { + key: "location", + value: function location(spec) { + return this.custom(spec, external_kolmafia_.Location.get, "LOCATION"); + } + }, { + key: "locations", + value: function locations(spec) { + return this.arrayFromArg(spec, this.location); + } + }, { + key: "monster", + value: function monster(spec) { + return this.custom(spec, external_kolmafia_.Monster.get, "MONSTER"); + } + }, { + key: "monsters", + value: function monsters(spec) { + return this.arrayFromArg(spec, this.monster); + } + }, { + key: "path", + value: function path(spec) { + return this.custom(spec, external_kolmafia_.Path.get, "PATH"); + } + }, { + key: "paths", + value: function paths(spec) { + return this.arrayFromArg(spec, this.path); + } + }, { + key: "skill", + value: function skill(spec) { + return this.custom(spec, external_kolmafia_.Skill.get, "SKILL"); + } + }, { + key: "skills", + value: function skills(spec) { + return this.arrayFromArg(spec, this.skill); + } + /** + * Create a group of arguments that will be printed separately in the help. + * + * Note that keys in the group must still be globally distinct. + * + * @param groupName The display name for the group in help. + * @param args A JS object specifying the script arguments. Its values should + * be {@link Arg} objects (created by Args.string, Args.number, or others) + * or groups of arguments (created by Args.group). + */ + }, { + key: "group", + value: function group(groupName, args) { + return { + name: groupName, + args: args + }; + } + /** + * Create a set of input arguments for a script. + * @param scriptName Prefix for property names; often the name of the script. + * @param scriptHelp Brief description of this script, for the help message. + * @param args A JS object specifying the script arguments. Its values should + * be {@link Arg} objects (created by Args.string, Args.number, or others) + * or groups of arguments (created by Args.group). + * @param options Config options for the args and arg parser. + * @returns An object which can hold parsed argument values. The keys of this + * object are identical to the keys in 'args'. + */ + }, { + key: "create", + value: function create(scriptName, scriptHelp, args, options) { + var _objectSpread2; + _traverse(args, (keySpec, key) => { + if (key === "help" || keySpec.key === "help") throw "help is a reserved argument name"; + }); + var argsWithHelp = _objectSpread(_objectSpread({}, args), {}, { + help: this.flag({ + help: "Show this message and exit.", + setting: "" + }) + }); + // Create an object to hold argument results, with a default value for + // each argument. + var res = _objectSpread(_objectSpread({}, _loadDefaultValues(argsWithHelp)), {}, (_objectSpread2 = {}, _defineProperty(_objectSpread2, specSymbol, argsWithHelp), _defineProperty(_objectSpread2, scriptSymbol, scriptName), _defineProperty(_objectSpread2, scriptHelpSymbol, scriptHelp), _defineProperty(_objectSpread2, optionsSymbol, options !== null && options !== void 0 ? options : {}), _objectSpread2)); + if (options === null || options === void 0 ? void 0 : options.positionalArgs) { + var keys = []; + var metadata = Args.getMetadata(res); + metadata.traverse((keySpec, key) => { + var _a; + keys.push((_a = keySpec.key) !== null && _a !== void 0 ? _a : key); + }); + var _iterator2 = _createForOfIteratorHelper(options.positionalArgs), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var arg = _step2.value; + if (!keys.includes(arg)) throw "Unknown key for positional arg: ".concat(arg); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } + return res; + } + /** + * Parse the command line input into the provided script arguments. + * @param args An object to hold the parsed argument values, from Args.create(*). + * @param command The command line input. + * @param includeSettings If true, parse values from settings as well. + */ + }, { + key: "fill", + value: function fill(args, command) { + var includeSettings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + var _a; + var metadata = Args.getMetadata(args); + // Load the list of keys and flags from the arg spec + var keys = new Set(); + var flags = new Set(); + metadata.traverse((keySpec, key) => { + var _a; + var name = (_a = keySpec.key) !== null && _a !== void 0 ? _a : key; + if (flags.has(name) || keys.has(name)) throw "Duplicate arg key ".concat(name, " is not allowed"); + if (keySpec.valueHelpName === "FLAG") flags.add(name);else keys.add(name); + }); + // Parse values from settings. + if (includeSettings) { + metadata.traverseAndMaybeSet(args, (keySpec, key) => { + var _a, _b; + var setting = (_a = keySpec.setting) !== null && _a !== void 0 ? _a : "".concat(metadata.scriptName, "_").concat((_b = keySpec.key) !== null && _b !== void 0 ? _b : key); + if (setting === "") return undefined; // no setting + var value_str = (0,external_kolmafia_.getProperty)(setting); + if (value_str === "") return undefined; // no setting + return parseAndValidate(keySpec, "Setting ".concat(setting), value_str); + }); + } + // Parse new argments from the command line + if (command === undefined || command === "") return; + var parsed = new CommandParser(command, keys, flags, (_a = metadata.options.positionalArgs) !== null && _a !== void 0 ? _a : []).parse(); + metadata.traverseAndMaybeSet(args, (keySpec, key) => { + var _a; + var argKey = (_a = keySpec.key) !== null && _a !== void 0 ? _a : key; + var value_str = parsed.get(argKey); + if (value_str === undefined) return undefined; // no setting + return parseAndValidate(keySpec, "Argument ".concat(argKey), value_str); + }); + } + /** + * Parse command line input into a new set of script arguments. + * @param scriptName Prefix to use in property names; typically the name of the script. + * @param scriptHelp Brief description of this script, for the help message. + * @param spec An object specifying the script arguments. + * @param command The command line input. + * @param options Config options for the args and arg parser. + */ + }, { + key: "parse", + value: function parse(scriptName, scriptHelp, spec, command, options) { + var args = this.create(scriptName, scriptHelp, spec, options); + this.fill(args, command); + return args; + } + /** + * Print a description of the script arguments to the CLI. + * + * First, all top-level argument descriptions are printed in the order they + * were defined. Afterwards, descriptions for groups of arguments are printed + * in the order they were defined. + * + * @param args An object of parsed arguments, from Args.create(*). + * @param maxOptionsToDisplay If given, do not list more than this many options for each arg. + */ + }, { + key: "showHelp", + value: function showHelp(args, maxOptionsToDisplay) { + var _a; + var metadata = Args.getMetadata(args); + (0,external_kolmafia_.printHtml)("".concat(metadata.scriptHelp)); + (0,external_kolmafia_.printHtml)(""); + (0,external_kolmafia_.printHtml)("".concat((_a = metadata.options.defaultGroupName) !== null && _a !== void 0 ? _a : "Options", ":")); + metadata.traverse((arg, key) => { + var _a, _b, _c, _d, _e; + if (arg.hidden) return; + var nameText = "").concat((_a = arg.key) !== null && _a !== void 0 ? _a : key, ""); + var valueText = arg.valueHelpName === "FLAG" ? "" : "".concat(arg.valueHelpName, ""); + var helpText = (_b = arg.help) !== null && _b !== void 0 ? _b : ""; + var defaultText = "default" in arg ? "[default: ".concat(arg.default, "]") : ""; + var settingText = arg.setting === "" ? "" : "[setting: ".concat((_c = arg.setting) !== null && _c !== void 0 ? _c : "".concat(metadata.scriptName, "_").concat((_d = arg.key) !== null && _d !== void 0 ? _d : key), "]"); + (0,external_kolmafia_.printHtml)("  ".concat([nameText, valueText, "-", helpText, defaultText, settingText].join(" "))); + var valueOptions = (_e = arg.options) !== null && _e !== void 0 ? _e : []; + if (valueOptions.length < (maxOptionsToDisplay !== null && maxOptionsToDisplay !== void 0 ? maxOptionsToDisplay : Number.MAX_VALUE)) { + var _iterator3 = _createForOfIteratorHelper(valueOptions), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var option = _step3.value; + if (option.length === 1 || option[1] === undefined) { + (0,external_kolmafia_.printHtml)("    ".concat(nameText, " ").concat(option[0])); + } else { + (0,external_kolmafia_.printHtml)("    ".concat(nameText, " ").concat(option[0], " - ").concat(option[1])); + } + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + } + }, group => { + (0,external_kolmafia_.printHtml)(""); + (0,external_kolmafia_.printHtml)("".concat(group.name, ":")); + }); + } + /** + * Load the metadata information for a set of arguments. Only for advanced usage. + * + * @param args A JS object specifying the script arguments. Its values should + * be {@link Arg} objects (created by Args.string, Args.number, or others) + * or groups of arguments (created by Args.group). + * @returns A class containing metadata information. + */ + }, { + key: "getMetadata", + value: function getMetadata(args) { + return new WrappedArgMetadata(args); + } + }]); + return Args; +}(); +var ParseError = /*#__PURE__*/_createClass(function ParseError(message) { + _classCallCheck(this, ParseError); + this.message = message; +}); +/** + * Metadata for the parsed arguments. + * + * This information is hidden within the parsed argument object so that it + * is invisible to the user but available to fill(*) and showHelp(*). + */ +var specSymbol = Symbol("spec"); +var scriptSymbol = Symbol("script"); +var scriptHelpSymbol = Symbol("scriptHelp"); +var optionsSymbol = Symbol("options"); +/** + * Parse a string into a value for a given argument, throwing if the parsing fails. + * @param arg An argument that takes values in T. + * @param source A description of where this value came from, for the error message. + * @param value The value to parse. + * @returns the parsed value. + */ +function parseAndValidate(arg, source, value) { + var parsed_value; + try { + parsed_value = arg.parser(value); + } catch (_a) { + parsed_value = undefined; + } + if (parsed_value === undefined) throw "".concat(source, " expected ").concat(arg.valueHelpName, " but could not parse ").concat(value); + if (parsed_value instanceof ParseError) throw "".concat(source, " ").concat(parsed_value.message); + return parsed_value; +} +/** + * A class that reveals the hidden metadata and specs for arguments. + * + * Only for advanced usage. + */ +var WrappedArgMetadata = /*#__PURE__*/function () { + function WrappedArgMetadata(args) { + _classCallCheck(this, WrappedArgMetadata); + this.spec = args[specSymbol]; + this.scriptName = args[scriptSymbol]; + this.scriptHelp = args[scriptHelpSymbol]; + this.options = args[optionsSymbol]; + } + /** + * Create a parsed args object from this spec using all default values. + */ + _createClass(WrappedArgMetadata, [{ + key: "loadDefaultValues", + value: function loadDefaultValues() { + return _loadDefaultValues(this.spec); + } + /** + * Traverse the spec and possibly generate a value for each argument. + * + * @param result The object to hold the resulting argument values, typically + * the result of loadDefaultValues(). + * @param setTo A function to generate an argument value from each arg spec. + * If this function returns undefined, then the argument value is unchanged. + */ + }, { + key: "traverseAndMaybeSet", + value: function traverseAndMaybeSet(result, setTo) { + return _traverseAndMaybeSet(this.spec, result, setTo); + } + /** + * Traverse the spec and call a method for each argument. + * + * @param process A function to call at each arg spec. + */ + }, { + key: "traverse", + value: function traverse(process, onGroup) { + return _traverse(this.spec, process, onGroup); + } + }]); + return WrappedArgMetadata; +}(); +/** + * Create a parsed args object from a spec using all default values. + * + * @param spec The spec for all arguments. + */ +function _loadDefaultValues(spec) { + var result = {}; + for (var k in spec) { + var argSpec = spec[k]; + if ("args" in argSpec) { + result[k] = _loadDefaultValues(argSpec.args); + } else { + if ("default" in argSpec) result[k] = argSpec.default;else result[k] = undefined; + } + } + return result; +} +/** + * Traverse the spec and possibly generate a value for each argument. + * + * @param spec The spec for all arguments. + * @param result The object to hold the resulting argument values. + * @param setTo A function to generate an argument value from each arg spec. + * If this function returns undefined, then the argument value is unchanged. + */ +function _traverseAndMaybeSet(spec, result, setTo) { + var groups = []; + for (var k in spec) { + var argSpec = spec[k]; + if ("args" in argSpec) { + groups.push([argSpec, k]); + } else { + var value = setTo(argSpec, k); + if (value === undefined) continue; + result[k] = value; + } + } + for (var _i = 0, _groups = groups; _i < _groups.length; _i++) { + var group_and_key = _groups[_i]; + _traverseAndMaybeSet(group_and_key[0].args, result[group_and_key[1]], setTo); + } +} +/** + * Traverse the spec and possibly generate a value for each argument. + * + * @param spec The spec for all arguments. + * @param process A function to call at each arg spec. + */ +function _traverse(spec, process, onGroup) { + var groups = []; + for (var k in spec) { + var argSpec = spec[k]; + if ("args" in argSpec) { + groups.push([argSpec, k]); + } else { + process(argSpec, k); + } + } + for (var _i2 = 0, _groups2 = groups; _i2 < _groups2.length; _i2++) { + var group_and_key = _groups2[_i2]; + onGroup === null || onGroup === void 0 ? void 0 : onGroup(group_and_key[0], group_and_key[1]); + _traverse(group_and_key[0].args, process, onGroup); + } +} +/** + * A parser to extract key/value pairs from a command line input. + * @member command The command line input. + * @member keys The set of valid keys that can appear. + * @member flags The set of valid flags that can appear. + * @member index An internal marker for the progress of the parser over the input. + */ +var CommandParser = /*#__PURE__*/function () { + function CommandParser(command, keys, flags, positionalArgs) { + _classCallCheck(this, CommandParser); + this.command = command; + this.index = 0; + this.keys = keys; + this.flags = flags; + this.positionalArgs = positionalArgs; + this.positionalArgsParsed = 0; + } + /** + * Perform the parsing of (key, value) pairs. + * @returns The set of extracted (key, value) pairs. + */ + _createClass(CommandParser, [{ + key: "parse", + value: function parse() { + var _a, _b, _c, _d; + this.index = 0; // reset the parser + var result = new Map(); + while (!this.finished()) { + // A flag F may appear as !F to be parsed as false. + var parsing_negative_flag = false; + if (this.peek() === "!") { + parsing_negative_flag = true; + this.consume(["!"]); + } + var startIndex = this.index; + var key = this.parseKey(); + if (result.has(key)) { + throw "Duplicate key ".concat(key, " (first set to ").concat((_a = result.get(key)) !== null && _a !== void 0 ? _a : "", ")"); + } + if (this.flags.has(key)) { + // The key corresponds to a flag. + // Parse [key] as true and ![key] as false. + result.set(key, parsing_negative_flag ? "false" : "true"); + if (this.peek() === "=") throw "Flag ".concat(key, " cannot be assigned a value"); + if (!this.finished()) this.consume([" "]); + this.prevUnquotedKey = undefined; + } else if (this.keys.has(key)) { + // Parse [key]=[value] or [key] [value] + this.consume(["=", " "]); + var value = this.parseValue(); + if (["'", '"'].includes((_b = this.prev()) !== null && _b !== void 0 ? _b : "")) this.prevUnquotedKey = undefined;else this.prevUnquotedKey = key; + if (!this.finished()) this.consume([" "]); + result.set(key, value); + } else if (this.positionalArgsParsed < this.positionalArgs.length && this.peek() !== "=") { + // Parse [value] as the next positional arg + var positionalKey = this.positionalArgs[this.positionalArgsParsed]; + this.positionalArgsParsed++; + this.index = startIndex; // back up to reparse the key as a value + var _value = this.parseValue(); + if (["'", '"'].includes((_c = this.prev()) !== null && _c !== void 0 ? _c : "")) this.prevUnquotedKey = undefined;else this.prevUnquotedKey = key; + if (!this.finished()) this.consume([" "]); + if (result.has(positionalKey)) throw "Cannot assign ".concat(_value, " to ").concat(positionalKey, " (positionally) since ").concat(positionalKey, " was already set to ").concat((_d = result.get(positionalKey)) !== null && _d !== void 0 ? _d : ""); + result.set(positionalKey, _value); + } else { + // Key not found; include a better error message if it is possible for quotes to have been missed + if (this.prevUnquotedKey && this.peek() !== "=") throw "Unknown argument: ".concat(key, " (if this should have been parsed as part of ").concat(this.prevUnquotedKey, ", you should surround the entire value in quotes)");else throw "Unknown argument: ".concat(key); + } + } + return result; + } + /** + * @returns True if the entire command has been parsed. + */ + }, { + key: "finished", + value: function finished() { + return this.index >= this.command.length; + } + /** + * @returns The next character to parse, if it exists. + */ + }, { + key: "peek", + value: function peek() { + if (this.index >= this.command.length) return undefined; + return this.command.charAt(this.index); + } + /** + * @returns The character just parsed, if it exists. + */ + }, { + key: "prev", + value: function prev() { + if (this.index <= 0) return undefined; + if (this.index >= this.command.length + 1) return undefined; + return this.command.charAt(this.index - 1); + } + /** + * Advance the internal marker over the next expected character. + * Throws an error on unexpected characters. + * + * @param allowed Characters that are expected. + */ + }, { + key: "consume", + value: function consume(allowed) { + var _a; + if (this.finished()) throw "Expected ".concat(allowed); + if (allowed.includes((_a = this.peek()) !== null && _a !== void 0 ? _a : "")) { + this.index += 1; + } + } + /** + * Find the next occurance of one of the provided characters, or the end of + * the string if the characters never appear again. + * + * @param searchValue The characters to locate. + */ + }, { + key: "findNext", + value: function findNext(searchValue) { + var result = this.command.length; + var _iterator4 = _createForOfIteratorHelper(searchValue), + _step4; + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var value = _step4.value; + var index = this.command.indexOf(value, this.index); + if (index !== -1 && index < result) result = index; + } + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + return result; + } + /** + * Starting from the internal marker, parse a single key. + * This also advances the internal marker. + * + * @returns The next key. + */ + }, { + key: "parseKey", + value: function parseKey() { + var keyEnd = this.findNext(["=", " "]); + var key = this.command.substring(this.index, keyEnd); + this.index = keyEnd; + return key; + } + /** + * Starting from the internal marker, parse a single value. + * This also advances the internal marker. + * + * Values are a single word or enclosed in matching quotes, i.e. one of: + * "[^"]*" + * '[^']*" + * [^'"][^ ]* + * + * @returns The next value. + */ + }, { + key: "parseValue", + value: function parseValue() { + var _a, _b; + var valueEnder = " "; + var quotes = ["'", '"']; + if (quotes.includes((_a = this.peek()) !== null && _a !== void 0 ? _a : "")) { + valueEnder = (_b = this.peek()) !== null && _b !== void 0 ? _b : ""; // The value is everything until the next quote + this.consume([valueEnder]); // Consume opening quote + } + + var valueEnd = this.findNext([valueEnder]); + var value = this.command.substring(this.index, valueEnd); + if (valueEnder !== " " && valueEnd === this.command.length) { + throw "No closing ".concat(valueEnder, " found for ").concat(valueEnder).concat(value); + } + // Consume the value (and closing quote) + this.index = valueEnd; + if (valueEnder !== " ") this.consume([valueEnder]); + return value; + } + }]); + return CommandParser; +}(); +;// CONCATENATED MODULE: ./node_modules/libram/dist/utils.js +function utils_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = utils_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || utils_unsupportedIterableToArray(arr, i) || _nonIterableRest(); } +function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || utils_unsupportedIterableToArray(arr) || _nonIterableSpread(); } +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function utils_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return utils_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return utils_arrayLikeToArray(o, minLen); } +function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return utils_arrayLikeToArray(arr); } +function utils_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +/** + * Type guard against null value + * + * @param value Value that can be null + * @returns Whether the value is not null or... not + */ +function notNull(value) { + return value !== null; +} +/** + * Parse string to number, stripping commas + * + * @param n Numberical string to parse + * @returns Numerical value of string + */ +function parseNumber(n) { + return Number.parseInt(n.replace(/,/g, "")); +} +/** + * Clamp a number between lower and upper bounds. + * + * @param n Number to clamp. + * @param min Lower bound. + * @param max Upper bound. + * @returns Clamped value + */ +function clamp(n, min, max) { + return Math.max(min, Math.min(max, n)); +} +/** + * Split an {@param array} into {@param chunkSize} sized chunks + * + * @param array Array to split + * @param chunkSize Size of chunk + * @returns Split array + */ +function utils_chunk(array, chunkSize) { + var result = []; + for (var i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + return result; +} +/** + * Count distinct values in an array + * + * @param array Array of values + * @returns Map of distinct values to count + */ +function arrayToCountedMap(array) { + if (!Array.isArray(array)) return array; + var map = new Map(); + array.forEach(item => { + map.set(item, (map.get(item) || 0) + 1); + }); + return map; +} +/** + * Turn map of distinct values to count into array of values + * + * @param map Map to turn into array + * @returns Array of values + */ +function countedMapToArray(map) { + var _ref; + return (_ref = []).concat.apply(_ref, _toConsumableArray(_toConsumableArray(map).map(_ref2 => { + var _ref3 = _slicedToArray(_ref2, 2), + item = _ref3[0], + quantity = _ref3[1]; + return Array(quantity).fill(item); + }))); +} +/** + * Stringify a counted map + * + * @param map Map of counted values + * @returns String representing map of counted values + */ +function countedMapToString(map) { + return _toConsumableArray(map).map(_ref4 => { + var _ref5 = _slicedToArray(_ref4, 2), + item = _ref5[0], + quantity = _ref5[1]; + return "".concat(quantity, " x ").concat(item); + }).join(", "); +} +/** + * Sum an array of numbers. + * + * @param addends Addends to sum. + * @param x Property or mapping function of addends to sum + * @returns Sum of numbers + */ +function sum(addends, x) { + return addends.reduce((subtotal, element) => subtotal + (typeof x === "function" ? x(element) : element[x]), 0); +} +/** + * Sum array of numbers + * + * @param addends Numbers to sum + * @returns Sum of numbers + */ +function sumNumbers(addends) { + return sum(addends, x => x); +} +/** + * Checks if a given item is in a readonly array, acting as a typeguard. + * + * @param item Needle + * @param array Readonly array haystack + * @returns Whether the item is in the array, and narrows the type of the item. + */ +function arrayContains(item, array) { + return array.includes(item); +} +/** + * Checks if two arrays contain the same elements in the same quantity. + * + * @param a First array for comparison + * @param b Second array for comparison + * @returns Whether the two arrays are equal, irrespective of order. + */ +function setEqual(a, b) { + var sortedA = _toConsumableArray(a).sort(); + var sortedB = _toConsumableArray(b).sort(); + return a.length === b.length && sortedA.every((item, index) => item === sortedB[index]); +} +/** + * Reverses keys and values for a given map + * + * @param map Map to invert + * @returns Inverted map + */ +function invertMap(map) { + var returnValue = new Map(); + var _iterator = utils_createForOfIteratorHelper(map), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var _step$value = _slicedToArray(_step.value, 2), + key = _step$value[0], + value = _step$value[1]; + returnValue.set(value, key); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return returnValue; +} +/** + * Splits a string by commas while also respecting escaping commas with a backslash + * + * @param str String to split + * @returns List of tokens + */ +function splitByCommasWithEscapes(str) { + var returnValue = []; + var ignoreNext = false; + var currentString = ""; + var _iterator2 = utils_createForOfIteratorHelper(str.split("")), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var char = _step2.value; + if (char === "\\") { + ignoreNext = true; + } else { + if (char == "," && !ignoreNext) { + returnValue.push(currentString.trim()); + currentString = ""; + } else { + currentString += char; + } + ignoreNext = false; + } + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + returnValue.push(currentString.trim()); + return returnValue; +} +/** + * Find the best element of an array, where "best" is defined by some given criteria. + * + * @param array The array to traverse and find the best element of. + * @param optimizer Either a key on the objects we're looking at that corresponds to numerical values, or a function for mapping these objects to numbers. Essentially, some way of assigning value to the elements of the array. + * @param reverse Make this true to find the worst element of the array, and false to find the best. Defaults to false. + * @returns Best element by optimizer function + */ +function maxBy(array, optimizer) { + var reverse = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + if (!array.length) throw new Error("Cannot call maxBy on an empty array!"); + if (typeof optimizer === "function") { + return _toConsumableArray(array).reduce((_ref6, other) => { + var value = _ref6.value, + item = _ref6.item; + var otherValue = optimizer(other); + return value >= otherValue !== reverse ? { + value: value, + item: item + } : { + value: otherValue, + item: other + }; + }, { + item: array[0], + value: optimizer(array[0]) + }).item; + } else { + return array.reduce((a, b) => a[optimizer] >= b[optimizer] !== reverse ? a : b); + } +} +/** + * Compare arrays shallowly + * + * @param left One array to compare + * @param right The other array to compare + * @returns Whether the two arrays are shallowly equal + */ +function arrayEquals(left, right) { + if (left.length !== right.length) return false; + return left.every((element, index) => element === right[index]); +} +/** + * Used to collapse a Delayed object into an entity of type "T" as represented by the object. + * + * @param delayedObject Object of type Delayed that represents either a value of type T or a function returning a value of type T. + * @param args The arguments to pass to the delay function + * @returns The return value of the function, if delayedObject is a function. Otherwise, this returns the original element. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function undelay(delayedObject) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + return typeof delayedObject === "function" ? delayedObject.apply(void 0, args) : delayedObject; +} +/** + * Makes a byX function, like byStat or byClass + * + * @param source A method for finding your stat, or class, or whatever X is in this context + * @returns A function akin to byStat or byClass; it accepts an object that either is "complete" in the sense that it has a key for every conceivable value, or contains a `default` parameter. If an inappropriate input is provided, returns undefined. + */ +function makeByXFunction(source) { + return function (options) { + var _options$val; + var val = undelay(source); + if ("default" in options) return (_options$val = options[val]) !== null && _options$val !== void 0 ? _options$val : options.default; + return options[val]; + }; +} +/** + * Flattens an array. Basically replacing Array.prototype.flat for which Rhino doesn't yet have an implementation + * + * @param arr Array to flatten + * @param depth Number of layers to flatten by; Infinity for a fully flat array + * @returns Flattened array + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function flat(arr) { + var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Infinity; + var flatArray = []; + var _iterator3 = utils_createForOfIteratorHelper(arr), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var item = _step3.value; + if (Array.isArray(item) && depth > 0) { + flatArray = flatArray.concat(flat(item, depth - 1)); + } else { + flatArray.push(item); + } + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + return flatArray; +} +/** + * @param array Array to select from + * @returns Random item from array + */ +function random(array) { + return array[Math.floor(Math.random() * array.length)]; +} +/** + * Title cases a single word + * + * @param word Word to transform + * @returns Word in title case + */ +var tc = word => word.charAt(0).toUpperCase() + word.slice(1); +;// CONCATENATED MODULE: ./node_modules/libram/dist/template-string.js + + +var concatTemplateString = function concatTemplateString(literals) { + for (var _len = arguments.length, placeholders = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + placeholders[_key - 1] = arguments[_key]; + } + return literals.raw.reduce((acc, literal, i) => { + var _placeholders$i; + return acc + literal + ((_placeholders$i = placeholders[i]) !== null && _placeholders$i !== void 0 ? _placeholders$i : ""); + }, ""); +}; +var handleTypeGetError = (Type, error) => { + var message = "".concat(error); + var match = message.match(RegExp("Bad ".concat(Type.name.toLowerCase(), " value: .*"))); + if (match) { + (0,external_kolmafia_.print)("".concat(match[0], "; if you're certain that this ").concat(Type.name, " exists and is spelled correctly, please update KoLMafia"), "red"); + } else { + (0,external_kolmafia_.print)(message); + } +}; +var createSingleConstant = Type => { + var tagFunction = function tagFunction(literals) { + for (var _len2 = arguments.length, placeholders = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + placeholders[_key2 - 1] = arguments[_key2]; + } + var input = concatTemplateString.apply(void 0, [literals].concat(placeholders)); + try { + return Type.get(input); + } catch (error) { + handleTypeGetError(Type, error); + } + (0,external_kolmafia_.abort)(); + }; + tagFunction.none = Type.none; + return tagFunction; +}; +var createPluralConstant = Type => { + var tagFunction = function tagFunction(literals) { + for (var _len3 = arguments.length, placeholders = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { + placeholders[_key3 - 1] = arguments[_key3]; + } + var input = concatTemplateString.apply(void 0, [literals].concat(placeholders)); + if (input === "") { + return Type.all(); + } + try { + return Type.get(splitByCommasWithEscapes(input)); + } catch (error) { + handleTypeGetError(Type, error); + } + (0,external_kolmafia_.abort)(); + }; + tagFunction.all = () => Type.all(); + return tagFunction; +}; +/** + * A Bounty specified by name. + * + * @category In-game constant + */ +var $bounty = createSingleConstant(external_kolmafia_.Bounty); +/** + * A list of Bounties specified by a comma-separated list of names. + * For a list of all possible Bounties, leave the template string blank. + * + * @category In-game constant + */ +var $bounties = createPluralConstant(external_kolmafia_.Bounty); +/** + * A Class specified by name. + * + * @category In-game constant + */ +var $class = createSingleConstant(external_kolmafia_.Class); +/** + * A list of Classes specified by a comma-separated list of names. + * For a list of all possible Classes, leave the template string blank. + * + * @category In-game constant + */ +var $classes = createPluralConstant(external_kolmafia_.Class); +/** + * A Coinmaster specified by name. + * + * @category In-game constant + */ +var $coinmaster = createSingleConstant(external_kolmafia_.Coinmaster); +/** + * A list of Coinmasters specified by a comma-separated list of names. + * For a list of all possible Coinmasters, leave the template string blank. + * + * @category In-game constant + */ +var $coinmasters = createPluralConstant(external_kolmafia_.Coinmaster); +/** + * An Effect specified by name. + * + * @category In-game constant + */ +var template_string_$effect = createSingleConstant(external_kolmafia_.Effect); +/** + * A list of Effects specified by a comma-separated list of names. + * For a list of all possible Effects, leave the template string blank. + * + * @category In-game constant + */ +var $effects = createPluralConstant(external_kolmafia_.Effect); +/** + * An Element specified by name. + * + * @category In-game constant + */ +var $element = createSingleConstant(external_kolmafia_.Element); +/** + * A list of Elements specified by a comma-separated list of names. + * For a list of all possible Elements, leave the template string blank. + * + * @category In-game constant + */ +var $elements = createPluralConstant(external_kolmafia_.Element); +/** + * A Familiar specified by name. + * + * @category In-game constant + */ +var template_string_$familiar = createSingleConstant(external_kolmafia_.Familiar); +/** + * A list of Familiars specified by a comma-separated list of names. + * For a list of all possible Familiars, leave the template string blank. + * + * @category In-game constant + */ +var $familiars = createPluralConstant(external_kolmafia_.Familiar); +/** + * An Item specified by name. + * + * @category In-game constant + */ +var template_string_$item = createSingleConstant(external_kolmafia_.Item); +/** + * A list of Items specified by a comma-separated list of names. + * For a list of all possible Items, leave the template string blank. + * + * @category In-game constant + */ +var template_string_$items = createPluralConstant(external_kolmafia_.Item); +/** + * A Location specified by name. + * + * @category In-game constant + */ +var $location = createSingleConstant(external_kolmafia_.Location); +/** + * A list of Locations specified by a comma-separated list of names. + * For a list of all possible Locations, leave the template string blank. + * + * @category In-game constant + */ +var $locations = createPluralConstant(external_kolmafia_.Location); +/** + * A Modifier specified by name. + * + * @category In-game constant + */ +var $modifier = createSingleConstant(external_kolmafia_.Modifier); +/** + * A list of Modifiers specified by a comma-separated list of names. + * For a list of all possible Modifiers, leave the template string blank. + * + * @category In-game constant + */ +var $modifiers = createPluralConstant(external_kolmafia_.Modifier); +/** + * A Monster specified by name. + * + * @category In-game constant + */ +var $monster = createSingleConstant(external_kolmafia_.Monster); +/** + * A list of Monsters specified by a comma-separated list of names. + * For a list of all possible Monsters, leave the template string blank. + * + * @category In-game constant + */ +var $monsters = createPluralConstant(external_kolmafia_.Monster); +/** + * A Phylum specified by name. + * + * @category In-game constant + */ +var $phylum = createSingleConstant(external_kolmafia_.Phylum); +/** + * A list of Phyla specified by a comma-separated list of names. + * For a list of all possible Phyla, leave the template string blank. + * + * @category In-game constant + */ +var $phyla = createPluralConstant(external_kolmafia_.Phylum); +/** + * A Servant specified by name. + * + * @category In-game constant + */ +var $servant = createSingleConstant(external_kolmafia_.Servant); +/** + * A list of Servants specified by a comma-separated list of names. + * For a list of all possible Servants, leave the template string blank. + * + * @category In-game constant + */ +var $servants = createPluralConstant(external_kolmafia_.Servant); +/** + * A Skill specified by name. + * + * @category In-game constant + */ +var template_string_$skill = createSingleConstant(external_kolmafia_.Skill); +/** + * A list of Skills specified by a comma-separated list of names. + * For a list of all possible Skills, leave the template string blank. + * + * @category In-game constant + */ +var $skills = createPluralConstant(external_kolmafia_.Skill); +/** + * A Slot specified by name. + * + * @category In-game constant + */ +var $slot = createSingleConstant(external_kolmafia_.Slot); +/** + * A list of Slots specified by a comma-separated list of names. + * For a list of all possible Slots, leave the template string blank. + * + * @category In-game constant + */ +var $slots = createPluralConstant(external_kolmafia_.Slot); +/** + * A Stat specified by name. + * + * @category In-game constant + */ +var $stat = createSingleConstant(external_kolmafia_.Stat); +/** + * A list of Stats specified by a comma-separated list of names. + * For a list of all possible Stats, leave the template string blank. + * + * @category In-game constant + */ +var $stats = createPluralConstant(external_kolmafia_.Stat); +/** + * A Thrall specified by name. + * + * @category In-game constant + */ +var $thrall = createSingleConstant(external_kolmafia_.Thrall); +/** + * A list of Thralls specified by a comma-separated list of names. + * For a list of all possible Thralls, leave the template string blank. + * + * @category In-game constant + */ +var $thralls = createPluralConstant(external_kolmafia_.Thrall); +/** + * A Path specified by name. + * + * @category In-game constant + */ +var $path = createSingleConstant(external_kolmafia_.Path); +/** + * A list of Paths specified by a comma-separated list of names. + * For a list of all possible Paths, leave the template string blank. + * + * @category In-game constant + */ +var $paths = createPluralConstant(external_kolmafia_.Path); +;// CONCATENATED MODULE: ./node_modules/libram/dist/lib.js +var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8, _templateObject9, _templateObject10, _templateObject11, _templateObject12, _templateObject13, _templateObject14, _templateObject15, _templateObject16, _templateObject17, _templateObject18, _templateObject19, _templateObject20, _templateObject21, _templateObject22, _templateObject23, _templateObject24, _templateObject25, _templateObject26, _templateObject27, _templateObject28, _templateObject29, _templateObject30, _templateObject31, _templateObject32, _templateObject33, _templateObject34, _templateObject35, _templateObject36, _templateObject37, _templateObject38, _templateObject39, _templateObject40, _templateObject41, _templateObject42, _templateObject43, _templateObject44, _templateObject45; +function lib_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, lib_toPropertyKey(descriptor.key), descriptor); } } +function lib_createClass(Constructor, protoProps, staticProps) { if (protoProps) lib_defineProperties(Constructor.prototype, protoProps); if (staticProps) lib_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function lib_toPropertyKey(arg) { var key = lib_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function lib_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function lib_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } +function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct.bind(); } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +function lib_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = lib_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function lib_slicedToArray(arr, i) { return lib_arrayWithHoles(arr) || lib_iterableToArrayLimit(arr, i) || lib_unsupportedIterableToArray(arr, i) || lib_nonIterableRest(); } +function lib_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function lib_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return lib_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return lib_arrayLikeToArray(o, minLen); } +function lib_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function lib_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function lib_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } +/** @module GeneralLibrary */ + + + + + +/** + * Determines the current maximum Accordion Thief songs the player can have in their head + * + * @category General + * @returns Maximum number of songs for player + */ +function getSongLimit() { + return 3 + (booleanModifier("Four Songs") ? 1 : 0) + numericModifier("Additional Song"); +} +/** + * Determine whether the Skill or Effect provided is an Accordion Thief song + * + * @category General + * @param skillOrEffect The Skill or Effect + * @returns Whether it's a song + */ +function isSong(skillOrEffect) { + if (skillOrEffect instanceof external_kolmafia_.Effect && skillOrEffect.attributes.includes("song")) { + return true; + } else { + var skill = skillOrEffect instanceof external_kolmafia_.Effect ? (0,external_kolmafia_.toSkill)(skillOrEffect) : skillOrEffect; + return skill.class === $class(_templateObject || (_templateObject = _taggedTemplateLiteral(["Accordion Thief"]))) && skill.buff; + } +} +/** + * List all active Effects + * + * @category General + * @returns List of Effects + */ +function getActiveEffects() { + return Object.keys(myEffects()).map(e => Effect.get(e)); +} +/** + * List currently active Accordion Thief songs + * + * @category General + * @returns List of song Effects + */ +function getActiveSongs() { + return getActiveEffects().filter(isSong); +} +/** + * List number of active Accordion Thief songs + * + * @category General + * @returns Number of songs + */ +function getSongCount() { + return getActiveSongs().length; +} +/** + * Determine whether player can remember another Accordion Thief song + * + * @category General + * @param quantity Number of songs to test the space for + * @returns Whether player can remember another song + */ +function canRememberSong() { + var quantity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; + return getSongLimit() - getSongCount() >= quantity; +} +/** + * Determine the locations in which the given monster can be encountered naturally + * + * @category General + * @param monster Monster to find + * @returns Locations for monster + */ +function getMonsterLocations(monster) { + return Location.all().filter(location => monster.name in appearanceRates(location)); +} +/** + * Determine the player's remaining liver space + * + * @category General + * @returns Remaining liver space + */ +function getRemainingLiver() { + return inebrietyLimit() - myInebriety(); +} +/** + * Determine the player's remaining stomach space + * + * @category General + * @returns Remaining stomach space + */ +function getRemainingStomach() { + return fullnessLimit() - myFullness(); +} +/** + * Determine the player's remaining spleen space + * + * @category General + * @returns Remaining spleen space + */ +function getRemainingSpleen() { + return spleenLimit() - mySpleenUse(); +} +/** + * Determine whether the player "has" any entity which one could feasibly "have". + * + * @category General + * @param thing Thing to check + * @param quantity Minimum quantity the player must have to pass + * @returns Whether the player meets the requirements of owning the supplied thing + */ +function have(thing) { + var quantity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + if (thing instanceof external_kolmafia_.Effect) { + return (0,external_kolmafia_.haveEffect)(thing) >= quantity; + } + if (thing instanceof external_kolmafia_.Familiar) { + return (0,external_kolmafia_.haveFamiliar)(thing); + } + if (thing instanceof external_kolmafia_.Item) { + return (0,external_kolmafia_.availableAmount)(thing) >= quantity; + } + if (thing instanceof external_kolmafia_.Servant) { + return (0,external_kolmafia_.haveServant)(thing); + } + if (thing instanceof external_kolmafia_.Skill) { + return (0,external_kolmafia_.haveSkill)(thing); + } + if (thing instanceof external_kolmafia_.Thrall) { + var thrall = (0,external_kolmafia_.myThrall)(); + return thrall.id === thing.id && thrall.level >= quantity; + } + return false; +} +/** + * Determine whether a given item is in the player's campground + * + * @category General + * @param item The Item KoLmafia uses to represent the campground item + * @returns Whether the item is in the campground + */ +function haveInCampground(item) { + return Object.keys(getCampground()).map(i => Item.get(i)).includes(item); +} +var Wanderer; +(function (Wanderer) { + Wanderer["Digitize"] = "Digitize Monster"; + Wanderer["Enamorang"] = "Enamorang Monster"; + Wanderer["Familiar"] = "Familiar"; + Wanderer["Holiday"] = "Holiday Monster"; + Wanderer["Kramco"] = "Kramco"; + Wanderer["Nemesis"] = "Nemesis Assassin"; + Wanderer["Portscan"] = "portscan.edu"; + Wanderer["Romantic"] = "Romantic Monster"; + Wanderer["Vote"] = "Vote Monster"; +})(Wanderer || (Wanderer = {})); +var deterministicWanderers = [Wanderer.Digitize, Wanderer.Portscan]; +/** + * Determine whether the player has the specified counter + * + * @param counterName Name of the counter + * @param minTurns Minimum turns the counter is set to + * @param maxTurns Maximum turns the counter is set to + * @category General + * @returns Whether player has the counter + */ +function haveCounter(counterName) { + var minTurns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var maxTurns = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500; + return getCounters(counterName, minTurns, maxTurns) === counterName; +} +/** + * Determine whether the player has the specified wanderer's counter + * + * @param wanderer Wanderer to check + * @category Wanderers + * @returns Whether player has the wanderer counter + */ +function haveWandererCounter(wanderer) { + if (deterministicWanderers.includes(wanderer)) { + return haveCounter(wanderer); + } + var begin = wanderer + " window begin"; + var end = wanderer + " window end"; + return haveCounter(begin) || haveCounter(end); +} +/** + * Determine whether the player will encounter a vote wanderer on the next turn, + * providing an "I Voted!" sticker is equipped. + * + * @category Wanderers + * @returns Whether the vote wanderer is due + */ +function isVoteWandererNow() { + return totalTurnsPlayed() % 11 === 1 && get("lastVoteMonsterTurn") < totalTurnsPlayed(); +} +/** + * Tells us whether we can expect a given wanderer now. Behaves differently + * for different types of wanderer. + * + * - For deterministic wanderers, return whether the player will encounter + * the queried wanderer on the next turn + * + * - For variable wanderers (window), return whether the player is within + * an encounter window for the queried wanderer + * + * - For variable wanderers (chance per turn), returns true unless the player + * has exhausted the number of wanderers possible + * + * @category Wanderers + * @param wanderer Wanderer to check + * @returns Whether the wanderer is due + */ +function isWandererNow(wanderer) { + if (deterministicWanderers.includes(wanderer)) { + return haveCounter(wanderer, 0, 0); + } + if (wanderer === Wanderer.Kramco) { + return true; + } + if (wanderer === Wanderer.Vote) { + return isVoteWandererNow(); + } + if (wanderer === Wanderer.Familiar) { + return get("_hipsterAdv") < 7; + } + var begin = wanderer + " window begin"; + var end = wanderer + " window end"; + return !haveCounter(begin, 1) && haveCounter(end); +} +/** + * Determines the chance the player will encounter a sausage goblin on the + * next turn, providing the Kramco Sausage-o-Matic is equipped. + * + * @category Wanderers + * @returns Chance that the sausage goblin is due (as a number between 0 and 1) + */ +function getKramcoWandererChance() { + var fights = get("_sausageFights"); + var lastFight = get("_lastSausageMonsterTurn"); + var totalTurns = totalTurnsPlayed(); + if (fights < 1) { + return lastFight === totalTurns && myTurncount() < 1 ? 0.5 : 1.0; + } + var turnsSinceLastFight = totalTurns - lastFight; + return Math.min(1.0, (turnsSinceLastFight + 1) / (5 + fights * 3 + Math.pow(Math.max(0, fights - 5), 3))); +} +/** + * Determines the chance the player will encounter an Artistic Goth Kid or + * Mini-Hipster wanderer on the next turn, providing a familiar is equipped. + * + * NOTE: You must complete one combat with the Artistic Goth Kid before you + * can encounter any wanderers. Consequently,ƒ the first combat with the + * Artist Goth Kid is effectively 0% chance to encounter a wanderer. + * + * @category Wanderers + * @returns Chance that the familiar wanderer is due (as a number between 0 and 1) + */ +function getFamiliarWandererChance() { + var totalFights = get("_hipsterAdv"); + var probability = [0.5, 0.4, 0.3, 0.2]; + if (totalFights < 4) { + return probability[totalFights]; + } + return totalFights > 7 ? 0.0 : 0.1; +} +/** + * Determines the chance the player will encounter the specified wanderer + * on the next turn. + * + * @category Wanderers + * @param wanderer Wanderer to check + * @returns Chance that the specified wanderer is due (as a number between 0 and 1) + */ +function getWandererChance(wanderer) { + if (deterministicWanderers.includes(wanderer)) { + return haveCounter(wanderer, 0, 0) ? 1.0 : 0.0; + } + if (wanderer === Wanderer.Kramco) { + getKramcoWandererChance(); + } + if (wanderer === Wanderer.Vote) { + return isVoteWandererNow() ? 1.0 : 0.0; + } + if (wanderer === Wanderer.Familiar) { + getFamiliarWandererChance(); + } + var begin = wanderer + " window begin"; + var end = wanderer + " window end"; + if (haveCounter(begin, 1, 100)) { + return 0.0; + } + var counters = get("relayCounters"); + var re = new RegExp("(\\d+):" + end); + var matches = counters.match(re); + if (matches && matches.length === 2) { + var window = Number.parseInt(matches[1]) - myTurncount(); + return 1.0 / window; + } + return 0.0; +} +/** + * Determines whether the player's current familiar is equal to the one supplied + * + * @category General + * @param familiar Familiar to check + * @returns Whether it is the player's current familiar + */ +function isCurrentFamiliar(familiar) { + return myFamiliar() === familiar; +} +/** + * Determines the fold group (if any) of which the given item is a part + * + * @category General + * @param item Item that is part of the required fold group + * @returns List of items in the fold group + */ +function getFoldGroup(item) { + return Object.entries(getRelated(item, "fold")).sort((_ref, _ref2) => { + var _ref3 = lib_slicedToArray(_ref, 2), + a = _ref3[1]; + var _ref4 = lib_slicedToArray(_ref2, 2), + b = _ref4[1]; + return a - b; + }).map(_ref5 => { + var _ref6 = lib_slicedToArray(_ref5, 1), + i = _ref6[0]; + return Item.get(i); + }); +} +/** + * Determines the zap group (if any) of which the given item is a part + * + * @category General + * @param item Item that is part of the required zap group + * @returns List of items in the zap group + */ +function getZapGroup(item) { + return Object.keys(getRelated(item, "zap")).map(i => Item.get(i)); +} +/** + * Get a map of banished monsters keyed by what banished them + * + * @category General + * @returns Map of banished monsters + */ +function getBanishedMonsters() { + var banishes = chunk(get("banishedMonsters").split(":"), 3); + var result = new Map(); + var _iterator = lib_createForOfIteratorHelper(banishes), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var _step$value = lib_slicedToArray(_step.value, 2), + foe = _step$value[0], + banisher = _step$value[1]; + if (foe === undefined || banisher === undefined) break; + // toItem doesn"t error if the item doesn"t exist, so we have to use that. + var banisherItem = toItem(banisher); + if (banisher.toLowerCase() === "saber force") { + result.set($skill(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["Use the Force"]))), Monster.get(foe)); + } else if (banisher.toLowerCase() === "nanorhino") { + result.set($skill(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["Unleash Nanites"]))), Monster.get(foe)); + } else if ([Item.none, Item.get("training scroll: Snokebomb"), Item.get("tomayohawk-style reflex hammer"), null].includes(banisherItem)) { + if (Skill.get(banisher) === $skill.none) { + break; + } else { + result.set(Skill.get(banisher), Monster.get(foe)); + } + } else { + result.set(banisherItem, Monster.get(foe)); + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return result; +} +/** + * Determines whether the item is usable + * + * This function will be an ongoing work in progress + * + * @param item Item to check + * @returns Whether item is usable + */ +function canUse(item) { + var path = myPath(); + if (path !== Path.get("Nuclear Autumn")) { + if ($items(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["Shrieking Weasel holo-record, Power-Guy 2000 holo-record, Lucky Strikes holo-record, EMD holo-record, Superdrifter holo-record, The Pigs holo-record, Drunk Uncles holo-record"]))).includes(item)) { + return false; + } + } + if (path === Path.get("G-Lover")) { + if (!item.name.toLowerCase().includes("g")) return false; + } + if (path === Path.get("Bees Hate You")) { + if (item.name.toLowerCase().includes("b")) return false; + } + return true; +} +/** + * Turn KoLmafia `none`s to JavaScript `null`s + * + * @param thing Thing that can have a mafia "none" value + * @returns The thing specified or `null` + */ +function noneToNull(thing) { + if (thing instanceof Effect) { + return thing === Effect.none ? null : thing; + } + if (thing instanceof Familiar) { + return thing === Familiar.none ? null : thing; + } + if (thing instanceof Item) { + return thing === Item.none ? null : thing; + } + return thing; +} +/** + * Determine the average value from the sort of range that KoLmafia encodes as a string + * + * @param range KoLmafia-style range string + * @returns Average value fo range + */ +function getAverage(range) { + var _range$match; + if (range.indexOf("-") < 0) return Number(range); + var _ref7 = (_range$match = range.match(/(-?[0-9]+)-(-?[0-9]+)/)) !== null && _range$match !== void 0 ? _range$match : ["0", "0", "0"], + _ref8 = lib_slicedToArray(_ref7, 3), + lower = _ref8[1], + upper = _ref8[2]; + return (Number(lower) + Number(upper)) / 2; +} +/** + * Deternube tge average adventures expected from consuming an Item + * + * If item is not a consumable, will just return "0". + * + * @param item Consumable item + * @returns Average aventures from consumable + */ +function getAverageAdventures(item) { + return getAverage(item.adventures); +} +/** + * Remove an effect + * + * @category General + * @param effect Effect to remove + * @returns Success + */ +function uneffect(effect) { + return (0,external_kolmafia_.cliExecute)("uneffect ".concat(effect.name)); +} +/** + * Get both the name and id of a player from either their name or id + * + * @param idOrName Id or name of player + * @returns Object containing id and name of player + */ +function getPlayerFromIdOrName(idOrName) { + var id = typeof idOrName === "number" ? idOrName : parseInt(getPlayerId(idOrName)); + return { + name: getPlayerName(id), + id: id + }; +} +/** + * Determine the step as a number for a given quest property. + * + * @param questName Name of quest property to check. + * @returns Quest step + */ +function questStep(questName) { + var stringStep = get(questName); + if (stringStep === "unstarted") return -1;else if (stringStep === "started") return 0;else if (stringStep === "finished" || stringStep === "") return 999;else { + if (stringStep.substring(0, 4) !== "step") { + throw new Error("Quest state parsing error."); + } + return parseInt(stringStep.substring(4), 10); + } +} +var EnsureError = /*#__PURE__*/function (_Error) { + _inherits(EnsureError, _Error); + var _super = _createSuper(EnsureError); + function EnsureError(cause, reason) { + var _this; + lib_classCallCheck(this, EnsureError); + _this = _super.call(this, "Failed to ensure ".concat(cause.name, "!").concat(reason ? " ".concat(reason) : "")); + _this.name = "Ensure Error"; + return _this; + } + return lib_createClass(EnsureError); +}( /*#__PURE__*/_wrapNativeSuper(Error)); +/** + * Tries to get an effect using the default method + * + * @param ef effect to try to get + * @param turns turns to aim for; default of 1 + * @throws {EnsureError} Throws an error if the effect cannot be guaranteed + */ +function ensureEffect(ef) { + var turns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + if ((0,external_kolmafia_.haveEffect)(ef) < turns) { + if (ef.default === null) { + throw new EnsureError(ef, "No default action"); + } + if (!(0,external_kolmafia_.cliExecute)(ef.default) || (0,external_kolmafia_.haveEffect)(ef) === 0) { + throw new EnsureError(ef); + } + } +} +var valueMap = new Map(); +var MALL_VALUE_MODIFIER = 0.9; +/** + * Determiens the average value (based on mallprice and autosell) of a collection of items + * + * @param items items whose value you care about + * @returns Average value of items + */ +function getSaleValue() { + for (var _len = arguments.length, items = new Array(_len), _key = 0; _key < _len; _key++) { + items[_key] = arguments[_key]; + } + return items.map(item => { + if (valueMap.has(item)) return valueMap.get(item) || 0; + if (item.discardable) { + valueMap.set(item, mallPrice(item) > Math.max(2 * autosellPrice(item), 100) ? MALL_VALUE_MODIFIER * mallPrice(item) : autosellPrice(item)); + } else { + valueMap.set(item, mallPrice(item) > 100 ? MALL_VALUE_MODIFIER * mallPrice(item) : 0); + } + return valueMap.get(item) || 0; + }).reduce((s, price) => s + price, 0) / items.length; +} +var Environment = { + Outdoor: "outdoor", + Indoor: "indoor", + Underground: "underground", + Underwater: "underwater" +}; +/** + * Determines the weight-coefficient of any leprechaunning that this familiar may find itself doing + * Assumes the familiar is nude and thus fails for hatrack & pantsrack + * For the Mutant Cactus Bud, returns the efficacy-multiplier instead + * + * @param familiar The familiar whose leprechaun multiplier you're interested in + * @returns Weight-coefficient + */ +function findLeprechaunMultiplier(familiar) { + if (familiar === $familiar(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["Mutant Cactus Bud"])))) { + return numericModifier(familiar, "Leprechaun Effectiveness", 1, $item.none); + } + if (familiar === $familiar(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral(["Reanimated Reanimator"])))) return 0; + var meatBonus = numericModifier(familiar, "Meat Drop", 1, $item.none); + if (meatBonus === 0) return 0; + return Math.pow(Math.sqrt(meatBonus / 2 + 55 / 4 + 3) - Math.sqrt(55) / 2, 2); +} +/** + * Determines the weight-coefficient of any baby gravy fairying that this familiar may find itself doing + * Assumes the familiar is nude and thus fails for hatrack & pantsrack + * For the Mutant Fire Ant, returns the efficacy-multiplier instead + * + * @param familiar The familiar whose fairy multiplier you're interested in + * @returns Weight-coefficient + */ +function findFairyMultiplier(familiar) { + if (familiar === $familiar(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral(["Mutant Fire Ant"])))) { + return numericModifier(familiar, "Fairy Effectiveness", 1, $item.none); + } + if (familiar === $familiar(_templateObject8 || (_templateObject8 = _taggedTemplateLiteral(["Reanimated Reanimator"])))) return 0; + var itemBonus = numericModifier(familiar, "Item Drop", 1, $item.none); + if (itemBonus === 0) return 0; + return Math.pow(Math.sqrt(itemBonus + 55 / 4 + 3) - Math.sqrt(55) / 2, 2); +} +var holidayWanderers = new Map([["El Dia De Los Muertos Borrachos", $monsters(_templateObject9 || (_templateObject9 = _taggedTemplateLiteral(["Novia Cad\xE1ver, Novio Cad\xE1ver, Padre Cad\xE1ver, Persona Inocente Cad\xE1ver"])))], ["Feast of Boris", $monsters(_templateObject10 || (_templateObject10 = _taggedTemplateLiteral(["Candied Yam Golem, Malevolent Tofurkey, Possessed Can of Cranberry Sauce, Stuffing Golem"])))], ["Talk Like a Pirate Day", $monsters(_templateObject11 || (_templateObject11 = _taggedTemplateLiteral(["ambulatory pirate, migratory pirate, peripatetic pirate"])))]]); +/** + * Get today's holiday wanderers + * + * @returns List of holiday wanderer Monsters + */ +function getTodaysHolidayWanderers() { + return flat((0,external_kolmafia_.holiday)().split("/").map(holiday => { + var _holidayWanderers$get; + return (_holidayWanderers$get = holidayWanderers.get(holiday)) !== null && _holidayWanderers$get !== void 0 ? _holidayWanderers$get : []; + })); +} +/** + * Determines whether or not we can safely call visitUrl(), based on whether we're in a fight, multi-fight, choice, etc + * + * @returns Whether urls can be safely visited + */ +function canVisitUrl() { + if (currentRound()) { + logger.debug("Current round is ".concat(currentRound(), "; you're in combat.")); + return false; + } + if (inMultiFight()) { + logger.debug("You're in a multifight."); + return false; + } + if (choiceFollowsFight()) { + logger.debug("A choice follows this fight."); + return false; + } + if (handlingChoice()) { + logger.debug("You're currently in a choice adventure"); + return false; + } + return true; +} +/** + * Calculate damage taken from a specific element after factoring in resistance + * + * @param baseDamage Base damage + * @param element Element + * @returns damage after factoring in resistances + */ +function damageTakenByElement(baseDamage, element) { + if (baseDamage < 0) return 1; + var res = elementalResistance(element); + return Math.max(1, Math.ceil(baseDamage - baseDamage * res / 100)); +} +var telescopeStats = new Map([["standing around flexing their muscles and using grip exercisers", $stat(_templateObject12 || (_templateObject12 = _taggedTemplateLiteral(["Muscle"])))], ["sitting around playing chess and solving complicated-looking logic puzzles", $stat(_templateObject13 || (_templateObject13 = _taggedTemplateLiteral(["Mysticality"])))], ["all wearing sunglasses and dancing", $stat(_templateObject14 || (_templateObject14 = _taggedTemplateLiteral(["Moxie"])))]]); +var telescopeElements = new Map([["people, all of whom appear to be on fire", $element(_templateObject15 || (_templateObject15 = _taggedTemplateLiteral(["hot"])))], ["people, surrounded by a cloud of eldritch mist", $element(_templateObject16 || (_templateObject16 = _taggedTemplateLiteral(["spooky"])))], ["greasy-looking people furtively skulking around", $element(_templateObject17 || (_templateObject17 = _taggedTemplateLiteral(["sleaze"])))], ["people, surrounded by garbage and clouds of flies", $element(_templateObject18 || (_templateObject18 = _taggedTemplateLiteral(["stench"])))], ["people, clustered around a group of igloos", $element(_templateObject19 || (_templateObject19 = _taggedTemplateLiteral(["cold"])))]]); +var hedgeTrap1 = new Map([["smoldering bushes on the outskirts of a hedge maze", $element(_templateObject20 || (_templateObject20 = _taggedTemplateLiteral(["hot"])))], ["creepy-looking black bushes on the outskirts of a hedge maze", $element(_templateObject21 || (_templateObject21 = _taggedTemplateLiteral(["spooky"])))], ["purplish, greasy-looking hedges", $element(_templateObject22 || (_templateObject22 = _taggedTemplateLiteral(["sleaze"])))], ["nasty-looking, dripping green bushes on the outskirts of a hedge maze", $element(_templateObject23 || (_templateObject23 = _taggedTemplateLiteral(["stench"])))], ["frost-rimed bushes on the outskirts of a hedge maze", $element(_templateObject24 || (_templateObject24 = _taggedTemplateLiteral(["cold"])))]]); +var hedgeTrap2 = new Map([["smoke rising from deeper within the maze", $element(_templateObject25 || (_templateObject25 = _taggedTemplateLiteral(["hot"])))], ["a miasma of eldritch vapors rising from deeper within the maze", $element(_templateObject26 || (_templateObject26 = _taggedTemplateLiteral(["spooky"])))], ["a greasy purple cloud hanging over the center of the maze", $element(_templateObject27 || (_templateObject27 = _taggedTemplateLiteral(["sleaze"])))], ["a cloud of green gas hovering over the maze", $element(_templateObject28 || (_templateObject28 = _taggedTemplateLiteral(["stench"])))], ["wintry mists rising from deeper within the maze", $element(_templateObject29 || (_templateObject29 = _taggedTemplateLiteral(["cold"])))]]); +var hedgeTrap3 = new Map([["with lava slowly oozing out of it", $element(_templateObject30 || (_templateObject30 = _taggedTemplateLiteral(["hot"])))], ["surrounded by creepy black mist", $element(_templateObject31 || (_templateObject31 = _taggedTemplateLiteral(["spooky"])))], ["that occasionally vomits out a greasy ball of hair", $element(_templateObject32 || (_templateObject32 = _taggedTemplateLiteral(["sleaze"])))], ["disgorging a really surprising amount of sewage", $element(_templateObject33 || (_templateObject33 = _taggedTemplateLiteral(["stench"])))], ["occasionally disgorging a bunch of ice cubes", $element(_templateObject34 || (_templateObject34 = _taggedTemplateLiteral(["cold"])))]]); +/** + * Get information from telescope + * + * @returns An object with all information the telescope gives you about the sorceress's contests and maze + */ +function telescope() { + return { + statContest: telescopeStats.get(get("telescope1")), + elementContest: telescopeElements.get(get("telescope2")), + hedge1: hedgeTrap1.get(get("telescope3")), + hedge2: hedgeTrap2.get(get("telescope4")), + hedge3: hedgeTrap3.get(get("telescope5")) + }; +} +/** + * Visit the desc_x.php page for a given thing + * + * @param thing Thing to examine + * @returns Contents of desc_x.php page + */ +function examine(thing) { + var url = thing instanceof Item ? "desc_item.php?whichitem=".concat(thing.descid) : thing instanceof Familiar ? "desc_familiar.php?which=".concat(thing.id) : thing instanceof Effect ? "desc_effect.php?whicheffect=".concat(thing.descid) : "desc_skill.php?whichskill=".concat(thing.id); + return visitUrl(url); +} +/** + * Picks an option based on your primestat + * + * @param options An object keyed by stat; it must either contain all stats, or have a `default` parameter. + * @returns The option corresponding to your primestat. + */ +var byStat = makeByXFunction(() => (0,external_kolmafia_.myPrimestat)().toString()); +/** + * Picks an option based on your player class + * + * @param options An object keyed by player class; it must either contain all classes, or have a `default` parameter. + * @returns The option corresponding to your player class. + */ +var byClass = makeByXFunction(() => (0,external_kolmafia_.myClass)().toString()); +/** + * Use an item with visitUrl instead of `use`; this is sometimes useful + * + * @param item The item you want to use + * @returns The html of the resulting page + */ +function directlyUse(item) { + return visitUrl("inv_use.php?which=3&whichitem=".concat(item.id, "&pwd")); +} +/** + * Empty a slot, or unequip all instances of a given equipped item + * + * @param thing The slot or item in question + * @returns Whether we succeeded completely--`false` if we unequip some but not all instances of the item. + */ +function unequip(thing) { + if (thing instanceof Slot) return equip(thing, $item.none); + var failedSlots = Slot.all().filter(s => { + // Filter the slot out if it doesn't contain the relevant item + if (equippedItem(s) !== thing) return false; + // Filter the slot out if we succeed at unequipping it + return !unequip(thing); + // This leaves only slots that do contain the item but that we failed to unequip + }); + + if (failedSlots.length) logger.debug("Failed to unequip ".concat(thing, " from slots ").concat(failedSlots.join(", "))); + return failedSlots.length === 0; +} +/** + * @returns a Date object corresponding to the current in-game day, at midnight + */ +function gameDay() { + var _todayToString$match; + var _map = ((_todayToString$match = todayToString().match(/(\d{4})(\d{2})(\d{2})/)) !== null && _todayToString$match !== void 0 ? _todayToString$match : []).map(Number), + _map2 = lib_slicedToArray(_map, 4), + year = _map2[1], + month = _map2[2], + day = _map2[3]; + return new Date(year, month - 1, day, 0, 0, 0); +} +/** + * @param [type="all"] the type of crafting to check for free crafts + * @returns the number of free crafts available of that type + */ +function freeCrafts() { + var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "all"; + var effectCrafts = effect => Math.floor(haveEffect(effect) / 5); + var all = (have($skill(_templateObject35 || (_templateObject35 = _taggedTemplateLiteral(["Rapid Prototyping"])))) ? 5 - get("_rapidPrototypingUsed") : 0) + (have($skill(_templateObject36 || (_templateObject36 = _taggedTemplateLiteral(["Expert Corner-Cutter"])))) ? 5 - get("_expertCornerCutterUsed") : 0) + effectCrafts($effect(_templateObject37 || (_templateObject37 = _taggedTemplateLiteral(["Inigo's Incantation of Inspiration"])))) + effectCrafts($effect(_templateObject38 || (_templateObject38 = _taggedTemplateLiteral(["Craft Tea"])))) + + // eslint-disable-next-line libram/verify-constants + effectCrafts($effect(_templateObject39 || (_templateObject39 = _taggedTemplateLiteral(["Cooking Concentrate"])))); + var food = type === "food" ? 5 - get("_cookbookbatCrafting") : 0; + var smith = type === "smith" ? 5 - get("_thorsPliersCrafting") : 0; + var booze = 0; // currently there is no booze specific free crafting skill + return all + food + smith + booze; +} +var realmTypes = (/* unused pure expression or super */ null && (["spooky", "stench", "hot", "cold", "sleaze", "fantasy", "pirate"])); +/** + * @param identifier which realm to check for + * @returns if that realm is available + */ +function realmAvailable(identifier) { + if (identifier === "fantasy") { + return get("_frToday") || get("frAlways"); + } else if (identifier === "pirate") { + return get("_prToday") || get("prAlways"); + } + return get("_".concat(identifier, "AirportToday")) || get("".concat(identifier, "AirportAlways")); +} +/** + * Compute the currently available Lucky Gold Ring Currencies + * @param realm the realm type to consider + * @returns The currency for the given zone + */ +function realmCurrency(realm) { + switch (realm) { + case "sleaze": + return $item(_templateObject40 || (_templateObject40 = _taggedTemplateLiteral(["Beach Buck"]))); + case "spooky": + return $item(_templateObject41 || (_templateObject41 = _taggedTemplateLiteral(["Coinspiracy"]))); + case "stench": + return $item(_templateObject42 || (_templateObject42 = _taggedTemplateLiteral(["FunFunds\u2122"]))); + case "cold": + return $item(_templateObject43 || (_templateObject43 = _taggedTemplateLiteral(["Wal-Mart gift certificate"]))); + case "hot": + return $item(_templateObject44 || (_templateObject44 = _taggedTemplateLiteral(["Volcoino"]))); + case "fantasy": + return $item(_templateObject45 || (_templateObject45 = _taggedTemplateLiteral(["Rubee\u2122"]))); + } +} +/** + * Compute which Lucky Gold Ring currencies are currently available + * @returns a list of currently available currencies + */ +function lgrCurrencies() { + return realmTypes.filter(realm => realmAvailable(realm) && !(realm === "hot" && get("_luckyGoldRingVolcoino"))).map(realmCurrency).filter(i => !!i); +} +;// CONCATENATED MODULE: ./node_modules/libram/dist/overlappingNames.js +/** THIS FILE IS AUTOMATICALLY GENERATED. See tools/parseItemSkillNames.ts for more information */ +var overlappingItemNames = ["spider web", "really sticky spider web", "dictionary", "NG", "Cloaca-Cola", "yo-yo", "top", "ball", "kite", "yo", "red potion", "blue potion", "bowling ball", "adder", "red button", "pile of sand", "mushroom", "deluxe mushroom"]; +var overlappingSkillNames = ["Shoot", "Thrust-Smack", "Headbutt", "Toss", "Knife in the Dark", "Sing", "Disarm", "LIGHT", "BURN", "Extract", "Meteor Shower", "Snipe", "Cleave", "Boil", "Slice", "Rainbow"]; +;// CONCATENATED MODULE: ./node_modules/libram/dist/propertyTypes.js +/** THIS FILE IS AUTOMATICALLY GENERATED. See tools/parseDefaultProperties.ts for more information */ +var booleanProperties = ["abortOnChoiceWhenNotInChoice", "addChatCommandLine", "addCreationQueue", "addStatusBarToFrames", "allowCloseableDesktopTabs", "allowNegativeTally", "allowNonMoodBurning", "allowSummonBurning", "autoHighlightOnFocus", "broadcastEvents", "cacheMallSearches", "chatBeep", "chatLinksUseRelay", "compactChessboard", "copyAsHTML", "customizedTabs", "debugBuy", "debugConsequences", "debugFoxtrotRemoval", "debugPathnames", "debugTopMenuStyle", "gapProtection", "gitInstallDependencies", "gitShowCommitMessages", "gitUpdateOnLogin", "greenScreenProtection", "guiUsesOneWindow", "hideServerDebugText", "logAcquiredItems", "logBattleAction", "logBrowserInteractions", "logChatMessages", "logChatRequests", "logCleanedHTML", "logDecoratedResponses", "logFamiliarActions", "logGainMessages", "logReadableHTML", "logPreferenceChange", "logMonsterHealth", "logReverseOrder", "logStatGains", "logStatusEffects", "logStatusOnLogin", "macroDebug", "macroLens", "mementoListActive", "mergeHobopolisChat", "pingLogin", "pingStealthyTimein", "printStackOnAbort", "proxySet", "relayAddSounds", "relayAddsCustomCombat", "relayAddsDiscoHelper", "relayAddsGraphicalCLI", "relayAddsQuickScripts", "relayAddsRestoreLinks", "relayAddsUpArrowLinks", "relayAddsUseLinks", "relayAddsWikiLinks", "relayAllowRemoteAccess", "relayBrowserOnly", "relayCacheUncacheable", "relayFormatsChatText", "relayHidesJunkMallItems", "relayMaintainsEffects", "relayMaintainsHealth", "relayMaintainsMana", "relayOverridesImages", "relayRunsAfterAdventureScript", "relayRunsBeforeBattleScript", "relayRunsBeforePVPScript", "relayScriptButtonFirst", "relayTextualizesEffects", "relayTrimsZapList", "relayUsesInlineLinks", "relayUsesIntegratedChat", "relayWarnOnRecoverFailure", "removeMalignantEffects", "saveSettingsOnSet", "sharePriceData", "showAllRequests", "showExceptionalRequests", "stealthLogin", "svnAlwaysAdd", "svnAlwaysOverwrite", "svnInstallDependencies", "svnShowCommitMessages", "svnUpdateOnLogin", "switchEquipmentForBuffs", "syncAfterSvnUpdate", "useChatToolbar", "useContactsFrame", "useDevServer", "useDockIconBadge", "useHugglerChannel", "useImageCache", "useLastUserAgent", "useSystemTrayIcon", "useTabbedChatFrame", "useToolbars", "useCachedVolcanoMaps", "useZoneComboBox", "verboseSpeakeasy", "verboseFloundry", "wrapLongLines", "_gitUpdated", "_svnRepoFileFetched", "_svnUpdated", "antagonisticSnowmanKitAvailable", "arcadeGameHints", "armoryUnlocked", "autoForbidIgnoringStores", "autoCraft", "autoQuest", "autoEntangle", "autoGarish", "autoManaRestore", "autoFillMayoMinder", "autoPinkyRing", "autoPlantHardcore", "autoPlantSoftcore", "autoPotionID", "autoRepairBoxServants", "autoSatisfyWithCloset", "autoSatisfyWithCoinmasters", "autoSatisfyWithMall", "autoSatisfyWithNPCs", "autoSatisfyWithStash", "autoSatisfyWithStorage", "autoSetConditions", "autoSteal", "autoTuxedo", "backupCameraReverserEnabled", "badMoonEncounter01", "badMoonEncounter02", "badMoonEncounter03", "badMoonEncounter04", "badMoonEncounter05", "badMoonEncounter06", "badMoonEncounter07", "badMoonEncounter08", "badMoonEncounter09", "badMoonEncounter10", "badMoonEncounter11", "badMoonEncounter12", "badMoonEncounter13", "badMoonEncounter14", "badMoonEncounter15", "badMoonEncounter16", "badMoonEncounter17", "badMoonEncounter18", "badMoonEncounter19", "badMoonEncounter20", "badMoonEncounter21", "badMoonEncounter22", "badMoonEncounter23", "badMoonEncounter24", "badMoonEncounter25", "badMoonEncounter26", "badMoonEncounter27", "badMoonEncounter28", "badMoonEncounter29", "badMoonEncounter30", "badMoonEncounter31", "badMoonEncounter32", "badMoonEncounter33", "badMoonEncounter34", "badMoonEncounter35", "badMoonEncounter36", "badMoonEncounter37", "badMoonEncounter38", "badMoonEncounter39", "badMoonEncounter40", "badMoonEncounter41", "badMoonEncounter42", "badMoonEncounter43", "badMoonEncounter44", "badMoonEncounter45", "badMoonEncounter46", "badMoonEncounter47", "badMoonEncounter48", "barrelShrineUnlocked", "bigBrotherRescued", "blackBartsBootyAvailable", "bondAdv", "bondBeach", "bondBeat", "bondBooze", "bondBridge", "bondDesert", "bondDR", "bondDrunk1", "bondDrunk2", "bondHoney", "bondHP", "bondInit", "bondItem1", "bondItem2", "bondItem3", "bondJetpack", "bondMartiniDelivery", "bondMartiniPlus", "bondMartiniTurn", "bondMeat", "bondMox1", "bondMox2", "bondMPregen", "bondMus1", "bondMus2", "bondMys1", "bondMys2", "bondSpleen", "bondStat", "bondStat2", "bondStealth", "bondStealth2", "bondSymbols", "bondWar", "bondWeapon2", "bondWpn", "booPeakLit", "bootsCharged", "breakfastCompleted", "burrowgrubHiveUsed", "calzoneOfLegendEaten", "canteenUnlocked", "chaosButterflyThrown", "chatbotScriptExecuted", "chateauAvailable", "chatLiterate", "chatServesUpdates", "checkJackassHardcore", "checkJackassSoftcore", "clanAttacksEnabled", "coldAirportAlways", "considerShadowNoodles", "controlRoomUnlock", "concertVisited", "controlPanel1", "controlPanel2", "controlPanel3", "controlPanel4", "controlPanel5", "controlPanel6", "controlPanel7", "controlPanel8", "controlPanel9", "corralUnlocked", "dailyDungeonDone", "dampOldBootPurchased", "daycareOpen", "deepDishOfLegendEaten", "demonSummoned", "dinseyAudienceEngagement", "dinseyGarbagePirate", "dinseyRapidPassEnabled", "dinseyRollercoasterNext", "dinseySafetyProtocolsLoose", "doghouseBoarded", "dontStopForCounters", "drippingHallUnlocked", "drippyShieldUnlocked", "edUsedLash", "eldritchFissureAvailable", "eldritchHorrorAvailable", "errorOnAmbiguousFold", "essenceOfAnnoyanceAvailable", "essenceOfBearAvailable", "expressCardUsed", "falloutShelterChronoUsed", "falloutShelterCoolingTankUsed", "fireExtinguisherBatHoleUsed", "fireExtinguisherChasmUsed", "fireExtinguisherCyrptUsed", "fireExtinguisherDesertUsed", "fireExtinguisherHaremUsed", "fistTeachingsHaikuDungeon", "fistTeachingsPokerRoom", "fistTeachingsBarroomBrawl", "fistTeachingsConservatory", "fistTeachingsBatHole", "fistTeachingsFunHouse", "fistTeachingsMenagerie", "fistTeachingsSlums", "fistTeachingsFratHouse", "fistTeachingsRoad", "fistTeachingsNinjaSnowmen", "flickeringPixel1", "flickeringPixel2", "flickeringPixel3", "flickeringPixel4", "flickeringPixel5", "flickeringPixel6", "flickeringPixel7", "flickeringPixel8", "floristFriarAvailable", "floristFriarChecked", "frAlways", "frCemetaryUnlocked", "friarsBlessingReceived", "frMountainsUnlocked", "frSwampUnlocked", "frVillageUnlocked", "frWoodUnlocked", "getawayCampsiteUnlocked", "ghostPencil1", "ghostPencil2", "ghostPencil3", "ghostPencil4", "ghostPencil5", "ghostPencil6", "ghostPencil7", "ghostPencil8", "ghostPencil9", "gingerAdvanceClockUnlocked", "gingerBlackmailAccomplished", "gingerbreadCityAvailable", "gingerExtraAdventures", "gingerNegativesDropped", "gingerSewersUnlocked", "gingerSubwayLineUnlocked", "gingerRetailUnlocked", "glitchItemAvailable", "grabCloversHardcore", "grabCloversSoftcore", "guideToSafariAvailable", "guyMadeOfBeesDefeated", "hallowienerDefiledNook", "hallowienerGuanoJunction", "hallowienerKnollGym", "hallowienerMadnessBakery", "hallowienerMiddleChamber", "hallowienerOvergrownLot", "hallowienerSkeletonStore", "hallowienerSmutOrcs", "hallowienerSonofaBeach", "hallowienerVolcoino", "hardcorePVPWarning", "harvestBatteriesHardcore", "harvestBatteriesSoftcore", "hasAutumnaton", "hasBartender", "hasChef", "hasCocktailKit", "hasCosmicBowlingBall", "hasDetectiveSchool", "hasMaydayContract", "hasOven", "hasRange", "hasShaker", "hasSushiMat", "hasTwinkleVision", "haveBoxingDaydreamHardcore", "haveBoxingDaydreamSoftcore", "hermitHax0red", "holidayHalsBookAvailable", "horseryAvailable", "hotAirportAlways", "implementGlitchItem", "intenseCurrents", "itemBoughtPerAscension637", "itemBoughtPerAscension8266", "itemBoughtPerAscension10790", "itemBoughtPerAscension10794", "itemBoughtPerAscension10795", "itemBoughtPerCharacter6423", "itemBoughtPerCharacter6428", "itemBoughtPerCharacter6429", "kingLiberated", "lastPirateInsult1", "lastPirateInsult2", "lastPirateInsult3", "lastPirateInsult4", "lastPirateInsult5", "lastPirateInsult6", "lastPirateInsult7", "lastPirateInsult8", "lawOfAveragesAvailable", "leafletCompleted", "ledCandleDropped", "libraryCardUsed", "lockPicked", "logBastilleBattalionBattles", "loginRecoveryHardcore", "loginRecoverySoftcore", "lovebugsUnlocked", "loveTunnelAvailable", "lowerChamberUnlock", "madnessBakeryAvailable", "makePocketWishesHardcore", "makePocketWishesSoftcore", "manualOfNumberologyAvailable", "mappingMonsters", "mapToAnemoneMinePurchased", "mapToKokomoAvailable", "mapToMadnessReefPurchased", "mapToTheDiveBarPurchased", "mapToTheMarinaraTrenchPurchased", "mapToTheSkateParkPurchased", "maraisBeaverUnlock", "maraisCorpseUnlock", "maraisDarkUnlock", "maraisVillageUnlock", "maraisWildlifeUnlock", "maraisWizardUnlock", "maximizerAlwaysCurrent", "maximizerCreateOnHand", "maximizerCurrentMallPrices", "maximizerFoldables", "maximizerIncludeAll", "maximizerNoAdventures", "middleChamberUnlock", "milkOfMagnesiumActive", "moonTuned", "neverendingPartyAlways", "noncombatForcerActive", "oasisAvailable", "odeBuffbotCheck", "oilPeakLit", "oscusSodaUsed", "outrageousSombreroUsed", "overgrownLotAvailable", "ownsFloristFriar", "ownsSpeakeasy", "pathedSummonsHardcore", "pathedSummonsSoftcore", "pizzaOfLegendEaten", "popularTartUnlocked", "potatoAlarmClockUsed", "prAlways", "prayedForGlamour", "prayedForProtection", "prayedForVigor", "primaryLabCheerCoreGrabbed", "pyramidBombUsed", "rageGlandVented", "readManualHardcore", "readManualSoftcore", "relayShowSpoilers", "relayShowWarnings", "rememberDesktopSize", "replicaChateauAvailable", "replicaNeverendingPartyAlways", "replicaWitchessSetAvailable", "requireBoxServants", "requireSewerTestItems", "restUsingCampAwayTent", "restUsingChateau", "ROMOfOptimalityAvailable", "safePickpocket", "schoolOfHardKnocksDiplomaAvailable", "scriptCascadingMenus", "serverAddsCustomCombat", "SHAWARMAInitiativeUnlocked", "showForbiddenStores", "showGainsPerUnit", "showIgnoringStorePrices", "showNoSummonOnly", "showTurnFreeOnly", "skeletonStoreAvailable", "sleazeAirportAlways", "snojoAvailable", "sortByEffect", "sortByRoom", "spacegateAlways", "spacegateVaccine1", "spacegateVaccine2", "spacegateVaccine3", "spaceInvaderDefeated", "spelunkyHints", "spiceMelangeUsed", "spookyAirportAlways", "stenchAirportAlways", "stopForFixedWanderer", "stopForUltraRare", "styxPixieVisited", "superconductorDefeated", "suppressInappropriateNags", "suppressPowerPixellation", "suppressMallPriceCacheMessages", "telegraphOfficeAvailable", "telescopeLookedHigh", "timeTowerAvailable", "trackLightsOut", "uneffectWithHotTub", "universalSeasoningActive", "universalSeasoningAvailable", "useBookOfEverySkillHardcore", "useBookOfEverySkillSoftcore", "useCrimboToysHardcore", "useCrimboToysSoftcore", "verboseMaximizer", "visitLoungeHardcore", "visitLoungeSoftcore", "visitRumpusHardcore", "visitRumpusSoftcore", "voteAlways", "wildfireBarrelCaulked", "wildfireDusted", "wildfireFracked", "wildfirePumpGreased", "wildfireSprinkled", "yearbookCameraPending", "youRobotScavenged", "_2002MrStoreCreditsCollected", "_affirmationCookieEaten", "_affirmationHateUsed", "_airFryerUsed", "_akgyxothUsed", "_alienAnimalMilkUsed", "_alienPlantPodUsed", "_allYearSucker", "_aprilShower", "_armyToddlerCast", "_aug1Cast", "_aug2Cast", "_aug3Cast", "_aug4Cast", "_aug5Cast", "_aug6Cast", "_aug7Cast", "_aug8Cast", "_aug9Cast", "_aug10Cast", "_aug11Cast", "_aug12Cast", "_aug13Cast", "_aug14Cast", "_aug15Cast", "_aug16Cast", "_aug17Cast", "_aug18Cast", "_aug19Cast", "_aug20Cast", "_aug21Cast", "_aug22Cast", "_aug23Cast", "_aug24Cast", "_aug25Cast", "_aug26Cast", "_aug27Cast", "_aug28Cast", "_aug29Cast", "_aug30Cast", "_aug31Cast", "_augTodayCast", "_authorsInkUsed", "_baconMachineUsed", "_bagOfCandy", "_bagOfCandyUsed", "_bagOTricksUsed", "_ballastTurtleUsed", "_ballInACupUsed", "_ballpit", "_barrelPrayer", "_bastilleLastBattleWon", "_beachCombing", "_bendHellUsed", "_blackMonolithUsed", "_blankoutUsed", "_bonersSummoned", "_bookOfEverySkillUsed", "_borrowedTimeUsed", "_bowleggedSwaggerUsed", "_bowlFullOfJellyUsed", "_boxOfHammersUsed", "_brainPreservationFluidUsed", "_brassDreadFlaskUsed", "_cameraUsed", "_canSeekBirds", "_carboLoaded", "_cargoPocketEmptied", "_ceciHatUsed", "_chateauDeskHarvested", "_chateauMonsterFought", "_chibiChanged", "_chronerCrossUsed", "_chronerTriggerUsed", "_chubbyAndPlumpUsed", "_circadianRhythmsRecalled", "_circleDrumUsed", "_clanFortuneBuffUsed", "_claraBellUsed", "_coalPaperweightUsed", "_cocoaDispenserUsed", "_cocktailShakerUsed", "_coldAirportToday", "_coldOne", "_communismUsed", "_confusingLEDClockUsed", "_controlPanelUsed", "_cookbookbatRecipeDrops", "_corruptedStardustUsed", "_cosmicSixPackConjured", "_crappyCameraUsed", "_creepyVoodooDollUsed", "_crimboTraining", "_crimboTree", "_cursedKegUsed", "_cursedMicrowaveUsed", "_dailyDungeonMalwareUsed", "_darkChocolateHeart", "_daycareFights", "_daycareNap", "_daycareSpa", "_daycareToday", "_defectiveTokenChecked", "_defectiveTokenUsed", "_dinseyGarbageDisposed", "_discoKnife", "_distentionPillUsed", "_dnaHybrid", "_docClocksThymeCocktailDrunk", "_drippingHallDoor1", "_drippingHallDoor2", "_drippingHallDoor3", "_drippingHallDoor4", "_drippyCaviarUsed", "_drippyNuggetUsed", "_drippyPilsnerUsed", "_drippyPlumUsed", "_drippyWineUsed", "_eldritchHorrorEvoked", "_eldritchTentacleFought", "_entauntaunedToday", "_envyfishEggUsed", "_epicMcTwistUsed", "_essentialTofuUsed", "_etchedHourglassUsed", "_eternalCarBatteryUsed", "_everfullGlassUsed", "_eyeAndATwistUsed", "_fancyChessSetUsed", "_falloutShelterSpaUsed", "_fancyHotDogEaten", "_farmerItemsCollected", "_favoriteBirdVisited", "_firedJokestersGun", "_fireExtinguisherRefilled", "_fireStartingKitUsed", "_fireworksShop", "_fireworksShopHatBought", "_fireworksShopEquipmentBought", "_fireworkUsed", "_fishyPipeUsed", "_floundryItemCreated", "_floundryItemUsed", "_freePillKeeperUsed", "_frToday", "_fudgeSporkUsed", "_garbageItemChanged", "_gingerBiggerAlligators", "_gingerbreadCityToday", "_gingerbreadClockAdvanced", "_gingerbreadClockVisited", "_gingerbreadColumnDestroyed", "_gingerbreadMobHitUsed", "_glennGoldenDiceUsed", "_glitchItemImplemented", "_gnollEyeUsed", "_governmentPerDiemUsed", "_grimBuff", "_guildManualUsed", "_guzzlrQuestAbandoned", "_hardKnocksDiplomaUsed", "_hippyMeatCollected", "_hobbyHorseUsed", "_holidayFunUsed", "_holoWristCrystal", "_hotAirportToday", "_hungerSauceUsed", "_hyperinflatedSealLungUsed", "_iceHotelRoomsRaided", "_iceSculptureUsed", "_incredibleSelfEsteemCast", "_infernoDiscoVisited", "_internetDailyDungeonMalwareBought", "_internetGallonOfMilkBought", "_internetPlusOneBought", "_internetPrintScreenButtonBought", "_internetViralVideoBought", "_interviewIsabella", "_interviewMasquerade", "_interviewVlad", "_inquisitorsUnidentifiableObjectUsed", "_ironicMoustache", "_jackassPlumberGame", "_jarlsCheeseSummoned", "_jarlsCreamSummoned", "_jarlsDoughSummoned", "_jarlsEggsSummoned", "_jarlsFruitSummoned", "_jarlsMeatSummoned", "_jarlsPotatoSummoned", "_jarlsVeggiesSummoned", "_jingleBellUsed", "_jukebox", "_kgbFlywheelCharged", "_kgbLeftDrawerUsed", "_kgbOpened", "_kgbRightDrawerUsed", "_kolConSixPackUsed", "_kolhsCutButNotDried", "_kolhsIsskayLikeAnAshtray", "_kolhsPoeticallyLicenced", "_kolhsSchoolSpirited", "_kudzuSaladEaten", "_lastCombatLost", "_lastCombatWon", "_latteBanishUsed", "_latteCopyUsed", "_latteDrinkUsed", "_leafAntEggCrafted", "_leafDayShortenerCrafted", "_leafTattooCrafted", "_leavesJumped", "_legendaryBeat", "_licenseToChillUsed", "_lodestoneUsed", "_lookingGlass", "_loveTunnelToday", "_loveTunnelUsed", "_luckyGoldRingVolcoino", "_lunchBreak", "_lupineHormonesUsed", "_lyleFavored", "_madLiquorDrunk", "_madTeaParty", "_mafiaMiddleFingerRingUsed", "_managerialManipulationUsed", "_mansquitoSerumUsed", "_mapToACandyRichBlockUsed", "_maydayDropped", "_mayoDeviceRented", "_mayoTankSoaked", "_meatballMachineUsed", "_meatifyMatterUsed", "_milkOfMagnesiumUsed", "_mimeArmyShotglassUsed", "_missGravesVermouthDrunk", "_missileLauncherUsed", "_molehillMountainUsed", "_momFoodReceived", "_mrBurnsgerEaten", "_muffinOrderedToday", "_mushroomGardenVisited", "_neverendingPartyToday", "_newYouQuestCompleted", "_olympicSwimmingPool", "_olympicSwimmingPoolItemFound", "_overflowingGiftBasketUsed", "_partyHard", "_pastaAdditive", "_perfectFreezeUsed", "_perfectlyFairCoinUsed", "_petePartyThrown", "_peteRiotIncited", "_photocopyUsed", "_pickyTweezersUsed", "_pingPongGame", "_pirateBellowUsed", "_pirateForkUsed", "_pixelOrbUsed", "_plumbersMushroomStewEaten", "_pneumaticityPotionUsed", "_portableSteamUnitUsed", "_pottedTeaTreeUsed", "_prToday", "_psychoJarFilled", "_psychoJarUsed", "_psychokineticHugUsed", "_rainStickUsed", "_redwoodRainStickUsed", "_replicaSnowconeTomeUsed", "_replicaResolutionLibramUsed", "_replicaSmithsTomeUsed", "_requestSandwichSucceeded", "_rhinestonesAcquired", "_seaJellyHarvested", "_setOfJacksUsed", "_sewingKitUsed", "_sexChanged", "_shadowAffinityToday", "_shadowForestLooted", "_shrubDecorated", "_silverDreadFlaskUsed", "_sitCourseCompleted", "_skateBuff1", "_skateBuff2", "_skateBuff3", "_skateBuff4", "_skateBuff5", "_sleazeAirportToday", "_sobrieTeaUsed", "_softwareGlitchTurnReceived", "_sotParcelReturned", "_spacegateMurderbot", "_spacegateRuins", "_spacegateSpant", "_spacegateToday", "_spacegateVaccine", "_spaghettiBreakfast", "_spaghettiBreakfastEaten", "_spinmasterLatheVisited", "_spinningWheel", "_spookyAirportToday", "_stabonicScrollUsed", "_steelyEyedSquintUsed", "_stenchAirportToday", "_stinkyCheeseBanisherUsed", "_strangeStalagmiteUsed", "_streamsCrossed", "_stuffedPocketwatchUsed", "_styxSprayUsed", "_summonAnnoyanceUsed", "_summonCarrotUsed", "_summonResortPassUsed", "_sweetToothUsed", "_syntheticDogHairPillUsed", "_tacoFlierUsed", "_telegraphOfficeToday", "_templeHiddenPower", "_tempuraAirUsed", "_thesisDelivered", "_tiedUpFlamingLeafletFought", "_tiedUpFlamingMonsteraFought", "_tiedUpLeaviathanFought", "_timeSpinnerReplicatorUsed", "_toastSummoned", "_tonicDjinn", "_treasuryEliteMeatCollected", "_treasuryHaremMeatCollected", "_trivialAvocationsGame", "_tryptophanDartUsed", "_turtlePowerCast", "_twelveNightEnergyUsed", "_ultraMegaSourBallUsed", "_victorSpoilsUsed", "_villainLairCanLidUsed", "_villainLairColorChoiceUsed", "_villainLairDoorChoiceUsed", "_villainLairFirecrackerUsed", "_villainLairSymbologyChoiceUsed", "_villainLairWebUsed", "_vmaskBanisherUsed", "_voraciTeaUsed", "_volcanoItemRedeemed", "_volcanoSuperduperheatedMetal", "_voteToday", "_VYKEACafeteriaRaided", "_VYKEALoungeRaided", "_walfordQuestStartedToday", "_warbearBankUsed", "_warbearBreakfastMachineUsed", "_warbearGyrocopterUsed", "_warbearSodaMachineUsed", "_wildfireBarrelHarvested", "_witchessBuff", "_workshedItemUsed", "_zombieClover", "_preventScurvy", "lockedItem4637", "lockedItem4638", "lockedItem4639", "lockedItem4646", "lockedItem4647", "unknownRecipe3542", "unknownRecipe3543", "unknownRecipe3544", "unknownRecipe3545", "unknownRecipe3546", "unknownRecipe3547", "unknownRecipe3548", "unknownRecipe3749", "unknownRecipe3751", "unknownRecipe4172", "unknownRecipe4173", "unknownRecipe4174", "unknownRecipe5060", "unknownRecipe5061", "unknownRecipe5062", "unknownRecipe5063", "unknownRecipe5064", "unknownRecipe5066", "unknownRecipe5067", "unknownRecipe5069", "unknownRecipe5070", "unknownRecipe5072", "unknownRecipe5073", "unknownRecipe5670", "unknownRecipe5671", "unknownRecipe6501", "unknownRecipe6564", "unknownRecipe6565", "unknownRecipe6566", "unknownRecipe6567", "unknownRecipe6568", "unknownRecipe6569", "unknownRecipe6570", "unknownRecipe6571", "unknownRecipe6572", "unknownRecipe6573", "unknownRecipe6574", "unknownRecipe6575", "unknownRecipe6576", "unknownRecipe6577", "unknownRecipe6578", "unknownRecipe7752", "unknownRecipe7753", "unknownRecipe7754", "unknownRecipe7755", "unknownRecipe7756", "unknownRecipe7757", "unknownRecipe7758", "unknownRecipe10970", "unknownRecipe10971", "unknownRecipe10972", "unknownRecipe10973", "unknownRecipe10974", "unknownRecipe10975", "unknownRecipe10976", "unknownRecipe10977", "unknownRecipe10978", "unknownRecipe10988", "unknownRecipe10989", "unknownRecipe10990", "unknownRecipe10991", "unknownRecipe10992", "unknownRecipe11000"]; +var numericProperties = ["coinMasterIndex", "dailyDeedsVersion", "defaultDropdown1", "defaultDropdown2", "defaultDropdownSplit", "defaultLimit", "fixedThreadPoolSize", "itemManagerIndex", "lastBuffRequestType", "lastGlobalCounterDay", "lastImageCacheClear", "pingDefaultTestPings", "pingLoginCount", "pingLoginGoal", "pingLoginThreshold", "pingTestPings", "previousUpdateRevision", "relayDelayForSVN", "relaySkillButtonCount", "scriptButtonPosition", "statusDropdown", "svnThreadPoolSize", "toolbarPosition", "_beachTides", "_g9Effect", "8BitBonusTurns", "8BitScore", "addingScrolls", "affirmationCookiesEaten", "aminoAcidsUsed", "antagonisticSnowmanKitCost", "ascensionsToday", "asolDeferredPoints", "asolPointsPigSkinner", "asolPointsCheeseWizard", "asolPointsJazzAgent", "autoAbortThreshold", "autoAntidote", "autoBuyPriceLimit", "autumnatonQuestTurn", "availableCandyCredits", "availableDimes", "availableFunPoints", "availableMrStore2002Credits", "availableQuarters", "availableStoreCredits", "availableSwagger", "averageSwagger", "awolMedicine", "awolPointsBeanslinger", "awolPointsCowpuncher", "awolPointsSnakeoiler", "awolDeferredPointsBeanslinger", "awolDeferredPointsCowpuncher", "awolDeferredPointsSnakeoiler", "awolVenom", "bagOTricksCharges", "ballpitBonus", "bankedKarma", "bartenderTurnsUsed", "basementMallPrices", "basementSafetyMargin", "batmanFundsAvailable", "batmanBonusInitialFunds", "batmanTimeLeft", "bearSwagger", "beeCounter", "beGregariousCharges", "beGregariousFightsLeft", "birdformCold", "birdformHot", "birdformRoc", "birdformSleaze", "birdformSpooky", "birdformStench", "blackBartsBootyCost", "blackPuddingsDefeated", "blackForestProgress", "blankOutUsed", "bloodweiserDrunk", "bondPoints", "bondVillainsDefeated", "boneAbacusVictories", "bookOfFactsGummi", "bookOfFactsPinata", "booPeakProgress", "borisPoints", "breakableHandling", "breakableHandling1964", "breakableHandling9691", "breakableHandling9692", "breakableHandling9699", "breathitinCharges", "brodenBacteria", "brodenSprinkles", "buffBotMessageDisposal", "buffBotPhilanthropyType", "buffJimmyIngredients", "burnoutsDefeated", "burrowgrubSummonsRemaining", "camelSpit", "camerasUsed", "campAwayDecoration", "candyWitchTurnsUsed", "candyWitchCandyTotal", "carboLoading", "catBurglarBankHeists", "cellarLayout", "charitableDonations", "chasmBridgeProgress", "chefTurnsUsed", "chessboardsCleared", "chibiAlignment", "chibiBirthday", "chibiFitness", "chibiIntelligence", "chibiLastVisit", "chibiSocialization", "chilledToTheBone", "cinchoSaltAndLime", "cinderellaMinutesToMidnight", "cinderellaScore", "cocktailSummons", "commerceGhostCombats", "controlPanelOmega", "cornucopiasOpened", "cosmicBowlingBallReturnCombats", "cozyCounter6332", "cozyCounter6333", "cozyCounter6334", "craftingClay", "craftingLeather", "craftingStraw", "crimbo16BeardChakraCleanliness", "crimbo16BootsChakraCleanliness", "crimbo16BungChakraCleanliness", "crimbo16CrimboHatChakraCleanliness", "crimbo16GutsChakraCleanliness", "crimbo16HatChakraCleanliness", "crimbo16JellyChakraCleanliness", "crimbo16LiverChakraCleanliness", "crimbo16NippleChakraCleanliness", "crimbo16NoseChakraCleanliness", "crimbo16ReindeerChakraCleanliness", "crimbo16SackChakraCleanliness", "crimboTrainingSkill", "crimboTreeDays", "cubelingProgress", "currentExtremity", "currentHedgeMazeRoom", "currentMojoFilters", "currentNunneryMeat", "currentPortalEnergy", "currentReplicaStoreYear", "cursedMagnifyingGlassCount", "cyrptAlcoveEvilness", "cyrptCrannyEvilness", "cyrptNicheEvilness", "cyrptNookEvilness", "cyrptTotalEvilness", "darkGyfftePoints", "daycareEquipment", "daycareInstructors", "daycareLastScavenge", "daycareToddlers", "dbNemesisSkill1", "dbNemesisSkill2", "dbNemesisSkill3", "desertExploration", "desktopHeight", "desktopWidth", "dinseyFilthLevel", "dinseyFunProgress", "dinseyNastyBearsDefeated", "dinseySocialJusticeIProgress", "dinseySocialJusticeIIProgress", "dinseyTouristsFed", "dinseyToxicMultiplier", "doctorBagQuestLights", "doctorBagUpgrades", "dreadScroll1", "dreadScroll2", "dreadScroll3", "dreadScroll4", "dreadScroll5", "dreadScroll6", "dreadScroll7", "dreadScroll8", "dripAdventuresSinceAscension", "drippingHallAdventuresSinceAscension", "drippingTreesAdventuresSinceAscension", "drippyBatsUnlocked", "drippyJuice", "drippyOrbsClaimed", "drunkenSwagger", "edDefeatAbort", "edPoints", "eldritchTentaclesFought", "electricKoolAidEaten", "elfGratitude", "encountersUntilDMTChoice", "encountersUntilYachtzeeChoice", "encountersUntilNEPChoice", "encountersUntilSRChoice", "ensorceleeLevel", "entauntaunedColdRes", "essenceOfAnnoyanceCost", "essenceOfBearCost", "extraRolloverAdventures", "falloutShelterLevel", "familiarSweat", "fingernailsClipped", "fistSkillsKnown", "flyeredML", "fossilB", "fossilD", "fossilN", "fossilP", "fossilS", "fossilW", "fratboysDefeated", "frenchGuardTurtlesFreed", "funGuyMansionKills", "garbageChampagneCharge", "garbageFireProgress", "garbageShirtCharge", "garbageTreeCharge", "garlandUpgrades", "getsYouDrunkTurnsLeft", "ghostPepperTurnsLeft", "gingerDigCount", "gingerLawChoice", "gingerMuscleChoice", "gingerTrainScheduleStudies", "gladiatorBallMovesKnown", "gladiatorBladeMovesKnown", "gladiatorNetMovesKnown", "glitchItemCost", "glitchItemImplementationCount", "glitchItemImplementationLevel", "glitchSwagger", "gloverPoints", "gnasirProgress", "goldenMrAccessories", "gongPath", "gooseDronesRemaining", "goreCollected", "gourdItemCount", "greyYouPoints", "grimoire1Summons", "grimoire2Summons", "grimoire3Summons", "grimstoneCharge", "guardTurtlesFreed", "guideToSafariCost", "guyMadeOfBeesCount", "guzzlrBronzeDeliveries", "guzzlrDeliveryProgress", "guzzlrGoldDeliveries", "guzzlrPlatinumDeliveries", "haciendaLayout", "hallowiener8BitRealm", "hallowienerCoinspiracy", "hareMillisecondsSaved", "hareTurnsUsed", "heavyRainsStartingThunder", "heavyRainsStartingRain", "heavyRainsStartingLightning", "heroDonationBoris", "heroDonationJarlsberg", "heroDonationSneakyPete", "hiddenApartmentProgress", "hiddenBowlingAlleyProgress", "hiddenHospitalProgress", "hiddenOfficeProgress", "hiddenTavernUnlock", "highTopPumped", "hippiesDefeated", "holidayHalsBookCost", "holidaySwagger", "homemadeRobotUpgrades", "homebodylCharges", "hpAutoRecovery", "hpAutoRecoveryTarget", "iceSwagger", "jarlsbergPoints", "jungCharge", "junglePuns", "knownAscensions", "kolhsTotalSchoolSpirited", "lastAnticheeseDay", "lastArcadeAscension", "lastBadMoonReset", "lastBangPotionReset", "lastBattlefieldReset", "lastBeardBuff", "lastBreakfast", "lastCartographyBooPeak", "lastCartographyCastleTop", "lastCartographyDarkNeck", "lastCartographyDefiledNook", "lastCartographyFratHouse", "lastCartographyFratHouseVerge", "lastCartographyGuanoJunction", "lastCartographyHauntedBilliards", "lastCartographyHippyCampVerge", "lastCartographyZeppelinProtesters", "lastCastleGroundUnlock", "lastCastleTopUnlock", "lastCellarReset", "lastChanceThreshold", "lastChasmReset", "lastColosseumRoundWon", "lastCouncilVisit", "lastCounterDay", "lastDesertUnlock", "lastDispensaryOpen", "lastDMTDuplication", "lastDwarfFactoryReset", "lastEVHelmetValue", "lastEVHelmetReset", "lastEmptiedStorage", "lastFilthClearance", "lastGoofballBuy", "lastGuildStoreOpen", "lastGuyMadeOfBeesReset", "lastFratboyCall", "lastFriarCeremonyAscension", "lastFriarsElbowNC", "lastFriarsHeartNC", "lastFriarsNeckNC", "lastHippyCall", "lastIslandUnlock", "lastKeyotronUse", "lastKingLiberation", "lastLightsOutTurn", "lastMushroomPlot", "lastMiningReset", "lastNemesisReset", "lastPaperStripReset", "lastPirateEphemeraReset", "lastPirateInsultReset", "lastPlusSignUnlock", "lastQuartetAscension", "lastQuartetRequest", "lastSecondFloorUnlock", "lastShadowForgeUnlockAdventure", "lastSkateParkReset", "lastStillBeatingSpleen", "lastTavernAscension", "lastTavernSquare", "lastTelescopeReset", "lastTempleAdventures", "lastTempleButtonsUnlock", "lastTempleUnlock", "lastThingWithNoNameDefeated", "lastTowelAscension", "lastTr4pz0rQuest", "lastTrainsetConfiguration", "lastVioletFogMap", "lastVoteMonsterTurn", "lastWartDinseyDefeated", "lastWuTangDefeated", "lastYearbookCameraAscension", "lastZapperWand", "lastZapperWandExplosionDay", "lawOfAveragesCost", "legacyPoints", "libramSummons", "lightsOutAutomation", "louvreDesiredGoal", "louvreGoal", "lovebugsAridDesert", "lovebugsBeachBuck", "lovebugsBooze", "lovebugsChroner", "lovebugsCoinspiracy", "lovebugsCyrpt", "lovebugsFreddy", "lovebugsFunFunds", "lovebugsHoboNickel", "lovebugsItemDrop", "lovebugsMeat", "lovebugsMeatDrop", "lovebugsMoxie", "lovebugsMuscle", "lovebugsMysticality", "lovebugsOilPeak", "lovebugsOrcChasm", "lovebugsPowder", "lovebugsWalmart", "lttQuestDifficulty", "lttQuestStageCount", "manaBurnSummonThreshold", "manaBurningThreshold", "manaBurningTrigger", "manorDrawerCount", "manualOfNumberologyCost", "mapToKokomoCost", "masksUnlocked", "maximizerMRUSize", "maximizerCombinationLimit", "maximizerEquipmentLevel", "maximizerEquipmentScope", "maximizerMaxPrice", "maximizerPriceLevel", "maxManaBurn", "mayflyExperience", "mayoLevel", "meansuckerPrice", "merkinVocabularyMastery", "miniAdvClass", "miniMartinisDrunk", "moleTunnelLevel", "mothershipProgress", "mpAutoRecovery", "mpAutoRecoveryTarget", "munchiesPillsUsed", "mushroomGardenCropLevel", "nextParanormalActivity", "nextQuantumFamiliarOwnerId", "nextQuantumFamiliarTurn", "noobPoints", "noobDeferredPoints", "noodleSummons", "nsContestants1", "nsContestants2", "nsContestants3", "nuclearAutumnPoints", "numericSwagger", "nunsVisits", "oilPeakProgress", "optimalSwagger", "optimisticCandleProgress", "palindomeDudesDefeated", "parasolUsed", "pendingMapReflections", "pingpongSkill", "pirateSwagger", "plantingDay", "plumberBadgeCost", "plumberCostumeCost", "plumberPoints", "poolSharkCount", "poolSkill", "primaryLabGooIntensity", "prismaticSummons", "procrastinatorLanguageFluency", "promptAboutCrafting", "puzzleChampBonus", "pyramidPosition", "quantumPoints", "reagentSummons", "reanimatorArms", "reanimatorLegs", "reanimatorSkulls", "reanimatorWeirdParts", "reanimatorWings", "recentLocations", "redSnapperProgress", "relayPort", "relocatePygmyJanitor", "relocatePygmyLawyer", "rockinRobinProgress", "ROMOfOptimalityCost", "rumpelstiltskinKidsRescued", "rumpelstiltskinTurnsUsed", "rwbMonsterCount", "safariSwagger", "sausageGrinderUnits", "schoolOfHardKnocksDiplomaCost", "schoolSwagger", "scrapbookCharges", "screechCombats", "scriptMRULength", "seaodesFound", "SeasoningSwagger", "sexChanges", "shenInitiationDay", "shockingLickCharges", "singleFamiliarRun", "skillBurn3", "skillBurn90", "skillBurn153", "skillBurn154", "skillBurn155", "skillBurn1019", "skillBurn5017", "skillBurn6014", "skillBurn6015", "skillBurn6016", "skillBurn6020", "skillBurn6021", "skillBurn6022", "skillBurn6023", "skillBurn6024", "skillBurn6026", "skillBurn6028", "skillBurn7323", "skillBurn14008", "skillBurn14028", "skillBurn14038", "skillBurn15011", "skillBurn15028", "skillBurn17005", "skillBurn22034", "skillBurn22035", "skillBurn23301", "skillBurn23302", "skillBurn23303", "skillBurn23304", "skillBurn23305", "skillBurn23306", "skillLevel46", "skillLevel47", "skillLevel48", "skillLevel117", "skillLevel118", "skillLevel121", "skillLevel128", "skillLevel134", "skillLevel144", "skillLevel180", "skillLevel188", "skillLevel227", "skillLevel7254", "slimelingFullness", "slimelingStacksDropped", "slimelingStacksDue", "smoresEaten", "smutOrcNoncombatProgress", "sneakyPetePoints", "snojoMoxieWins", "snojoMuscleWins", "snojoMysticalityWins", "sourceAgentsDefeated", "sourceEnlightenment", "sourceInterval", "sourcePoints", "sourceTerminalGram", "sourceTerminalPram", "sourceTerminalSpam", "spaceBabyLanguageFluency", "spacePirateLanguageFluency", "spelunkyNextNoncombat", "spelunkySacrifices", "spelunkyWinCount", "spookyPuttyCopiesMade", "spookyVHSTapeMonsterTurn", "statbotUses", "sugarCounter4178", "sugarCounter4179", "sugarCounter4180", "sugarCounter4181", "sugarCounter4182", "sugarCounter4183", "sugarCounter4191", "summonAnnoyanceCost", "sweat", "tacoDanCocktailSauce", "tacoDanFishMeat", "tavernLayout", "telescopeUpgrades", "tempuraSummons", "timeSpinnerMedals", "timesRested", "tomeSummons", "totalCharitableDonations", "trainsetPosition", "turtleBlessingTurns", "twinPeakProgress", "twoCRSPoints", "unicornHornInflation", "universalSeasoningCost", "usable1HWeapons", "usable1xAccs", "usable2HWeapons", "usable3HWeapons", "usableAccessories", "usableHats", "usableOffhands", "usableOther", "usablePants", "usableShirts", "valueOfAdventure", "valueOfInventory", "valueOfStill", "valueOfTome", "vintnerCharge", "vintnerWineLevel", "violetFogGoal", "walfordBucketProgress", "warehouseProgress", "welcomeBackAdv", "whetstonesUsed", "wolfPigsEvicted", "wolfTurnsUsed", "writingDesksDefeated", "xoSkeleltonXProgress", "xoSkeleltonOProgress", "yearbookCameraAscensions", "yearbookCameraUpgrades", "youRobotBody", "youRobotBottom", "youRobotLeft", "youRobotPoints", "youRobotRight", "youRobotTop", "zeppelinProtestors", "zigguratLianas", "zombiePoints", "_absintheDrops", "_abstractionDropsCrown", "_aguaDrops", "_xenomorphCharge", "_ancestralRecallCasts", "_antihangoverBonus", "_astralDrops", "_augSkillsCast", "_autumnatonQuests", "_backUpUses", "_badlyRomanticArrows", "_badgerCharge", "_balefulHowlUses", "_banderRunaways", "_bastilleCheese", "_bastilleGames", "_bastilleGameTurn", "_bastilleLastCheese", "_beanCannonUses", "_bearHugs", "_beerLensDrops", "_bellydancerPickpockets", "_benettonsCasts", "_birdsSoughtToday", "_bookOfFactsWishes", "_bookOfFactsTatters", "_boomBoxFights", "_boomBoxSongsLeft", "_bootStomps", "_boxingGloveArrows", "_brickoEyeSummons", "_brickoFights", "_campAwayCloudBuffs", "_campAwaySmileBuffs", "_candySummons", "_captainHagnkUsed", "_carnieCandyDrops", "_carrotNoseDrops", "_catBurglarCharge", "_catBurglarHeistsComplete", "_cheerleaderSteam", "_chestXRayUsed", "_chibiAdventures", "_chipBags", "_chocolateCigarsUsed", "_chocolateCoveredPingPongBallsUsed", "_chocolateSculpturesUsed", "_chocolatesUsed", "_chronolithActivations", "_chronolithNextCost", "_cinchUsed", "_cinchoRests", "_circadianRhythmsAdventures", "_clanFortuneConsultUses", "_clipartSummons", "_cloversPurchased", "_coldMedicineConsults", "_coldMedicineEquipmentTaken", "_companionshipCasts", "_cookbookbatCrafting", "_cosmicBowlingSkillsUsed", "_crimbo21ColdResistance", "_dailySpecialPrice", "_daycareGymScavenges", "_daycareRecruits", "_deckCardsDrawn", "_deluxeKlawSummons", "_demandSandwich", "_detectiveCasesCompleted", "_disavowed", "_dnaPotionsMade", "_donhosCasts", "_douseFoeUses", "_dreamJarDrops", "_drunkPygmyBanishes", "_edDefeats", "_edLashCount", "_elronsCasts", "_enamorangs", "_energyCollected", "_expertCornerCutterUsed", "_favorRareSummons", "_feastUsed", "_feelinTheRhythm", "_feelPrideUsed", "_feelExcitementUsed", "_feelHatredUsed", "_feelLonelyUsed", "_feelNervousUsed", "_feelEnvyUsed", "_feelDisappointedUsed", "_feelSuperiorUsed", "_feelLostUsed", "_feelNostalgicUsed", "_feelPeacefulUsed", "_fingertrapArrows", "_fireExtinguisherCharge", "_fragrantHerbsUsed", "_freeBeachWalksUsed", "_frButtonsPressed", "_fudgeWaspFights", "_gapBuffs", "_garbageFireDrops", "_garbageFireDropsCrown", "_genieFightsUsed", "_genieWishesUsed", "_gibbererAdv", "_gibbererCharge", "_gingerbreadCityTurns", "_glarkCableUses", "_glitchMonsterFights", "_gnomeAdv", "_godLobsterFights", "_goldenMoneyCharge", "_gongDrops", "_gothKidCharge", "_gothKidFights", "_greyYouAdventures", "_grimBrotherCharge", "_grimFairyTaleDrops", "_grimFairyTaleDropsCrown", "_grimoireConfiscatorSummons", "_grimoireGeekySummons", "_grimstoneMaskDrops", "_grimstoneMaskDropsCrown", "_grooseCharge", "_grooseDrops", "_grubbyWoolDrops", "_guzzlrDeliveries", "_guzzlrGoldDeliveries", "_guzzlrPlatinumDeliveries", "_hareAdv", "_hareCharge", "_highTopPumps", "_hipsterAdv", "_hoardedCandyDropsCrown", "_hoboUnderlingSummons", "_holoWristDrops", "_holoWristProgress", "_hotAshesDrops", "_hotJellyUses", "_hotTubSoaks", "_humanMuskUses", "_iceballUses", "_inigosCasts", "_jerksHealthMagazinesUsed", "_jiggleCheese", "_jiggleCream", "_jiggleLife", "_jiggleSteak", "_jitbCharge", "_juneCleaverFightsLeft", "_juneCleaverEncounters", "_juneCleaverStench", "_juneCleaverSpooky", "_juneCleaverSleaze", "_juneCleaverHot", "_juneCleaverCold", "_juneCleaverSkips", "_jungDrops", "_kgbClicksUsed", "_kgbDispenserUses", "_kgbTranquilizerDartUses", "_klawSummons", "_kloopCharge", "_kloopDrops", "_kolhsAdventures", "_kolhsSavedByTheBell", "_lastDailyDungeonRoom", "_lastSausageMonsterTurn", "_lastZomboEye", "_latteRefillsUsed", "_leafblowerML", "_leafLassosCrafted", "_leafMonstersFought", "_leavesBurned", "_legionJackhammerCrafting", "_llamaCharge", "_longConUsed", "_lovebugsBeachBuck", "_lovebugsChroner", "_lovebugsCoinspiracy", "_lovebugsFreddy", "_lovebugsFunFunds", "_lovebugsHoboNickel", "_lovebugsWalmart", "_loveChocolatesUsed", "_lynyrdSnareUses", "_machineTunnelsAdv", "_macrometeoriteUses", "_mafiaThumbRingAdvs", "_mapToACandyRichBlockDrops", "_mayflowerDrops", "_mayflySummons", "_mediumSiphons", "_meteoriteAdesUsed", "_meteorShowerUses", "_micrometeoriteUses", "_mildEvilPerpetrated", "_miniMartiniDrops", "_monkeyPawWishesUsed", "_monsterHabitatsFightsLeft", "_monsterHabitatsRecalled", "_monstersMapped", "_mushroomGardenFights", "_nanorhinoCharge", "_navelRunaways", "_neverendingPartyFreeTurns", "_newYouQuestSharpensDone", "_newYouQuestSharpensToDo", "_nextColdMedicineConsult", "_nextQuantumAlignment", "_nightmareFuelCharges", "_noobSkillCount", "_nuclearStockpileUsed", "_oilExtracted", "_olfactionsUsed", "_optimisticCandleDropsCrown", "_oreDropsCrown", "_otoscopeUsed", "_oysterEggsFound", "_pantsgivingBanish", "_pantsgivingCount", "_pantsgivingCrumbs", "_pantsgivingFullness", "_pasteDrops", "_peteJukeboxFixed", "_peteJumpedShark", "_petePeeledOut", "_pieDrops", "_piePartsCount", "_pixieCharge", "_pocketProfessorLectures", "_poisonArrows", "_pokeGrowFertilizerDrops", "_poolGames", "_powderedGoldDrops", "_powderedMadnessUses", "_powerfulGloveBatteryPowerUsed", "_powerPillDrops", "_powerPillUses", "_precisionCasts", "_questPartyFairItemsOpened", "_radlibSummons", "_raindohCopiesMade", "_rapidPrototypingUsed", "_raveStealCount", "_reflexHammerUsed", "_resolutionAdv", "_resolutionRareSummons", "_riftletAdv", "_robinEggDrops", "_roboDrops", "_rogueProgramCharge", "_romanticFightsLeft", "_saberForceMonsterCount", "_saberForceUses", "_saberMod", "_saltGrainsConsumed", "_sandwormCharge", "_saplingsPlanted", "_sausageFights", "_sausagesEaten", "_sausagesMade", "_sealFigurineUses", "_sealScreeches", "_sealsSummoned", "_shadowBricksUsed", "_shadowRiftCombats", "_shatteringPunchUsed", "_shortOrderCookCharge", "_shrubCharge", "_sloppyDinerBeachBucks", "_smilesOfMrA", "_smithsnessSummons", "_snojoFreeFights", "_snojoParts", "_snokebombUsed", "_snowconeSummons", "_snowglobeDrops", "_snowSuitCount", "_sourceTerminalDigitizeMonsterCount", "_sourceTerminalDigitizeUses", "_sourceTerminalDuplicateUses", "_sourceTerminalEnhanceUses", "_sourceTerminalExtrudes", "_sourceTerminalPortscanUses", "_spaceFurDropsCrown", "_spacegatePlanetIndex", "_spacegateTurnsLeft", "_spaceJellyfishDrops", "_speakeasyDrinksDrunk", "_speakeasyFreeFights", "_spelunkerCharges", "_spelunkingTalesDrops", "_spikolodonSpikeUses", "_spookyJellyUses", "_stackLumpsUses", "_steamCardDrops", "_stickerSummons", "_stinkyCheeseCount", "_stressBallSqueezes", "_sugarSummons", "_sweatOutSomeBoozeUsed", "_taffyRareSummons", "_taffyYellowSummons", "_thanksgettingFoodsEaten", "_thingfinderCasts", "_thinknerdPackageDrops", "_thorsPliersCrafting", "_timeHelmetAdv", "_timeSpinnerMinutesUsed", "_tokenDrops", "_transponderDrops", "_turkeyBlastersUsed", "_turkeyBooze", "_turkeyMuscle", "_turkeyMyst", "_turkeyMoxie", "_unaccompaniedMinerUsed", "_unconsciousCollectiveCharge", "_universalSeasoningsUsed", "_universeCalculated", "_universeImploded", "_usedReplicaBatoomerang", "_vampyreCloakeFormUses", "_villainLairProgress", "_vitachocCapsulesUsed", "_vmaskAdv", "_voidFreeFights", "_volcanoItem1", "_volcanoItem2", "_volcanoItem3", "_volcanoItemCount1", "_volcanoItemCount2", "_volcanoItemCount3", "_voteFreeFights", "_VYKEACompanionLevel", "_warbearAutoAnvilCrafting", "_waxGlobDrops", "_whiteRiceDrops", "_witchessFights", "_xoHugsUsed", "_yellowPixelDropsCrown", "_zapCount", "_zombieSmashPocketsUsed"]; +var monsterProperties = ["beGregariousMonster", "cameraMonster", "chateauMonster", "clumsinessGroveBoss", "crappyCameraMonster", "crudeMonster", "enamorangMonster", "envyfishMonster", "glacierOfJerksBoss", "holdHandsMonster", "iceSculptureMonster", "lastCopyableMonster", "longConMonster", "maelstromOfLoversBoss", "makeFriendsMonster", "merkinLockkeyMonster", "monkeyPointMonster", "motifMonster", "nosyNoseMonster", "olfactedMonster", "photocopyMonster", "rainDohMonster", "romanticTarget", "rufusDesiredEntity", "rwbMonster", "screencappedMonster", "spookyPuttyMonster", "spookyVHSTapeMonster", "stenchCursedMonster", "superficiallyInterestedMonster", "waxMonster", "yearbookCameraTarget", "_gallapagosMonster", "_jiggleCreamedMonster", "_latteMonster", "_monsterHabitatsMonster", "_nanorhinoBanishedMonster", "_newYouQuestMonster", "_relativityMonster", "_saberForceMonster", "_sourceTerminalDigitizeMonster", "_voteMonster"]; +var locationProperties = ["autumnatonQuestLocation", "currentJunkyardLocation", "doctorBagQuestLocation", "ghostLocation", "guzzlrQuestLocation", "nextSpookyravenElizabethRoom", "nextSpookyravenStephenRoom", "rwbLocation", "sourceOracleTarget", "_floundryBassLocation", "_floundryCarpLocation", "_floundryCodLocation", "_floundryHatchetfishLocation", "_floundryTroutLocation", "_floundryTunaLocation", "_sotParcelLocation"]; +var stringProperties = ["autoLogin", "browserBookmarks", "chatFontSize", "combatHotkey0", "combatHotkey1", "combatHotkey2", "combatHotkey3", "combatHotkey4", "combatHotkey5", "combatHotkey6", "combatHotkey7", "combatHotkey8", "combatHotkey9", "commandLineNamespace", "dailyDeedsOptions", "defaultBorderColor", "displayName", "externalEditor", "getBreakfast", "headerStates", "highlightList", "http.proxyHost", "http.proxyPassword", "http.proxyPort", "http.proxyUser", "https.proxyHost", "https.proxyPassword", "https.proxyPort", "https.proxyUser", "initialDesktop", "initialFrames", "lastRelayUpdate", "lastUserAgent", "lastUsername", "logPreferenceChangeFilter", "loginScript", "loginServerName", "loginWindowLogo", "logoutScript", "pingDefaultTestPage", "pingLatest", "pingLoginAbort", "pingLoginCheck", "pingLoginFail", "pingLongest", "pingShortest", "pingTestPage", "previousNotifyList", "previousUpdateVersion", "saveState", "saveStateActive", "scriptList", "swingLookAndFeel", "userAgent", "8BitColor", "afterAdventureScript", "autoOlfact", "autoPutty", "autumnatonUpgrades", "backupCameraMode", "banishedMonsters", "banishedPhyla", "banishingShoutMonsters", "batmanStats", "batmanZone", "batmanUpgrades", "battleAction", "beachHeadsUnlocked", "beforePVPScript", "betweenBattleScript", "boomBoxSong", "breakfastAlways", "breakfastHardcore", "breakfastSoftcore", "buffBotCasting", "buyScript", "cargoPocketsEmptied", "cargoPocketScraps", "chatbotScript", "chatPlayerScript", "chibiName", "choiceAdventureScript", "chosenTrip", "clanFortuneReply1", "clanFortuneReply2", "clanFortuneReply3", "clanFortuneWord1", "clanFortuneWord2", "clanFortuneWord3", "commerceGhostItem", "counterScript", "copperheadClubHazard", "crimbotChassis", "crimbotArm", "crimbotPropulsion", "crystalBallPredictions", "csServicesPerformed", "currentAstralTrip", "currentDistillateMods", "currentEasyBountyItem", "currentHardBountyItem", "currentHippyStore", "currentJunkyardTool", "currentLlamaForm", "currentMood", "currentPVPSeason", "currentPvpVictories", "currentSpecialBountyItem", "currentSITSkill", "customCombatScript", "cyrusAdjectives", "defaultFlowerLossMessage", "defaultFlowerWinMessage", "demonName1", "demonName2", "demonName3", "demonName4", "demonName5", "demonName6", "demonName7", "demonName8", "demonName9", "demonName10", "demonName11", "demonName12", "demonName13", "dinseyGatorStenchDamage", "dinseyRollercoasterStats", "doctorBagQuestItem", "dolphinItem", "duckAreasCleared", "duckAreasSelected", "edPiece", "enamorangMonsterTurn", "ensorcelee", "EVEDirections", "extraCosmeticModifiers", "familiarScript", "forbiddenStores", "gameProBossSpecialPower", "gooseReprocessed", "grimoireSkillsHardcore", "grimoireSkillsSoftcore", "grimstoneMaskPath", "guzzlrQuestClient", "guzzlrQuestBooze", "guzzlrQuestTier", "harvestGardenHardcore", "harvestGardenSoftcore", "hpAutoRecoveryItems", "invalidBuffMessage", "jickSwordModifier", "juneCleaverQueue", "kingLiberatedScript", "lassoTraining", "lastAdventure", "lastBangPotion819", "lastBangPotion820", "lastBangPotion821", "lastBangPotion822", "lastBangPotion823", "lastBangPotion824", "lastBangPotion825", "lastBangPotion826", "lastBangPotion827", "lastChanceBurn", "lastChessboard", "lastCombatEnvironments", "lastDwarfDiceRolls", "lastDwarfDigitRunes", "lastDwarfEquipmentRunes", "lastDwarfFactoryItem118", "lastDwarfFactoryItem119", "lastDwarfFactoryItem120", "lastDwarfFactoryItem360", "lastDwarfFactoryItem361", "lastDwarfFactoryItem362", "lastDwarfFactoryItem363", "lastDwarfFactoryItem364", "lastDwarfFactoryItem365", "lastDwarfFactoryItem910", "lastDwarfFactoryItem3199", "lastDwarfOfficeItem3208", "lastDwarfOfficeItem3209", "lastDwarfOfficeItem3210", "lastDwarfOfficeItem3211", "lastDwarfOfficeItem3212", "lastDwarfOfficeItem3213", "lastDwarfOfficeItem3214", "lastDwarfOreRunes", "lastDwarfHopper1", "lastDwarfHopper2", "lastDwarfHopper3", "lastDwarfHopper4", "lastEncounter", "lastMacroError", "lastMessageId", "lastPaperStrip3144", "lastPaperStrip4138", "lastPaperStrip4139", "lastPaperStrip4140", "lastPaperStrip4141", "lastPaperStrip4142", "lastPaperStrip4143", "lastPaperStrip4144", "lastPirateEphemera", "lastPorkoBoard", "lastPorkoPayouts", "lastPorkoExpected", "lastSlimeVial3885", "lastSlimeVial3886", "lastSlimeVial3887", "lastSlimeVial3888", "lastSlimeVial3889", "lastSlimeVial3890", "lastSlimeVial3891", "lastSlimeVial3892", "lastSlimeVial3893", "lastSlimeVial3894", "lastSlimeVial3895", "lastSlimeVial3896", "latteIngredients", "latteModifier", "latteUnlocks", "ledCandleMode", "libramSkillsHardcore", "libramSkillsSoftcore", "louvreOverride", "lovePotion", "lttQuestName", "maximizerList", "maximizerMRUList", "mayoInMouth", "mayoMinderSetting", "merkinQuestPath", "mineLayout1", "mineLayout2", "mineLayout3", "mineLayout4", "mineLayout5", "mineLayout6", "mpAutoRecoveryItems", "muffinOnOrder", "nextAdventure", "nextDistillateMods", "nextQuantumFamiliarName", "nextQuantumFamiliarOwner", "nsChallenge2", "nsChallenge3", "nsChallenge4", "nsChallenge5", "nsTowerDoorKeysUsed", "oceanAction", "oceanDestination", "parkaMode", "pastaThrall1", "pastaThrall2", "pastaThrall3", "pastaThrall4", "pastaThrall5", "pastaThrall6", "pastaThrall7", "pastaThrall8", "peteMotorbikeTires", "peteMotorbikeGasTank", "peteMotorbikeHeadlight", "peteMotorbikeCowling", "peteMotorbikeMuffler", "peteMotorbikeSeat", "pieStuffing", "plantingDate", "plantingLength", "plantingScript", "plumberCostumeWorn", "pokefamBoosts", "postAscensionScript", "preAscensionScript", "questClumsinessGrove", "questDoctorBag", "questECoBucket", "questESlAudit", "questESlBacteria", "questESlCheeseburger", "questESlCocktail", "questESlDebt", "questESlFish", "questESlMushStash", "questESlSalt", "questESlSprinkles", "questESpClipper", "questESpEVE", "questESpFakeMedium", "questESpGore", "questESpJunglePun", "questESpOutOfOrder", "questESpSerum", "questESpSmokes", "questEStFishTrash", "questEStGiveMeFuel", "questEStNastyBears", "questEStSocialJusticeI", "questEStSocialJusticeII", "questEStSuperLuber", "questEStWorkWithFood", "questEStZippityDooDah", "questEUNewYou", "questF01Primordial", "questF02Hyboria", "questF03Future", "questF04Elves", "questF05Clancy", "questG01Meatcar", "questG02Whitecastle", "questG03Ego", "questG04Nemesis", "questG05Dark", "questG06Delivery", "questG07Myst", "questG08Moxie", "questG09Muscle", "questGlacierOfJerks", "questGuzzlr", "questI01Scapegoat", "questI02Beat", "questL02Larva", "questL03Rat", "questL04Bat", "questL05Goblin", "questL06Friar", "questL07Cyrptic", "questL08Trapper", "questL09Topping", "questL10Garbage", "questL11Black", "questL11Business", "questL11Curses", "questL11Desert", "questL11Doctor", "questL11MacGuffin", "questL11Manor", "questL11Palindome", "questL11Pyramid", "questL11Ron", "questL11Shen", "questL11Spare", "questL11Worship", "questL12HippyFrat", "questL12War", "questL13Final", "questL13Warehouse", "questLTTQuestByWire", "questM01Untinker", "questM02Artist", "questM03Bugbear", "questM05Toot", "questM06Gourd", "questM07Hammer", "questM08Baker", "questM09Rocks", "questM10Azazel", "questM11Postal", "questM12Pirate", "questM13Escape", "questM14Bounty", "questM15Lol", "questM16Temple", "questM17Babies", "questM18Swamp", "questM19Hippy", "questM20Necklace", "questM21Dance", "questM22Shirt", "questM23Meatsmith", "questM24Doc", "questM25Armorer", "questM26Oracle", "questMaelstromOfLovers", "questPAGhost", "questRufus", "questS01OldGuy", "questS02Monkees", "raveCombo1", "raveCombo2", "raveCombo3", "raveCombo4", "raveCombo5", "raveCombo6", "recoveryScript", "relayCounters", "retroCapeSuperhero", "retroCapeWashingInstructions", "royalty", "rufusDesiredArtifact", "rufusDesiredItems", "rufusQuestTarget", "rufusQuestType", "scriptMRUList", "seahorseName", "shadowLabyrinthGoal", "shadowRiftIngress", "shenQuestItem", "shrubGarland", "shrubGifts", "shrubLights", "shrubTopper", "sideDefeated", "sidequestArenaCompleted", "sidequestFarmCompleted", "sidequestJunkyardCompleted", "sidequestLighthouseCompleted", "sidequestNunsCompleted", "sidequestOrchardCompleted", "skateParkStatus", "snowsuit", "sourceTerminalChips", "sourceTerminalEducate1", "sourceTerminalEducate2", "sourceTerminalEnquiry", "sourceTerminalEducateKnown", "sourceTerminalEnhanceKnown", "sourceTerminalEnquiryKnown", "sourceTerminalExtrudeKnown", "spadingData", "spadingScript", "speakeasyName", "spelunkyStatus", "spelunkyUpgrades", "spookyravenRecipeUsed", "stationaryButton1", "stationaryButton2", "stationaryButton3", "stationaryButton4", "stationaryButton5", "streamCrossDefaultTarget", "sweetSynthesisBlacklist", "telescope1", "telescope2", "telescope3", "telescope4", "telescope5", "testudinalTeachings", "textColors", "thanksMessage", "tomeSkillsHardcore", "tomeSkillsSoftcore", "trackVoteMonster", "trainsetConfiguration", "trapperOre", "umbrellaState", "umdLastObtained", "vintnerWineEffect", "vintnerWineName", "vintnerWineType", "violetFogLayout", "volcanoMaze1", "volcanoMaze2", "volcanoMaze3", "volcanoMaze4", "volcanoMaze5", "walfordBucketItem", "warProgress", "watchedPreferences", "workteaClue", "yourFavoriteBird", "yourFavoriteBirdMods", "youRobotCPUUpgrades", "_bastilleBoosts", "_bastilleChoice1", "_bastilleChoice2", "_bastilleChoice3", "_bastilleCurrentStyles", "_bastilleEnemyCastle", "_bastilleEnemyName", "_bastilleLastBattleResults", "_bastilleLastEncounter", "_bastilleStats", "_beachHeadsUsed", "_beachLayout", "_beachMinutes", "_birdOfTheDay", "_birdOfTheDayMods", "_bittycar", "_campAwaySmileBuffSign", "_citizenZone", "_citizenZoneMods", "_cloudTalkMessage", "_cloudTalkSmoker", "_coatOfPaintModifier", "_dailySpecial", "_deckCardsSeen", "_feastedFamiliars", "_floristPlantsUsed", "_frAreasUnlocked", "_frHoursLeft", "_frMonstersKilled", "_horsery", "_horseryCrazyMox", "_horseryCrazyMus", "_horseryCrazyMys", "_horseryCrazyName", "_horseryCurrentName", "_horseryDarkName", "_horseryNormalName", "_horseryPaleName", "_jickJarAvailable", "_jiggleCheesedMonsters", "_lastCombatStarted", "_lastPirateRealmIsland", "_locketMonstersFought", "_mummeryMods", "_mummeryUses", "_newYouQuestSkill", "_noHatModifier", "_pantogramModifier", "_pottedPowerPlant", "_questESp", "_questPartyFair", "_questPartyFairProgress", "_questPartyFairQuest", "_roboDrinks", "_roninStoragePulls", "_spacegateAnimalLife", "_spacegateCoordinates", "_spacegateGear", "_spacegateHazards", "_spacegateIntelligentLife", "_spacegatePlanetName", "_spacegatePlantLife", "_stolenAccordions", "_tempRelayCounters", "_timeSpinnerFoodAvailable", "_unknownEasyBountyItem", "_unknownHardBountyItem", "_unknownSpecialBountyItem", "_untakenEasyBountyItem", "_untakenHardBountyItem", "_untakenSpecialBountyItem", "_userMods", "_villainLairColor", "_villainLairKey", "_voteLocal1", "_voteLocal2", "_voteLocal3", "_voteLocal4", "_voteMonster1", "_voteMonster2", "_voteModifier", "_VYKEACompanionType", "_VYKEACompanionRune", "_VYKEACompanionName"]; +var numericOrStringProperties = ["statusEngineering", "statusGalley", "statusMedbay", "statusMorgue", "statusNavigation", "statusScienceLab", "statusSonar", "statusSpecialOps", "statusWasteProcessing", "choiceAdventure2", "choiceAdventure3", "choiceAdventure4", "choiceAdventure5", "choiceAdventure6", "choiceAdventure7", "choiceAdventure8", "choiceAdventure9", "choiceAdventure10", "choiceAdventure11", "choiceAdventure12", "choiceAdventure14", "choiceAdventure15", "choiceAdventure16", "choiceAdventure17", "choiceAdventure18", "choiceAdventure19", "choiceAdventure20", "choiceAdventure21", "choiceAdventure22", "choiceAdventure23", "choiceAdventure24", "choiceAdventure25", "choiceAdventure26", "choiceAdventure27", "choiceAdventure28", "choiceAdventure29", "choiceAdventure40", "choiceAdventure41", "choiceAdventure42", "choiceAdventure45", "choiceAdventure46", "choiceAdventure47", "choiceAdventure71", "choiceAdventure72", "choiceAdventure73", "choiceAdventure74", "choiceAdventure75", "choiceAdventure76", "choiceAdventure77", "choiceAdventure86", "choiceAdventure87", "choiceAdventure88", "choiceAdventure89", "choiceAdventure90", "choiceAdventure91", "choiceAdventure105", "choiceAdventure106", "choiceAdventure107", "choiceAdventure108", "choiceAdventure109", "choiceAdventure110", "choiceAdventure111", "choiceAdventure112", "choiceAdventure113", "choiceAdventure114", "choiceAdventure115", "choiceAdventure116", "choiceAdventure117", "choiceAdventure118", "choiceAdventure120", "choiceAdventure123", "choiceAdventure125", "choiceAdventure126", "choiceAdventure127", "choiceAdventure129", "choiceAdventure131", "choiceAdventure132", "choiceAdventure135", "choiceAdventure136", "choiceAdventure137", "choiceAdventure138", "choiceAdventure139", "choiceAdventure140", "choiceAdventure141", "choiceAdventure142", "choiceAdventure143", "choiceAdventure144", "choiceAdventure145", "choiceAdventure146", "choiceAdventure147", "choiceAdventure148", "choiceAdventure149", "choiceAdventure151", "choiceAdventure152", "choiceAdventure153", "choiceAdventure154", "choiceAdventure155", "choiceAdventure156", "choiceAdventure157", "choiceAdventure158", "choiceAdventure159", "choiceAdventure160", "choiceAdventure161", "choiceAdventure162", "choiceAdventure163", "choiceAdventure164", "choiceAdventure165", "choiceAdventure166", "choiceAdventure167", "choiceAdventure168", "choiceAdventure169", "choiceAdventure170", "choiceAdventure171", "choiceAdventure172", "choiceAdventure177", "choiceAdventure178", "choiceAdventure180", "choiceAdventure181", "choiceAdventure182", "choiceAdventure184", "choiceAdventure185", "choiceAdventure186", "choiceAdventure187", "choiceAdventure188", "choiceAdventure189", "choiceAdventure191", "choiceAdventure197", "choiceAdventure198", "choiceAdventure199", "choiceAdventure200", "choiceAdventure201", "choiceAdventure202", "choiceAdventure203", "choiceAdventure204", "choiceAdventure205", "choiceAdventure206", "choiceAdventure207", "choiceAdventure208", "choiceAdventure211", "choiceAdventure212", "choiceAdventure213", "choiceAdventure214", "choiceAdventure215", "choiceAdventure216", "choiceAdventure217", "choiceAdventure218", "choiceAdventure219", "choiceAdventure220", "choiceAdventure221", "choiceAdventure222", "choiceAdventure223", "choiceAdventure224", "choiceAdventure225", "choiceAdventure230", "choiceAdventure272", "choiceAdventure273", "choiceAdventure276", "choiceAdventure277", "choiceAdventure278", "choiceAdventure279", "choiceAdventure280", "choiceAdventure281", "choiceAdventure282", "choiceAdventure283", "choiceAdventure284", "choiceAdventure285", "choiceAdventure286", "choiceAdventure287", "choiceAdventure288", "choiceAdventure289", "choiceAdventure290", "choiceAdventure291", "choiceAdventure292", "choiceAdventure293", "choiceAdventure294", "choiceAdventure295", "choiceAdventure296", "choiceAdventure297", "choiceAdventure298", "choiceAdventure299", "choiceAdventure302", "choiceAdventure303", "choiceAdventure304", "choiceAdventure305", "choiceAdventure306", "choiceAdventure307", "choiceAdventure308", "choiceAdventure309", "choiceAdventure310", "choiceAdventure311", "choiceAdventure317", "choiceAdventure318", "choiceAdventure319", "choiceAdventure320", "choiceAdventure321", "choiceAdventure322", "choiceAdventure326", "choiceAdventure327", "choiceAdventure328", "choiceAdventure329", "choiceAdventure330", "choiceAdventure331", "choiceAdventure332", "choiceAdventure333", "choiceAdventure334", "choiceAdventure335", "choiceAdventure336", "choiceAdventure337", "choiceAdventure338", "choiceAdventure339", "choiceAdventure340", "choiceAdventure341", "choiceAdventure342", "choiceAdventure343", "choiceAdventure344", "choiceAdventure345", "choiceAdventure346", "choiceAdventure347", "choiceAdventure348", "choiceAdventure349", "choiceAdventure350", "choiceAdventure351", "choiceAdventure352", "choiceAdventure353", "choiceAdventure354", "choiceAdventure355", "choiceAdventure356", "choiceAdventure357", "choiceAdventure358", "choiceAdventure360", "choiceAdventure361", "choiceAdventure362", "choiceAdventure363", "choiceAdventure364", "choiceAdventure365", "choiceAdventure366", "choiceAdventure367", "choiceAdventure372", "choiceAdventure376", "choiceAdventure387", "choiceAdventure388", "choiceAdventure389", "choiceAdventure390", "choiceAdventure391", "choiceAdventure392", "choiceAdventure393", "choiceAdventure395", "choiceAdventure396", "choiceAdventure397", "choiceAdventure398", "choiceAdventure399", "choiceAdventure400", "choiceAdventure401", "choiceAdventure402", "choiceAdventure403", "choiceAdventure423", "choiceAdventure424", "choiceAdventure425", "choiceAdventure426", "choiceAdventure427", "choiceAdventure428", "choiceAdventure429", "choiceAdventure430", "choiceAdventure431", "choiceAdventure432", "choiceAdventure433", "choiceAdventure435", "choiceAdventure438", "choiceAdventure439", "choiceAdventure442", "choiceAdventure444", "choiceAdventure445", "choiceAdventure446", "choiceAdventure447", "choiceAdventure448", "choiceAdventure449", "choiceAdventure451", "choiceAdventure452", "choiceAdventure453", "choiceAdventure454", "choiceAdventure455", "choiceAdventure456", "choiceAdventure457", "choiceAdventure458", "choiceAdventure460", "choiceAdventure461", "choiceAdventure462", "choiceAdventure463", "choiceAdventure464", "choiceAdventure465", "choiceAdventure467", "choiceAdventure468", "choiceAdventure469", "choiceAdventure470", "choiceAdventure471", "choiceAdventure472", "choiceAdventure473", "choiceAdventure474", "choiceAdventure475", "choiceAdventure477", "choiceAdventure478", "choiceAdventure480", "choiceAdventure483", "choiceAdventure484", "choiceAdventure485", "choiceAdventure486", "choiceAdventure488", "choiceAdventure489", "choiceAdventure490", "choiceAdventure491", "choiceAdventure496", "choiceAdventure497", "choiceAdventure502", "choiceAdventure503", "choiceAdventure504", "choiceAdventure505", "choiceAdventure506", "choiceAdventure507", "choiceAdventure509", "choiceAdventure510", "choiceAdventure511", "choiceAdventure512", "choiceAdventure513", "choiceAdventure514", "choiceAdventure515", "choiceAdventure517", "choiceAdventure518", "choiceAdventure519", "choiceAdventure521", "choiceAdventure522", "choiceAdventure523", "choiceAdventure527", "choiceAdventure528", "choiceAdventure529", "choiceAdventure530", "choiceAdventure531", "choiceAdventure532", "choiceAdventure533", "choiceAdventure534", "choiceAdventure535", "choiceAdventure536", "choiceAdventure538", "choiceAdventure539", "choiceAdventure542", "choiceAdventure543", "choiceAdventure544", "choiceAdventure546", "choiceAdventure548", "choiceAdventure549", "choiceAdventure550", "choiceAdventure551", "choiceAdventure552", "choiceAdventure553", "choiceAdventure554", "choiceAdventure556", "choiceAdventure557", "choiceAdventure558", "choiceAdventure559", "choiceAdventure560", "choiceAdventure561", "choiceAdventure562", "choiceAdventure563", "choiceAdventure564", "choiceAdventure565", "choiceAdventure566", "choiceAdventure567", "choiceAdventure568", "choiceAdventure569", "choiceAdventure571", "choiceAdventure572", "choiceAdventure573", "choiceAdventure574", "choiceAdventure575", "choiceAdventure576", "choiceAdventure577", "choiceAdventure578", "choiceAdventure579", "choiceAdventure581", "choiceAdventure582", "choiceAdventure583", "choiceAdventure584", "choiceAdventure594", "choiceAdventure595", "choiceAdventure596", "choiceAdventure597", "choiceAdventure598", "choiceAdventure599", "choiceAdventure600", "choiceAdventure603", "choiceAdventure604", "choiceAdventure616", "choiceAdventure634", "choiceAdventure640", "choiceAdventure654", "choiceAdventure655", "choiceAdventure656", "choiceAdventure657", "choiceAdventure658", "choiceAdventure664", "choiceAdventure669", "choiceAdventure670", "choiceAdventure671", "choiceAdventure672", "choiceAdventure673", "choiceAdventure674", "choiceAdventure675", "choiceAdventure676", "choiceAdventure677", "choiceAdventure678", "choiceAdventure679", "choiceAdventure681", "choiceAdventure683", "choiceAdventure684", "choiceAdventure685", "choiceAdventure686", "choiceAdventure687", "choiceAdventure688", "choiceAdventure689", "choiceAdventure690", "choiceAdventure691", "choiceAdventure692", "choiceAdventure693", "choiceAdventure694", "choiceAdventure695", "choiceAdventure696", "choiceAdventure697", "choiceAdventure698", "choiceAdventure700", "choiceAdventure701", "choiceAdventure705", "choiceAdventure706", "choiceAdventure707", "choiceAdventure708", "choiceAdventure709", "choiceAdventure710", "choiceAdventure711", "choiceAdventure712", "choiceAdventure713", "choiceAdventure714", "choiceAdventure715", "choiceAdventure716", "choiceAdventure717", "choiceAdventure721", "choiceAdventure725", "choiceAdventure729", "choiceAdventure733", "choiceAdventure737", "choiceAdventure741", "choiceAdventure745", "choiceAdventure749", "choiceAdventure753", "choiceAdventure771", "choiceAdventure778", "choiceAdventure780", "choiceAdventure781", "choiceAdventure783", "choiceAdventure784", "choiceAdventure785", "choiceAdventure786", "choiceAdventure787", "choiceAdventure788", "choiceAdventure789", "choiceAdventure791", "choiceAdventure793", "choiceAdventure794", "choiceAdventure795", "choiceAdventure796", "choiceAdventure797", "choiceAdventure803", "choiceAdventure805", "choiceAdventure808", "choiceAdventure809", "choiceAdventure813", "choiceAdventure815", "choiceAdventure830", "choiceAdventure832", "choiceAdventure833", "choiceAdventure834", "choiceAdventure835", "choiceAdventure837", "choiceAdventure838", "choiceAdventure839", "choiceAdventure840", "choiceAdventure841", "choiceAdventure842", "choiceAdventure851", "choiceAdventure852", "choiceAdventure853", "choiceAdventure854", "choiceAdventure855", "choiceAdventure856", "choiceAdventure857", "choiceAdventure858", "choiceAdventure866", "choiceAdventure873", "choiceAdventure875", "choiceAdventure876", "choiceAdventure877", "choiceAdventure878", "choiceAdventure879", "choiceAdventure880", "choiceAdventure881", "choiceAdventure882", "choiceAdventure888", "choiceAdventure889", "choiceAdventure918", "choiceAdventure919", "choiceAdventure920", "choiceAdventure921", "choiceAdventure923", "choiceAdventure924", "choiceAdventure925", "choiceAdventure926", "choiceAdventure927", "choiceAdventure928", "choiceAdventure929", "choiceAdventure930", "choiceAdventure931", "choiceAdventure932", "choiceAdventure940", "choiceAdventure941", "choiceAdventure942", "choiceAdventure943", "choiceAdventure944", "choiceAdventure945", "choiceAdventure946", "choiceAdventure950", "choiceAdventure955", "choiceAdventure957", "choiceAdventure958", "choiceAdventure959", "choiceAdventure960", "choiceAdventure961", "choiceAdventure962", "choiceAdventure963", "choiceAdventure964", "choiceAdventure965", "choiceAdventure966", "choiceAdventure970", "choiceAdventure973", "choiceAdventure974", "choiceAdventure975", "choiceAdventure976", "choiceAdventure977", "choiceAdventure979", "choiceAdventure980", "choiceAdventure981", "choiceAdventure982", "choiceAdventure983", "choiceAdventure988", "choiceAdventure989", "choiceAdventure993", "choiceAdventure998", "choiceAdventure1000", "choiceAdventure1003", "choiceAdventure1005", "choiceAdventure1006", "choiceAdventure1007", "choiceAdventure1008", "choiceAdventure1009", "choiceAdventure1010", "choiceAdventure1011", "choiceAdventure1012", "choiceAdventure1013", "choiceAdventure1015", "choiceAdventure1016", "choiceAdventure1017", "choiceAdventure1018", "choiceAdventure1019", "choiceAdventure1020", "choiceAdventure1021", "choiceAdventure1022", "choiceAdventure1023", "choiceAdventure1026", "choiceAdventure1027", "choiceAdventure1028", "choiceAdventure1029", "choiceAdventure1030", "choiceAdventure1031", "choiceAdventure1032", "choiceAdventure1033", "choiceAdventure1034", "choiceAdventure1035", "choiceAdventure1036", "choiceAdventure1037", "choiceAdventure1038", "choiceAdventure1039", "choiceAdventure1040", "choiceAdventure1041", "choiceAdventure1042", "choiceAdventure1044", "choiceAdventure1045", "choiceAdventure1046", "choiceAdventure1048", "choiceAdventure1051", "choiceAdventure1052", "choiceAdventure1053", "choiceAdventure1054", "choiceAdventure1055", "choiceAdventure1056", "choiceAdventure1057", "choiceAdventure1059", "choiceAdventure1060", "choiceAdventure1061", "choiceAdventure1062", "choiceAdventure1065", "choiceAdventure1067", "choiceAdventure1068", "choiceAdventure1069", "choiceAdventure1070", "choiceAdventure1071", "choiceAdventure1073", "choiceAdventure1077", "choiceAdventure1080", "choiceAdventure1081", "choiceAdventure1082", "choiceAdventure1083", "choiceAdventure1084", "choiceAdventure1085", "choiceAdventure1091", "choiceAdventure1094", "choiceAdventure1095", "choiceAdventure1096", "choiceAdventure1097", "choiceAdventure1102", "choiceAdventure1106", "choiceAdventure1107", "choiceAdventure1108", "choiceAdventure1110", "choiceAdventure1114", "choiceAdventure1115", "choiceAdventure1116", "choiceAdventure1118", "choiceAdventure1119", "choiceAdventure1120", "choiceAdventure1121", "choiceAdventure1122", "choiceAdventure1123", "choiceAdventure1171", "choiceAdventure1172", "choiceAdventure1173", "choiceAdventure1174", "choiceAdventure1175", "choiceAdventure1193", "choiceAdventure1195", "choiceAdventure1196", "choiceAdventure1197", "choiceAdventure1198", "choiceAdventure1199", "choiceAdventure1202", "choiceAdventure1203", "choiceAdventure1204", "choiceAdventure1205", "choiceAdventure1206", "choiceAdventure1207", "choiceAdventure1208", "choiceAdventure1209", "choiceAdventure1210", "choiceAdventure1211", "choiceAdventure1212", "choiceAdventure1213", "choiceAdventure1214", "choiceAdventure1215", "choiceAdventure1219", "choiceAdventure1222", "choiceAdventure1223", "choiceAdventure1224", "choiceAdventure1225", "choiceAdventure1226", "choiceAdventure1227", "choiceAdventure1228", "choiceAdventure1229", "choiceAdventure1236", "choiceAdventure1237", "choiceAdventure1238", "choiceAdventure1239", "choiceAdventure1240", "choiceAdventure1241", "choiceAdventure1242", "choiceAdventure1243", "choiceAdventure1244", "choiceAdventure1245", "choiceAdventure1246", "choiceAdventure1247", "choiceAdventure1248", "choiceAdventure1249", "choiceAdventure1250", "choiceAdventure1251", "choiceAdventure1252", "choiceAdventure1253", "choiceAdventure1254", "choiceAdventure1255", "choiceAdventure1256", "choiceAdventure1266", "choiceAdventure1280", "choiceAdventure1281", "choiceAdventure1282", "choiceAdventure1283", "choiceAdventure1284", "choiceAdventure1285", "choiceAdventure1286", "choiceAdventure1287", "choiceAdventure1288", "choiceAdventure1289", "choiceAdventure1290", "choiceAdventure1291", "choiceAdventure1292", "choiceAdventure1293", "choiceAdventure1294", "choiceAdventure1295", "choiceAdventure1296", "choiceAdventure1297", "choiceAdventure1298", "choiceAdventure1299", "choiceAdventure1300", "choiceAdventure1301", "choiceAdventure1302", "choiceAdventure1303", "choiceAdventure1304", "choiceAdventure1305", "choiceAdventure1307", "choiceAdventure1310", "choiceAdventure1312", "choiceAdventure1313", "choiceAdventure1314", "choiceAdventure1315", "choiceAdventure1316", "choiceAdventure1317", "choiceAdventure1318", "choiceAdventure1319", "choiceAdventure1321", "choiceAdventure1322", "choiceAdventure1323", "choiceAdventure1324", "choiceAdventure1325", "choiceAdventure1326", "choiceAdventure1327", "choiceAdventure1328", "choiceAdventure1332", "choiceAdventure1333", "choiceAdventure1335", "choiceAdventure1340", "choiceAdventure1341", "choiceAdventure1345", "choiceAdventure1389", "choiceAdventure1392", "choiceAdventure1397", "choiceAdventure1399", "choiceAdventure1405", "choiceAdventure1411", "choiceAdventure1415", "choiceAdventure1427", "choiceAdventure1428", "choiceAdventure1429", "choiceAdventure1430", "choiceAdventure1431", "choiceAdventure1432", "choiceAdventure1433", "choiceAdventure1434", "choiceAdventure1436", "choiceAdventure1460", "choiceAdventure1461", "choiceAdventure1467", "choiceAdventure1468", "choiceAdventure1469", "choiceAdventure1470", "choiceAdventure1471", "choiceAdventure1472", "choiceAdventure1473", "choiceAdventure1474", "choiceAdventure1475", "choiceAdventure1486", "choiceAdventure1487", "choiceAdventure1488", "choiceAdventure1489", "choiceAdventure1491", "choiceAdventure1494", "choiceAdventure1505"]; +var familiarProperties = ["commaFamiliar", "nextQuantumFamiliar", "stillsuitFamiliar"]; +var statProperties = ["nsChallenge1", "snojoSetting"]; +var phylumProperties = ["dnaSyringe", "locketPhylum", "redSnapperPhylum", "_circadianRhythmsPhylum"]; +;// CONCATENATED MODULE: ./node_modules/libram/dist/propertyTyping.js + +var booleanPropertiesSet = new Set(booleanProperties); +var numericPropertiesSet = new Set(numericProperties); +var numericOrStringPropertiesSet = new Set(numericOrStringProperties); +var stringPropertiesSet = new Set(stringProperties); +var locationPropertiesSet = new Set(locationProperties); +var monsterPropertiesSet = new Set(monsterProperties); +var familiarPropertiesSet = new Set(familiarProperties); +var statPropertiesSet = new Set(statProperties); +var phylumPropertiesSet = new Set(phylumProperties); +/** + * Determine whether a property has a boolean value + * + * @param property Property to check + * @returns Whether the supplied property has a boolean value + */ +function isBooleanProperty(property) { + return booleanPropertiesSet.has(property); +} +/** + * Determine whether a property has a numeric value + * + * @param property Property to check + * @returns Whether the supplied property has a numeric value + */ +function propertyTyping_isNumericProperty(property) { + return numericPropertiesSet.has(property); +} +/** + * Determine whether a property has a numeric or string value + * + * @param property Property to check + * @returns Whether the supplied property has a numeric or string value + */ +function isNumericOrStringProperty(property) { + return numericOrStringPropertiesSet.has(property); +} +/** + * Determine whether a property has a string value + * + * @param property Property to check + * @returns Whether the supplied property has a string value + */ +function isStringProperty(property) { + return stringPropertiesSet.has(property); +} +/** + * Determine whether a property has a Location value + * + * @param property Property to check + * @returns Whether the supplied property has a Location value + */ +function isLocationProperty(property) { + return locationPropertiesSet.has(property); +} +/** + * Determine whether a property has a Monster value + * + * @param property Property to check + * @returns Whether the supplied property has a Monster value + */ +function isMonsterProperty(property) { + return monsterPropertiesSet.has(property); +} +/** + * Determine whether a property has a Familiar value + * + * @param property Property to check + * @returns Whether the supplied property has a Familiar value + */ +function isFamiliarProperty(property) { + return familiarPropertiesSet.has(property); +} +/** + * Determine whether a property has a Stat value + * + * @param property Property to check + * @returns Whether the supplied property has a Stat value + */ +function isStatProperty(property) { + return statPropertiesSet.has(property); +} +/** + * Determine whether a property has a Phylum value + * + * @param property Property to check + * @returns Whether the supplied property has a Phylum value + */ +function isPhylumProperty(property) { + return phylumPropertiesSet.has(property); +} +;// CONCATENATED MODULE: ./node_modules/libram/dist/property.js +function property_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function property_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? property_ownKeys(Object(t), !0).forEach(function (r) { property_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : property_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function property_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function property_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, property_toPropertyKey(descriptor.key), descriptor); } } +function property_createClass(Constructor, protoProps, staticProps) { if (protoProps) property_defineProperties(Constructor.prototype, protoProps); if (staticProps) property_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function property_defineProperty(obj, key, value) { key = property_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function property_toPropertyKey(arg) { var key = property_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function property_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function property_slicedToArray(arr, i) { return property_arrayWithHoles(arr) || property_iterableToArrayLimit(arr, i) || property_unsupportedIterableToArray(arr, i) || property_nonIterableRest(); } +function property_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function property_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return property_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return property_arrayLikeToArray(o, minLen); } +function property_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function property_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function property_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + +var createPropertyGetter = transform => (property, default_) => { + var value = (0,external_kolmafia_.getProperty)(property); + if (default_ !== undefined && value === "") { + return default_; + } + return transform(value, property); +}; +var createMafiaClassPropertyGetter = (Type, toType) => createPropertyGetter(value => { + if (value === "") return null; + var v = toType(value); + return v === Type.none ? null : v; +}); +var getString = createPropertyGetter(value => value); +var getCommaSeparated = createPropertyGetter(value => value.split(/, ?/)); +var getBoolean = createPropertyGetter(value => value === "true"); +var getNumber = createPropertyGetter(value => Number(value)); +var getBounty = createMafiaClassPropertyGetter(external_kolmafia_.Bounty, external_kolmafia_.toBounty); +var getClass = createMafiaClassPropertyGetter(external_kolmafia_.Class, external_kolmafia_.toClass); +var getCoinmaster = createMafiaClassPropertyGetter(external_kolmafia_.Coinmaster, external_kolmafia_.toCoinmaster); +var getEffect = createMafiaClassPropertyGetter(external_kolmafia_.Effect, external_kolmafia_.toEffect); +var getElement = createMafiaClassPropertyGetter(external_kolmafia_.Element, external_kolmafia_.toElement); +var getFamiliar = createMafiaClassPropertyGetter(external_kolmafia_.Familiar, external_kolmafia_.toFamiliar); +var getItem = createMafiaClassPropertyGetter(external_kolmafia_.Item, external_kolmafia_.toItem); +var getLocation = createMafiaClassPropertyGetter(external_kolmafia_.Location, external_kolmafia_.toLocation); +var getMonster = createMafiaClassPropertyGetter(external_kolmafia_.Monster, external_kolmafia_.toMonster); +var getPhylum = createMafiaClassPropertyGetter(external_kolmafia_.Phylum, external_kolmafia_.toPhylum); +var getServant = createMafiaClassPropertyGetter(external_kolmafia_.Servant, external_kolmafia_.toServant); +var getSkill = createMafiaClassPropertyGetter(external_kolmafia_.Skill, external_kolmafia_.toSkill); +var getSlot = createMafiaClassPropertyGetter(external_kolmafia_.Slot, external_kolmafia_.toSlot); +var getStat = createMafiaClassPropertyGetter(external_kolmafia_.Stat, external_kolmafia_.toStat); +var getThrall = createMafiaClassPropertyGetter(external_kolmafia_.Thrall, external_kolmafia_.toThrall); +/** + * Gets the value of a mafia property, either built in or custom + * + * @param property Name of the property + * @param _default Default value for the property to take if not set + * @returns Value of the mafia property + */ +function property_get(property, _default) { + var value = getString(property); + // Handle known properties. + if (isBooleanProperty(property)) { + var _getBoolean; + return (_getBoolean = getBoolean(property, _default)) !== null && _getBoolean !== void 0 ? _getBoolean : false; + } else if (propertyTyping_isNumericProperty(property)) { + var _getNumber; + return (_getNumber = getNumber(property, _default)) !== null && _getNumber !== void 0 ? _getNumber : 0; + } else if (isNumericOrStringProperty(property)) { + return value.match(/^\d+$/) ? parseInt(value) : value; + } else if (isLocationProperty(property)) { + return getLocation(property, _default); + } else if (isMonsterProperty(property)) { + return getMonster(property, _default); + } else if (isFamiliarProperty(property)) { + return getFamiliar(property, _default); + } else if (isStatProperty(property)) { + return getStat(property, _default); + } else if (isPhylumProperty(property)) { + return getPhylum(property, _default); + } else if (isStringProperty(property)) { + return value; + } + // Not a KnownProperty from here on out. + if (_default instanceof external_kolmafia_.Location) { + return getLocation(property, _default); + } else if (_default instanceof external_kolmafia_.Monster) { + return getMonster(property, _default); + } else if (_default instanceof external_kolmafia_.Familiar) { + return getFamiliar(property, _default); + } else if (_default instanceof external_kolmafia_.Stat) { + return getStat(property, _default); + } else if (_default instanceof external_kolmafia_.Phylum) { + return getPhylum(property, _default); + } else if (typeof _default === "boolean") { + return value === "true" ? true : value === "false" ? false : _default; + } else if (typeof _default === "number") { + return value === "" ? _default : parseInt(value); + } else if (value === "") { + return _default === undefined ? "" : _default; + } else { + return value; + } +} +/** + * Sets the value of a mafia property, either built in or custom + * + * @param property Name of the property + * @param value Value to give the property + * @returns Value that was set + */ +function _set(property, value) { + var stringValue = value === null ? "" : value.toString(); + (0,external_kolmafia_.setProperty)(property, stringValue); + return value; +} +/** + * Increment a property + * + * @param property Numeric property to increment + * @param delta Number by which to increment + * @param max Maximum value to set + * @returns New value + */ + +function increment(property) { + var delta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + var max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Infinity; + var value = property_get(property); + if (!isNumericProperty(property)) return value; + var nextValue = Math.min(max, value + delta); + return _set(property, nextValue); +} +/** + * Decrement a property + * + * @param property Numeric property to decrement + * @param delta Number by which to decrement + * @param min Maximum value to set + * @returns New value + */ +function decrement(property) { + var delta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + var min = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Infinity; + var value = property_get(property); + if (!isNumericProperty(property)) return value; + var nextValue = Math.max(min, value - delta); + return _set(property, nextValue); +} +/** + * Sets the value of a set of mafia properties + * + * @param properties Set of properties + */ +function setProperties(properties) { + for (var _i = 0, _Object$entries = Object.entries(properties); _i < _Object$entries.length; _i++) { + var _Object$entries$_i = property_slicedToArray(_Object$entries[_i], 2), + prop = _Object$entries$_i[0], + value = _Object$entries$_i[1]; + _set(prop, value); + } +} +/** + * Carries out a callback during which a set of properties will be set as supplied + * + * @param properties Properties to set during callback + * @param callback Callback to execute with set properties + * @returns Return value of the supplied callback + */ +function withProperties(properties, callback) { + var propertiesBackup = Object.fromEntries(Object.entries(properties).map(_ref => { + var _ref2 = property_slicedToArray(_ref, 1), + prop = _ref2[0]; + return [prop, property_get(prop)]; + })); + setProperties(properties); + try { + return callback(); + } finally { + setProperties(propertiesBackup); + } +} +/** + * Carries out a callback during which a property will be set as supplied + * + * @param property Property to set during callback + * @param value Value to set property during callback + * @param callback Callback to execute with set properties + * @returns Return value of the supplied callback + */ +function withProperty(property, value, callback) { + return withProperties(property_defineProperty({}, property, value), callback); +} +/** + * Carries out a callback during which a set of choices will be handled as supplied + * + * @param choices Choices to set during callback + * @param callback Callback to execute with set choices + * @returns Return value of the supplied callback + */ +function withChoices(choices, callback) { + var properties = Object.fromEntries(Object.entries(choices).map(_ref3 => { + var _ref4 = property_slicedToArray(_ref3, 2), + choice = _ref4[0], + option = _ref4[1]; + return ["choiceAdventure".concat(choice), option]; + })); + return withProperties(properties, callback); +} +/** + * Carries out a callback during which a choice will be handled as supplied + * + * @param choice Choice to set during callback + * @param value How to handle choice during callback + * @param callback Callback to execute with set properties + * @returns Return value of the supplied callback + */ +function withChoice(choice, value, callback) { + return withChoices(property_defineProperty({}, choice, value), callback); +} +var PropertiesManager = /*#__PURE__*/function () { + function PropertiesManager() { + property_classCallCheck(this, PropertiesManager); + property_defineProperty(this, "properties", {}); + } + property_createClass(PropertiesManager, [{ + key: "storedValues", + get: function get() { + return this.properties; + } + /** + * Sets a collection of properties to the given values, storing the old values. + * + * @param propertiesToSet A Properties object, keyed by property name. + */ + }, { + key: "set", + value: function set(propertiesToSet) { + for (var _i2 = 0, _Object$entries2 = Object.entries(propertiesToSet); _i2 < _Object$entries2.length; _i2++) { + var _Object$entries2$_i = property_slicedToArray(_Object$entries2[_i2], 2), + propertyName = _Object$entries2$_i[0], + propertyValue = _Object$entries2$_i[1]; + if (this.properties[propertyName] === undefined) { + this.properties[propertyName] = property_get(propertyName); + } + _set(propertyName, propertyValue); + } + } + /** + * Sets a collection of choice adventure properties to the given values, storing the old values. + * + * @param choicesToSet An object keyed by choice adventure number. + */ + }, { + key: "setChoices", + value: function setChoices(choicesToSet) { + this.set(Object.fromEntries(Object.entries(choicesToSet).map(_ref5 => { + var _ref6 = property_slicedToArray(_ref5, 2), + choiceNumber = _ref6[0], + choiceValue = _ref6[1]; + return ["choiceAdventure".concat(choiceNumber), choiceValue]; + }))); + } + /** + * Sets a single choice adventure property to the given value, storing the old value. + * + * @param choiceToSet The number of the choice adventure to set the property for. + * @param value The value to assign to that choice adventure. + */ + }, { + key: "setChoice", + value: function setChoice(choiceToSet, value) { + this.setChoices(property_defineProperty({}, choiceToSet, value)); + } + /** + * Resets the given properties to their original stored value. Does not delete entries from the manager. + * + * @param properties Collection of properties to reset. + */ + }, { + key: "reset", + value: function reset() { + for (var _len = arguments.length, properties = new Array(_len), _key = 0; _key < _len; _key++) { + properties[_key] = arguments[_key]; + } + for (var _i3 = 0, _properties = properties; _i3 < _properties.length; _i3++) { + var property = _properties[_i3]; + var value = this.properties[property]; + if (value) { + _set(property, value); + } + } + } + /** + * Iterates over all stored values, setting each property back to its original stored value. Does not delete entries from the manager. + */ + }, { + key: "resetAll", + value: function resetAll() { + setProperties(this.properties); + } + /** + * Stops storing the original values of inputted properties. + * + * @param properties Properties for the manager to forget. + */ + }, { + key: "clear", + value: function clear() { + for (var _len2 = arguments.length, properties = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + properties[_key2] = arguments[_key2]; + } + for (var _i4 = 0, _properties2 = properties; _i4 < _properties2.length; _i4++) { + var property = _properties2[_i4]; + if (this.properties[property]) { + delete this.properties[property]; + } + } + } + /** + * Clears all properties. + */ + }, { + key: "clearAll", + value: function clearAll() { + this.properties = {}; + } + /** + * Increases a numeric property to the given value if necessary. + * + * @param property The numeric property we want to potentially raise. + * @param value The minimum value we want that property to have. + * @returns Whether we needed to change the property. + */ + }, { + key: "setMinimumValue", + value: function setMinimumValue(property, value) { + if (property_get(property, 0) < value) { + this.set(property_defineProperty({}, property, value)); + return true; + } + return false; + } + /** + * Decrease a numeric property to the given value if necessary. + * + * @param property The numeric property we want to potentially lower. + * @param value The maximum value we want that property to have. + * @returns Whether we needed to change the property. + */ + }, { + key: "setMaximumValue", + value: function setMaximumValue(property, value) { + if (property_get(property, 0) > value) { + this.set(property_defineProperty({}, property, value)); + return true; + } + return false; + } + /** + * Creates a new PropertiesManager with identical stored values to this one. + * + * @returns A new PropertiesManager, with identical stored values to this one. + */ + }, { + key: "clone", + value: function clone() { + var newGuy = new PropertiesManager(); + newGuy.properties = this.storedValues; + return newGuy; + } + /** + * Clamps a numeric property, modulating it up or down to fit within a specified range + * + * @param property The numeric property to clamp + * @param min The lower bound for what we want the property to be allowed to be. + * @param max The upper bound for what we want the property to be allowed to be. + * @returns Whether we ended up changing the property or not. + */ + }, { + key: "clamp", + value: function clamp(property, min, max) { + if (max < min) return false; + var start = property_get(property); + this.setMinimumValue(property, min); + this.setMaximumValue(property, max); + return start !== property_get(property); + } + /** + * Determines whether this PropertiesManager has identical stored values to another. + * + * @param other The PropertiesManager to compare to this one. + * @returns Whether their StoredValues are identical. + */ + }, { + key: "equals", + value: function equals(other) { + var thisProps = Object.entries(this.storedValues); + var otherProps = new Map(Object.entries(other.storedValues)); + if (thisProps.length !== otherProps.size) return false; + for (var _i5 = 0, _thisProps = thisProps; _i5 < _thisProps.length; _i5++) { + var _thisProps$_i = property_slicedToArray(_thisProps[_i5], 2), + propertyName = _thisProps$_i[0], + propertyValue = _thisProps$_i[1]; + if (otherProps.get(propertyName) === propertyValue) return false; + } + return true; + } + /** + * Merges a PropertiesManager onto this one, letting the input win in the event that both PropertiesManagers have a value stored. + * + * @param other The PropertiesManager to be merged onto this one. + * @returns A new PropertiesManager with stored values from both its parents. + */ + }, { + key: "merge", + value: function merge(other) { + var newGuy = new PropertiesManager(); + newGuy.properties = property_objectSpread(property_objectSpread({}, this.properties), other.properties); + return newGuy; + } + /** + * Merges an arbitrary collection of PropertiesManagers, letting the rightmost PropertiesManager win in the event of verlap. + * + * @param mergees The PropertiesManagers to merge together. + * @returns A PropertiesManager that is just an amalgam of all the constituents. + */ + }], [{ + key: "merge", + value: function merge() { + for (var _len3 = arguments.length, mergees = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + mergees[_key3] = arguments[_key3]; + } + if (mergees.length === 0) return new PropertiesManager(); + return mergees.reduce((a, b) => a.merge(b)); + } + }]); + return PropertiesManager; +}(); +;// CONCATENATED MODULE: ./node_modules/libram/dist/combat.js +function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } +function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = combat_getPrototypeOf(object); if (object === null) break; } return object; } +function combat_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = combat_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function combat_toConsumableArray(arr) { return combat_arrayWithoutHoles(arr) || combat_iterableToArray(arr) || combat_unsupportedIterableToArray(arr) || combat_nonIterableSpread(); } +function combat_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function combat_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return combat_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return combat_arrayLikeToArray(o, minLen); } +function combat_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function combat_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return combat_arrayLikeToArray(arr); } +function combat_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function combat_defineProperty(obj, key, value) { key = combat_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function combat_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, combat_toPropertyKey(descriptor.key), descriptor); } } +function combat_createClass(Constructor, protoProps, staticProps) { if (protoProps) combat_defineProperties(Constructor.prototype, protoProps); if (staticProps) combat_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function combat_toPropertyKey(arg) { var key = combat_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function combat_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function combat_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function combat_inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) combat_setPrototypeOf(subClass, superClass); } +function combat_createSuper(Derived) { var hasNativeReflectConstruct = combat_isNativeReflectConstruct(); return function _createSuperInternal() { var Super = combat_getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = combat_getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return combat_possibleConstructorReturn(this, result); }; } +function combat_possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return combat_assertThisInitialized(self); } +function combat_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function combat_wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; combat_wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !combat_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return combat_construct(Class, arguments, combat_getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return combat_setPrototypeOf(Wrapper, Class); }; return combat_wrapNativeSuper(Class); } +function combat_construct(Parent, args, Class) { if (combat_isNativeReflectConstruct()) { combat_construct = Reflect.construct.bind(); } else { combat_construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) combat_setPrototypeOf(instance, Class.prototype); return instance; }; } return combat_construct.apply(null, arguments); } +function combat_isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function combat_isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } +function combat_setPrototypeOf(o, p) { combat_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return combat_setPrototypeOf(o, p); } +function combat_getPrototypeOf(o) { combat_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return combat_getPrototypeOf(o); } + + + + +var MACRO_NAME = "Script Autoattack Macro"; +/** + * Get the KoL native ID of the macro with name name. + * + * @param name Name of the macro + * @category Combat + * @returns {number} The macro ID. + */ +function getMacroId() { + var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : MACRO_NAME; + var macroMatches = (0,external_kolmafia_.xpath)((0,external_kolmafia_.visitUrl)("account_combatmacros.php"), "//select[@name=\"macroid\"]/option[text()=\"".concat(name, "\"]/@value")); + if (macroMatches.length === 0) { + (0,external_kolmafia_.visitUrl)("account_combatmacros.php?action=new"); + var newMacroText = (0,external_kolmafia_.visitUrl)("account_combatmacros.php?macroid=0&name=".concat(name, "¯otext=abort&action=save")); + return parseInt((0,external_kolmafia_.xpath)(newMacroText, "//input[@name=".concat(name, "]/@value"))[0], 10); + } else { + return parseInt(macroMatches[0], 10); + } +} +/** + * Converts an item name to a Item, or passes through an existing instance of Item + * + * @param itemOrName Item name or Item instance + * @returns KoLmafia Item instance + */ +function itemOrNameToItem(itemOrName) { + return typeof itemOrName === "string" ? external_kolmafia_.Item.get(itemOrName) : itemOrName; +} +/** + * Create a string of the item or items provided that is compatible with BALLS syntax and is non-ambiguous + * + * @param itemOrItems Item name, item instance, or 2-tuple of item name or item instance + * @returns BALLS macro-compatible value for item or items provided + */ +function itemOrItemsBallsMacroName(itemOrItems) { + if (Array.isArray(itemOrItems)) { + return itemOrItems.map(itemOrItemsBallsMacroName).join(", "); + } else { + var item = itemOrNameToItem(itemOrItems); + return !overlappingItemNames.includes(item.name) ? item.name : item.id.toFixed(0); + } +} +/** + * Generate a BALLS macro condition to check wither the player has either a single or a 2-tuple of combat items + * + * @param itemOrItems Single or 2-tuple of combat items + * @returns BALLS macro condition + */ +function itemOrItemsBallsMacroPredicate(itemOrItems) { + if (Array.isArray(itemOrItems)) { + return itemOrItems.map(itemOrItemsBallsMacroPredicate).join(" && "); + } else { + return "hascombatitem ".concat(itemOrItems); + } +} +/** + * Converts a skill name to a Skill, or passes through an existing instance of Skill + * + * @param skillOrName Skill name or Skill instance + * @returns KoLmafia Skill instance + */ +function skillOrNameToSkill(skillOrName) { + if (typeof skillOrName === "string") { + return external_kolmafia_.Skill.get(skillOrName); + } else { + return skillOrName; + } +} +/** + * Get a skill name in a form that is appropriate for BALLS macros + * + * @param skillOrName Skill name or Skill instance + * @returns BALLS macro-suitable skill name + */ +function skillBallsMacroName(skillOrName) { + var skill = skillOrNameToSkill(skillOrName); + return skill.name.match(/^[A-Za-z ]+$/) && !overlappingSkillNames.includes(skill.name) ? skill.name : skill.id; +} +var InvalidMacroError = /*#__PURE__*/function (_Error) { + combat_inherits(InvalidMacroError, _Error); + var _super = combat_createSuper(InvalidMacroError); + function InvalidMacroError() { + combat_classCallCheck(this, InvalidMacroError); + return _super.apply(this, arguments); + } + return combat_createClass(InvalidMacroError); +}( /*#__PURE__*/combat_wrapNativeSuper(Error)); +/** + * BALLS macro builder for direct submission to KoL. + * Create a new macro with `new Macro()` and add steps using the instance methods. + * Uses a fluent interface, so each step returns the object for easy chaining of steps. + * Each method is also defined as a static method that creates a new Macro with only that step. + * For example, you can do `Macro.skill('Saucestorm').attack()`. + */ +var Macro = /*#__PURE__*/function () { + function Macro() { + combat_classCallCheck(this, Macro); + combat_defineProperty(this, "components", []); + combat_defineProperty(this, "name", MACRO_NAME); + } + combat_createClass(Macro, [{ + key: "toString", + value: + /** + * Convert macro to string. + * + * @returns BALLS macro + */ + function toString() { + return (this.components.join(";") + ";").replace(/;;+/g, ";"); + } + /** + * Gives your macro a new name to be used when saving an autoattack. + * + * @param name The name to be used when saving as an autoattack. + * @returns The macro in question + */ + }, { + key: "rename", + value: function rename(name) { + this.name = name; + return this; + } + /** + * Creates a new Macro with a name other than the default name. + * + * @param name The name to assign this macro. + * @returns A new Macro with the assigned name. + */ + }, { + key: "save", + value: + /** + * Save a macro to a Mafia property for use in a consult script. + */ + function save() { + _set(Macro.SAVED_MACRO_PROPERTY, this.toString()); + } + /** + * Load a saved macro from the Mafia property. + * + * @returns Loaded macro text + */ + }, { + key: "step", + value: + /** + * Statefully add one or several steps to a macro. + * + * @param nextSteps The steps to add to the macro. + * @returns {Macro} This object itself. + */ + function step() { + var _ref, _this$components; + for (var _len = arguments.length, nextSteps = new Array(_len), _key = 0; _key < _len; _key++) { + nextSteps[_key] = arguments[_key]; + } + var nextStepsStrings = (_ref = []).concat.apply(_ref, combat_toConsumableArray(nextSteps.map(x => x instanceof Macro ? x.components : [x]))); + (_this$components = this.components).push.apply(_this$components, combat_toConsumableArray(nextStepsStrings.filter(s => s.length > 0))); + return this; + } + /** + * Statefully add one or several steps to a macro. + * + * @param nextSteps The steps to add to the macro. + * @returns {Macro} This object itself. + */ + }, { + key: "submit", + value: + /** + * Submit the built macro to KoL. Only works inside combat. + * + * @returns Contents of the fight page after macro submission + */ + function submit() { + var final = this.toString(); + return (0,external_kolmafia_.visitUrl)("fight.php?action=macro¯otext=".concat((0,external_kolmafia_.urlEncode)(final)), true, true); + } + /** + * Set this macro as a KoL native autoattack. + */ + }, { + key: "setAutoAttack", + value: function setAutoAttack() { + var id = Macro.cachedMacroIds.get(this.name); + if (id === undefined) { + id = getMacroId(this.name); + Macro.cachedMacroIds.set(this.name, id); + } + if ((0,external_kolmafia_.getAutoAttack)() === 99000000 + id && this.toString() === Macro.cachedAutoAttacks.get(this.name)) { + // This macro is already set. Don"t make the server request. + return; + } + (0,external_kolmafia_.visitUrl)("account_combatmacros.php?macroid=".concat(id, "&name=").concat((0,external_kolmafia_.urlEncode)(this.name), "¯otext=").concat((0,external_kolmafia_.urlEncode)(this.toString()), "&action=save"), true, true); + (0,external_kolmafia_.visitUrl)("account.php?am=1&action=autoattack&value=".concat(99000000 + id, "&ajax=1")); + Macro.cachedAutoAttacks.set(this.name, this.toString()); + } + /** + * Renames the macro, then sets it as an autoattack. + * + * @param name The name to save the macro under as an autoattack. + */ + }, { + key: "setAutoAttackAs", + value: function setAutoAttackAs(name) { + this.name = name; + this.setAutoAttack(); + } + /** + * Clear all cached autoattacks, and delete all stored macros server-side. + */ + }, { + key: "abort", + value: + /** + * Add an "abort" step to this macro. + * + * @returns {Macro} This object itself. + */ + function abort() { + return this.step("abort"); + } + /** + * Create a new macro with an "abort" step. + * + * @returns {Macro} This object itself. + */ + }, { + key: "abortWithWarning", + value: + /** + * Adds an "abort" step to this macro, with a warning message to print + * + * @param warning The warning message to print + * @returns {Macro} This object itself. + */ + function abortWithWarning(warning) { + return this.step("abort \"".concat(warning, "\"")); + } + /** + * Create a new macro with an "abort" step to this macro, with a warning message to print + * + * @param warning The warning message to print + * @returns {Macro} This object itself. + */ + }, { + key: "runaway", + value: + /** + * Add a "runaway" step to this macro. + * + * @returns {Macro} This object itself. + */ + function runaway() { + return this.step("runaway"); + } + /** + * Create a new macro with an "runaway" step. + * + * @returns {Macro} This object itself. + */ + }, { + key: "if_", + value: + /** + * Add an "if" statement to this macro. + * + * @param condition The BALLS condition for the if statement. + * @param ifTrue Continuation if the condition is true. + * @returns {Macro} This object itself. + */ + function if_(condition, ifTrue) { + return this.step("if ".concat(Macro.makeBALLSPredicate(condition))).step(ifTrue).step("endif"); + } + /** + * Create a new macro with an "if" statement. + * + * @param condition The BALLS condition for the if statement. + * @param ifTrue Continuation if the condition is true. + * @returns {Macro} This object itself. + */ + }, { + key: "ifNot", + value: + /** + * Add an "if" statement to this macro, inverting the condition. + * + * @param condition The BALLS condition for the if statement. + * @param ifTrue Continuation if the condition is true. + * @returns {Macro} This object itself. + */ + function ifNot(condition, ifTrue) { + return this.step("if !(".concat(Macro.makeBALLSPredicate(condition), ")")).step(ifTrue).step("endif"); + } + /** + * Create a new macro with an "if" statement, inverting the condition. + * + * @param condition The BALLS condition for the if statement. + * @param ifTrue Continuation if the condition is true. + * @returns {Macro} This object itself. + */ + }, { + key: "while_", + value: + /** + * Add a "while" statement to this macro. + * + * @param condition The BALLS condition for the if statement. + * @param contents Loop to repeat while the condition is true. + * @returns {Macro} This object itself. + */ + function while_(condition, contents) { + return this.step("while ".concat(condition)).step(contents).step("endwhile"); + } + /** + * Create a new macro with a "while" statement. + * + * @param condition The BALLS condition for the if statement. + * @param contents Loop to repeat while the condition is true. + * @returns {Macro} This object itself. + */ + }, { + key: "externalIf", + value: + /** + * Conditionally add a step to a macro based on a condition evaluated at the time of building the macro. + * + * @param condition The JS condition. + * @param ifTrue Continuation to add if the condition is true. + * @param ifFalse Optional input to turn this into an if...else statement. + * @returns {Macro} This object itself. + */ + function externalIf(condition, ifTrue, ifFalse) { + if (condition) return this.step(ifTrue);else if (ifFalse) return this.step(ifFalse);else return this; + } + /** + * Create a new macro with a condition evaluated at the time of building the macro. + * + * @param condition The JS condition. + * @param ifTrue Continuation to add if the condition is true. + * @param ifFalse Optional input to turn this into an if...else statement. + * @returns {Macro} This object itself. + */ + }, { + key: "repeat", + value: + /** + * Add a repeat step to the macro. + * + * @returns {Macro} This object itself. + */ + function repeat() { + return this.step("repeat"); + } + /** + * Add one or more skill cast steps to the macro. + * + * @param skills Skills to cast. + * @returns {Macro} This object itself. + */ + }, { + key: "skill", + value: function skill() { + for (var _len2 = arguments.length, skills = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + skills[_key2] = arguments[_key2]; + } + return this.step.apply(this, combat_toConsumableArray(skills.map(skill => { + return "skill ".concat(skillBallsMacroName(skill)); + }))); + } + /** + * Create a new macro with one or more skill cast steps. + * + * @param skills Skills to cast. + * @returns {Macro} This object itself. + */ + }, { + key: "trySkill", + value: + /** + * Add one or more skill cast steps to the macro, where each step checks if you have the skill first. + * + * @param skills Skills to try casting. + * @returns {Macro} This object itself. + */ + function trySkill() { + for (var _len3 = arguments.length, skills = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + skills[_key3] = arguments[_key3]; + } + return this.step.apply(this, combat_toConsumableArray(skills.map(skill => { + return Macro.if_("hasskill ".concat(skillBallsMacroName(skill)), Macro.skill(skill)); + }))); + } + /** + * Create a new macro with one or more skill cast steps, where each step checks if you have the skill first. + * + * @param skills Skills to try casting. + * @returns {Macro} This object itself. + */ + }, { + key: "trySkillRepeat", + value: + /** + * Add one or more skill-cast-and-repeat steps to the macro, where each step checks if you have the skill first. + * + * @param skills Skills to try repeatedly casting. + * @returns {Macro} This object itself. + */ + function trySkillRepeat() { + for (var _len4 = arguments.length, skills = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + skills[_key4] = arguments[_key4]; + } + return this.step.apply(this, combat_toConsumableArray(skills.map(skill => { + return Macro.if_("hasskill ".concat(skillBallsMacroName(skill)), Macro.skill(skill).repeat()); + }))); + } + /** + * Create a new macro with one or more skill-cast-and-repeat steps, where each step checks if you have the skill first. + * + * @param skills Skills to try repeatedly casting. + * @returns {Macro} This object itself. + */ + }, { + key: "item", + value: + /** + * Add one or more item steps to the macro. + * + * @param items Items to use. Pass a tuple [item1, item2] to funksling. + * @returns {Macro} This object itself. + */ + function item() { + for (var _len5 = arguments.length, items = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + items[_key5] = arguments[_key5]; + } + return this.step.apply(this, combat_toConsumableArray(items.map(itemOrItems => { + return "use ".concat(itemOrItemsBallsMacroName(itemOrItems)); + }))); + } + /** + * Create a new macro with one or more item steps. + * + * @param items Items to use. Pass a tuple [item1, item2] to funksling. + * @returns {Macro} This object itself. + */ + }, { + key: "tryItem", + value: + /** + * Add one or more item steps to the macro, where each step checks to see if you have the item first. + * + * @param items Items to try using. Pass a tuple [item1, item2] to funksling. + * @returns {Macro} This object itself. + */ + function tryItem() { + for (var _len6 = arguments.length, items = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { + items[_key6] = arguments[_key6]; + } + return this.step.apply(this, combat_toConsumableArray(items.map(item => { + return Macro.if_(itemOrItemsBallsMacroPredicate(item), "use ".concat(itemOrItemsBallsMacroName(item))); + }))); + } + /** + * Create a new macro with one or more item steps, where each step checks to see if you have the item first. + * + * @param items Items to try using. Pass a tuple [item1, item2] to funksling. + * @returns {Macro} This object itself. + */ + }, { + key: "attack", + value: + /** + * Add an attack step to the macro. + * + * @returns {Macro} This object itself. + */ + function attack() { + return this.step("attack"); + } + /** + * Create a new macro with an attack step. + * + * @returns {Macro} This object itself. + */ + }, { + key: "ifHolidayWanderer", + value: + /** + * Create an if_ statement based on what holiday of loathing it currently is. On non-holidays, returns the original macro, unmutated. + * + * @param macro The macro to place in the if_ statement + * @returns This macro with supplied macro wapped in if statement matching holiday wanderers + */ + function ifHolidayWanderer(macro) { + var todaysWanderers = getTodaysHolidayWanderers(); + if (todaysWanderers.length === 0) return this; + return this.if_(todaysWanderers.map(monster => "monsterid ".concat(monster.id)).join(" || "), macro); + } + /** + * Create a new macro starting with an ifHolidayWanderer step. + * + * @param macro The macro to place inside the if_ statement + * @returns New macro with supplied macro wrapped in if statement matching holiday wanderers + */ + }, { + key: "ifNotHolidayWanderer", + value: + /** + * Create an if_ statement based on what holiday of loathing it currently is. On non-holidays, returns the original macro, with the input macro appended. + * + * @param macro The macro to place in the if_ statement. + * @returns This macro with supplied macro wrapped in if statement matching monsters that are not holiday wanderers + */ + function ifNotHolidayWanderer(macro) { + var todaysWanderers = getTodaysHolidayWanderers(); + if (todaysWanderers.length === 0) return this.step(macro); + return this.if_(todaysWanderers.map(monster => "!monsterid ".concat(monster.id)).join(" && "), macro); + } + /** + * Create a new macro starting with an ifNotHolidayWanderer step. + * + * @param macro The macro to place inside the if_ statement + * @returns New macro with supplied macro wrapped in if statement matching monsters that are not holiday wanderers + */ + }], [{ + key: "rename", + value: function rename(name) { + return new this().rename(name); + } + }, { + key: "load", + value: function load() { + var _this; + return (_this = new this()).step.apply(_this, combat_toConsumableArray(property_get(Macro.SAVED_MACRO_PROPERTY).split(";"))); + } + /** + * Clear the saved macro in the Mafia property. + */ + }, { + key: "clearSaved", + value: function clearSaved() { + (0,external_kolmafia_.removeProperty)(Macro.SAVED_MACRO_PROPERTY); + } + }, { + key: "step", + value: function step() { + var _this2; + return (_this2 = new this()).step.apply(_this2, arguments); + } + }, { + key: "clearAutoAttackMacros", + value: function clearAutoAttackMacros() { + var _iterator = combat_createForOfIteratorHelper(Macro.cachedAutoAttacks.keys()), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var _Macro$cachedMacroIds; + var name = _step.value; + var id = (_Macro$cachedMacroIds = Macro.cachedMacroIds.get(name)) !== null && _Macro$cachedMacroIds !== void 0 ? _Macro$cachedMacroIds : getMacroId(name); + (0,external_kolmafia_.visitUrl)("account_combatmacros.php?macroid=".concat(id, "&action=edit&what=Delete&confirm=1")); + Macro.cachedAutoAttacks.delete(name); + Macro.cachedMacroIds.delete(name); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } + }, { + key: "abort", + value: function abort() { + return new this().abort(); + } + }, { + key: "abortWithWarning", + value: function abortWithWarning(warning) { + return new this().abortWithWarning(warning); + } + }, { + key: "runaway", + value: function runaway() { + return new this().runaway(); + } + }, { + key: "makeBALLSPredicate", + value: function makeBALLSPredicate(condition) { + var ballsCondition = ""; + if (condition instanceof external_kolmafia_.Monster) { + ballsCondition = "monsterid ".concat(condition.id); + } else if (condition instanceof Array) { + ballsCondition = condition.map(mon => "monsterid ".concat(mon.id)).join(" || "); + ballsCondition = "(".concat(ballsCondition, ")"); + } else if (condition instanceof external_kolmafia_.Effect) { + ballsCondition = "haseffect ".concat(condition.id); + } else if (condition instanceof external_kolmafia_.Skill) { + ballsCondition = "hasskill ".concat(skillBallsMacroName(condition)); + } else if (condition instanceof external_kolmafia_.Item) { + if (!condition.combat) { + throw new InvalidMacroError("Item ".concat(condition, " cannot be made a valid BALLS predicate (it is not combat-usable)")); + } + ballsCondition = "hascombatitem ".concat(itemOrItemsBallsMacroName(condition)); + } else if (condition instanceof external_kolmafia_.Location) { + var snarfblat = condition.id; + if (snarfblat < 1) { + throw new InvalidMacroError("Location ".concat(condition, " cannot be made a valid BALLS predicate (it has no location id)")); + } + ballsCondition = "snarfblat ".concat(snarfblat); + } else if (condition instanceof external_kolmafia_.Class) { + if (condition.id > 6) { + throw new InvalidMacroError("Class ".concat(condition, " cannot be made a valid BALLS predicate (it is not a standard class)")); + } + ballsCondition = condition.toString().replaceAll(" ", "").toLowerCase(); + } else if (condition instanceof external_kolmafia_.Stat) { + ballsCondition = "".concat(condition.toString().toLowerCase(), "class"); + } else { + ballsCondition = condition; + } + return ballsCondition; + } + }, { + key: "if_", + value: function if_(condition, ifTrue) { + return new this().if_(condition, ifTrue); + } + }, { + key: "ifNot", + value: function ifNot(condition, ifTrue) { + return new this().ifNot(condition, ifTrue); + } + }, { + key: "while_", + value: function while_(condition, contents) { + return new this().while_(condition, contents); + } + }, { + key: "externalIf", + value: function externalIf(condition, ifTrue, ifFalse) { + return new this().externalIf(condition, ifTrue, ifFalse); + } + }, { + key: "skill", + value: function skill() { + var _this3; + return (_this3 = new this()).skill.apply(_this3, arguments); + } + }, { + key: "trySkill", + value: function trySkill() { + var _this4; + return (_this4 = new this()).trySkill.apply(_this4, arguments); + } + }, { + key: "trySkillRepeat", + value: function trySkillRepeat() { + var _this5; + return (_this5 = new this()).trySkillRepeat.apply(_this5, arguments); + } + }, { + key: "item", + value: function item() { + var _this6; + return (_this6 = new this()).item.apply(_this6, arguments); + } + }, { + key: "tryItem", + value: function tryItem() { + var _this7; + return (_this7 = new this()).tryItem.apply(_this7, arguments); + } + }, { + key: "attack", + value: function attack() { + return new this().attack(); + } + }, { + key: "ifHolidayWanderer", + value: function ifHolidayWanderer(macro) { + return new this().ifHolidayWanderer(macro); + } + }, { + key: "ifNotHolidayWanderer", + value: function ifNotHolidayWanderer(macro) { + return new this().ifNotHolidayWanderer(macro); + } + }]); + return Macro; +}(); +/** + * Adventure in a location and handle all combats with a given macro. + * To use this function you will need to create a consult script that runs Macro.load().submit() and a CCS that calls that consult script. + * See examples/consult.ts for an example. + * + * @category Combat + * @param loc Location to adventure in. + * @param macro Macro to execute. + */ +combat_defineProperty(Macro, "SAVED_MACRO_PROPERTY", "libram_savedMacro"); +combat_defineProperty(Macro, "cachedMacroIds", new Map()); +combat_defineProperty(Macro, "cachedAutoAttacks", new Map()); +function adventureMacro(loc, macro) { + macro.save(); + setAutoAttack(0); + try { + adv1(loc, 0, ""); + while (inMultiFight()) runCombat(); + if (choiceFollowsFight()) visitUrl("choice.php"); + } finally { + Macro.clearSaved(); + } +} +/** + * Adventure in a location and handle all combats with a given autoattack and manual macro. + * To use the nextMacro parameter you will need to create a consult script that runs Macro.load().submit() and a CCS that calls that consult script. + * See examples/consult.ts for an example. + * + * @category Combat + * @param loc Location to adventure in. + * @param autoMacro Macro to execute via KoL autoattack. + * @param nextMacro Macro to execute manually after autoattack completes. + */ +function adventureMacroAuto(loc, autoMacro) { + var _nextMacro; + var nextMacro = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; + nextMacro = (_nextMacro = nextMacro) !== null && _nextMacro !== void 0 ? _nextMacro : Macro.abort(); + autoMacro.setAutoAttack(); + nextMacro.save(); + try { + adv1(loc, 0, ""); + while (inMultiFight()) runCombat(); + if (choiceFollowsFight()) visitUrl("choice.php"); + } finally { + Macro.clearSaved(); + } +} +var StrictMacro = /*#__PURE__*/(/* unused pure expression or super */ null && (function (_Macro) { + combat_inherits(StrictMacro, _Macro); + var _super2 = combat_createSuper(StrictMacro); + function StrictMacro() { + combat_classCallCheck(this, StrictMacro); + return _super2.apply(this, arguments); + } + combat_createClass(StrictMacro, [{ + key: "skill", + value: + /** + * Add one or more skill cast steps to the macro. + * + * @param skills Skills to cast. + * @returns {StrictMacro} This object itself. + */ + function skill() { + var _get2; + for (var _len7 = arguments.length, skills = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + skills[_key7] = arguments[_key7]; + } + return (_get2 = _get(combat_getPrototypeOf(StrictMacro.prototype), "skill", this)).call.apply(_get2, [this].concat(skills)); + } + /** + * Create a new macro with one or more skill cast steps. + * + * @param skills Skills to cast. + * @returns {StrictMacro} This object itself. + */ + }, { + key: "item", + value: + /** + * Add one or more item steps to the macro. + * + * @param items Items to use. Pass a tuple [item1, item2] to funksling. + * @returns {StrictMacro} This object itself. + */ + function item() { + var _get3; + for (var _len8 = arguments.length, items = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { + items[_key8] = arguments[_key8]; + } + return (_get3 = _get(combat_getPrototypeOf(StrictMacro.prototype), "item", this)).call.apply(_get3, [this].concat(items)); + } + /** + * Create a new macro with one or more item steps. + * + * @param items Items to use. Pass a tuple [item1, item2] to funksling. + * @returns {StrictMacro} This object itself. + */ + }, { + key: "trySkill", + value: + /** + * Add one or more skill cast steps to the macro, where each step checks if you have the skill first. + * + * @param skills Skills to try casting. + * @returns {StrictMacro} This object itself. + */ + function trySkill() { + var _get4; + for (var _len9 = arguments.length, skills = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) { + skills[_key9] = arguments[_key9]; + } + return (_get4 = _get(combat_getPrototypeOf(StrictMacro.prototype), "trySkill", this)).call.apply(_get4, [this].concat(skills)); + } + /** + * Create a new macro with one or more skill cast steps, where each step checks if you have the skill first. + * + * @param skills Skills to try casting. + * @returns {StrictMacro} This object itself. + */ + }, { + key: "tryItem", + value: + /** + * Add one or more item steps to the macro, where each step checks to see if you have the item first. + * + * @param items Items to try using. Pass a tuple [item1, item2] to funksling. + * @returns {StrictMacro} This object itself. + */ + function tryItem() { + var _get5; + for (var _len10 = arguments.length, items = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) { + items[_key10] = arguments[_key10]; + } + return (_get5 = _get(combat_getPrototypeOf(StrictMacro.prototype), "tryItem", this)).call.apply(_get5, [this].concat(items)); + } + /** + * Create a new macro with one or more item steps, where each step checks to see if you have the item first. + * + * @param items Items to try using. Pass a tuple [item1, item2] to funksling. + * @returns {StrictMacro} This object itself. + */ + }, { + key: "trySkillRepeat", + value: + /** + * Add one or more skill-cast-and-repeat steps to the macro, where each step checks if you have the skill first. + * + * @param skills Skills to try repeatedly casting. + * @returns {StrictMacro} This object itself. + */ + function trySkillRepeat() { + var _get6; + for (var _len11 = arguments.length, skills = new Array(_len11), _key11 = 0; _key11 < _len11; _key11++) { + skills[_key11] = arguments[_key11]; + } + return (_get6 = _get(combat_getPrototypeOf(StrictMacro.prototype), "trySkillRepeat", this)).call.apply(_get6, [this].concat(skills)); + } + /** + * Create a new macro with one or more skill-cast-and-repeat steps, where each step checks if you have the skill first. + * + * @param skills Skills to try repeatedly casting. + * @returns {StrictMacro} This object itself. + */ + }], [{ + key: "skill", + value: function skill() { + var _this8; + return (_this8 = new this()).skill.apply(_this8, arguments); + } + }, { + key: "item", + value: function item() { + var _this9; + return (_this9 = new this()).item.apply(_this9, arguments); + } + }, { + key: "trySkill", + value: function trySkill() { + var _this10; + return (_this10 = new this()).trySkill.apply(_this10, arguments); + } + }, { + key: "tryItem", + value: function tryItem() { + var _this11; + return (_this11 = new this()).tryItem.apply(_this11, arguments); + } + }, { + key: "trySkillRepeat", + value: function trySkillRepeat() { + var _this12; + return (_this12 = new this()).trySkillRepeat.apply(_this12, arguments); + } + }]); + return StrictMacro; +}(Macro))); +;// CONCATENATED MODULE: ./node_modules/grimoire-kolmafia/dist/combat.js +function dist_combat_inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) dist_combat_setPrototypeOf(subClass, superClass); } +function dist_combat_setPrototypeOf(o, p) { dist_combat_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return dist_combat_setPrototypeOf(o, p); } +function dist_combat_createSuper(Derived) { var hasNativeReflectConstruct = dist_combat_isNativeReflectConstruct(); return function _createSuperInternal() { var Super = dist_combat_getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = dist_combat_getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return dist_combat_possibleConstructorReturn(this, result); }; } +function dist_combat_possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return dist_combat_assertThisInitialized(self); } +function dist_combat_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function dist_combat_isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function dist_combat_getPrototypeOf(o) { dist_combat_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return dist_combat_getPrototypeOf(o); } +function dist_combat_toConsumableArray(arr) { return dist_combat_arrayWithoutHoles(arr) || dist_combat_iterableToArray(arr) || dist_combat_unsupportedIterableToArray(arr) || dist_combat_nonIterableSpread(); } +function dist_combat_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function dist_combat_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function dist_combat_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return dist_combat_arrayLikeToArray(arr); } +function dist_combat_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = dist_combat_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function dist_combat_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return dist_combat_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return dist_combat_arrayLikeToArray(o, minLen); } +function dist_combat_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function dist_combat_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function dist_combat_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, dist_combat_toPropertyKey(descriptor.key), descriptor); } } +function dist_combat_createClass(Constructor, protoProps, staticProps) { if (protoProps) dist_combat_defineProperties(Constructor.prototype, protoProps); if (staticProps) dist_combat_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function dist_combat_toPropertyKey(arg) { var key = dist_combat_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function dist_combat_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } + + +/** + * The strategy to use for combat for a task, which indicates what to do + * for each monster. + * + * There are two ways to specify in a task what to do for a given monster: + * 1. Provide a macro directly through .macro(macro, ...monsters) + * 2. Provide an action through .action(action, ...monsters) + * + * An action is a strategy for dealing with a monster that is not fully + * defined in the task. The possible actions are set with the type parameter A. + * Actions should typically end the fight. + * + * For example, a task may want to banish a monster but not necessarily know or + * care which banisher is used. Instead, it is best for the engine to determine + * which banisher to use on the monster. To facilitate this, "banish" can be + * defined as an action, e.g. with CombatStrategy<"banish">; + * + * Each action can be resolved by the engine by: + * 1. Providing a default macro for the action through ActionDefaults, + * which can be done through combat_defaults in Engine options, or + * 2. Providing a CombatResource for the action through CombatResources. + * This is typically done in Engine.customize() by checking if a given + * action is requested by the task with combat.can(.), and then providing + * an appropriate resource with resources.provide(.). + * + * A monster may have both a macro and an action defined, and a macro or action + * can be specified to be done on all monsters. The order of combat is then: + * 1. The macro(s) given in .startingMacro(). + * 2. The monster-specific macro(s) from .macro(). + * 3. The general macro(s) from .macro(). + * 4. The monster-specific action from .action(). + * 5. The general action from .action(). + * + * If an autoattack is set with .autoattack(), the order of the autoattack is: + * 1. The monster-specific macro(s) from .autoattack(). + * 2. The general macro(s) from .autoattack(). + */ +var CombatStrategy = /*#__PURE__*/function () { + function CombatStrategy() { + dist_combat_classCallCheck(this, CombatStrategy); + this.macros = new Map(); + this.autoattacks = new Map(); + this.actions = new Map(); + this.ccs_entries = new Map(); + } + /** + * Add a macro to perform for this monster. If multiple macros are given + * for the same monster, they are concatinated. + * + * @param macro The macro to perform. + * @param monsters Which monsters to use the macro on. If not given, add the + * macro as a general macro. + * @param prepend If true, add the macro before all previous macros for + * the same monster. If false, add after all previous macros. + * @returns this + */ + dist_combat_createClass(CombatStrategy, [{ + key: "macro", + value: function macro(_macro, monsters, prepend) { + var _a, _b; + if (monsters === undefined) { + if (this.default_macro === undefined) this.default_macro = []; + if (prepend) this.default_macro.unshift(_macro);else this.default_macro.push(_macro); + } else { + if (monsters instanceof external_kolmafia_.Monster) monsters = [monsters]; + var _iterator = dist_combat_createForOfIteratorHelper(monsters), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var monster = _step.value; + if (!this.macros.has(monster)) this.macros.set(monster, []); + if (prepend) (_a = this.macros.get(monster)) === null || _a === void 0 ? void 0 : _a.unshift(_macro);else (_b = this.macros.get(monster)) === null || _b === void 0 ? void 0 : _b.push(_macro); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } + return this; + } + /** + * Add a macro to perform as an autoattack for this monster. If multiple + * macros are given for the same monster, they are concatinated. + * + * @param macro The macro to perform as autoattack. + * @param monsters Which monsters to use the macro on. If not given, add the + * macro as a general macro. + * @param prepend If true, add the macro before all previous autoattack + * macros for the same monster. If false, add after all previous macros. + * @returns this + */ + }, { + key: "autoattack", + value: function autoattack(macro, monsters, prepend) { + var _a, _b; + if (monsters === undefined) { + if (this.default_autoattack === undefined) this.default_autoattack = []; + if (prepend) this.default_autoattack.unshift(macro);else this.default_autoattack.push(macro); + } else { + if (monsters instanceof external_kolmafia_.Monster) monsters = [monsters]; + var _iterator2 = dist_combat_createForOfIteratorHelper(monsters), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var monster = _step2.value; + if (!this.autoattacks.has(monster)) this.autoattacks.set(monster, []); + if (prepend) (_a = this.autoattacks.get(monster)) === null || _a === void 0 ? void 0 : _a.unshift(macro);else (_b = this.autoattacks.get(monster)) === null || _b === void 0 ? void 0 : _b.push(macro); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } + return this; + } + /** + * Add a macro to perform at the start of combat. + * @param macro The macro to perform. + * @param prepend If true, add the macro before all previous starting + * macros. If false, add after all previous starting macros. + * @returns this + */ + }, { + key: "startingMacro", + value: function startingMacro(macro, prepend) { + if (this.starting_macro === undefined) this.starting_macro = []; + if (prepend) this.starting_macro.unshift(macro);else this.starting_macro.push(macro); + return this; + } + /** + * Add an action to perform for this monster. Only one action can be set for + * each monster; any previous actions are overwritten. + * + * @param action The action to perform. + * @param monsters Which monsters to use the action on. If not given, set the + * action as the general action for all monsters. + * @returns this + */ + }, { + key: "action", + value: function action(_action, monsters) { + if (monsters === undefined) { + this.default_action = _action; + } else if (monsters instanceof external_kolmafia_.Monster) { + this.actions.set(monsters, _action); + } else { + var _iterator3 = dist_combat_createForOfIteratorHelper(monsters), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var monster = _step3.value; + this.actions.set(monster, _action); + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + } + return this; + } + /** + * Add a separate entry in the grimoire-generated CCS file for the specified + * monster. If multiple entries are given for the same monster, they are + * concatinated. + * + * This should typically be only used rarely, on monsters for which KoL does + * not support macros in combat (e.g. rampaging adding machine). + * + * @param entry The entry to add for the given monster. + * @param monsters Which monsters to add the entry to. + * @param prepend If true, add the entry before all previous entries. If + * false, add after all previous entries. + */ + }, { + key: "ccs", + value: function ccs(entry, monsters, prepend) { + var _a, _b; + if (monsters instanceof external_kolmafia_.Monster) monsters = [monsters]; + var _iterator4 = dist_combat_createForOfIteratorHelper(monsters), + _step4; + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var monster = _step4.value; + if (!this.ccs_entries.has(monster)) this.ccs_entries.set(monster, []); + if (prepend) (_a = this.ccs_entries.get(monster)) === null || _a === void 0 ? void 0 : _a.unshift(entry);else (_b = this.ccs_entries.get(monster)) === null || _b === void 0 ? void 0 : _b.push(entry); + } + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + return this; + } + /** + * Check if the provided action was requested for any monsters, or for the + * general action. + */ + }, { + key: "can", + value: function can(action) { + if (action === this.default_action) return true; + return Array.from(this.actions.values()).includes(action); + } + /** + * Return the general action (if it exists). + */ + }, { + key: "getDefaultAction", + value: function getDefaultAction() { + return this.default_action; + } + /** + * Return all monsters where the provided action was requested. + */ + }, { + key: "where", + value: function where(action) { + return Array.from(this.actions.keys()).filter(key => this.actions.get(key) === action); + } + /** + * Return the requested action (if it exists) for the provided monster. + */ + }, { + key: "currentStrategy", + value: function currentStrategy(monster) { + var _a; + return (_a = this.actions.get(monster)) !== null && _a !== void 0 ? _a : this.default_action; + } + /** + * Perform a deep copy of this combat strategy. + */ + }, { + key: "clone", + value: function clone() { + var result = new CombatStrategy(); + if (this.starting_macro) result.starting_macro = dist_combat_toConsumableArray(this.starting_macro); + if (this.default_macro) result.default_macro = dist_combat_toConsumableArray(this.default_macro); + var _iterator5 = dist_combat_createForOfIteratorHelper(this.macros), + _step5; + try { + for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { + var pair = _step5.value; + result.macros.set(pair[0], dist_combat_toConsumableArray(pair[1])); + } + } catch (err) { + _iterator5.e(err); + } finally { + _iterator5.f(); + } + if (this.default_autoattack) result.default_autoattack = dist_combat_toConsumableArray(this.default_autoattack); + var _iterator6 = dist_combat_createForOfIteratorHelper(this.autoattacks), + _step6; + try { + for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { + var _pair = _step6.value; + result.autoattacks.set(_pair[0], dist_combat_toConsumableArray(_pair[1])); + } + } catch (err) { + _iterator6.e(err); + } finally { + _iterator6.f(); + } + result.default_action = this.default_action; + var _iterator7 = dist_combat_createForOfIteratorHelper(this.actions), + _step7; + try { + for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { + var _pair2 = _step7.value; + result.actions.set(_pair2[0], _pair2[1]); + } + } catch (err) { + _iterator7.e(err); + } finally { + _iterator7.f(); + } + var _iterator8 = dist_combat_createForOfIteratorHelper(this.ccs_entries), + _step8; + try { + for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) { + var _pair3 = _step8.value; + result.ccs_entries.set(_pair3[0], dist_combat_toConsumableArray(_pair3[1])); + } + } catch (err) { + _iterator8.e(err); + } finally { + _iterator8.f(); + } + return result; + } + /** + * Compile this combat strategy into a complete macro. + * + * @param resources The resources to use to fulfil actions. + * @param defaults Macros to perform for each action without a resource. + * @param location The adventuring location, if known. + * @returns The compiled macro. + */ + }, { + key: "compile", + value: function compile(resources, defaults, location) { + var _a, _b; + var result = new Macro(); + // If there is macro precursor, do it now + if (this.starting_macro) { + result.step.apply(result, dist_combat_toConsumableArray(this.starting_macro.map(undelay))); + } + // Perform any monster-specific macros (these may or may not end the fight) + var monster_macros = new CompressedMacro(); + this.macros.forEach((value, key) => { + var _Macro; + monster_macros.add(key, (_Macro = new Macro()).step.apply(_Macro, dist_combat_toConsumableArray(value.map(undelay)))); + }); + result.step(monster_macros.compile()); + // Perform the non-monster specific macro + if (this.default_macro) result.step.apply(result, dist_combat_toConsumableArray(this.default_macro.map(undelay))); + // Perform any monster-specific actions (these should end the fight) + var monster_actions = new CompressedMacro(); + this.actions.forEach((action, key) => { + var _a, _b; + var macro = (_a = resources.getMacro(action)) !== null && _a !== void 0 ? _a : (_b = defaults === null || defaults === void 0 ? void 0 : defaults[action]) === null || _b === void 0 ? void 0 : _b.call(defaults, key); + if (macro) monster_actions.add(key, new Macro().step(macro)); + }); + result.step(monster_actions.compile()); + // Perform the non-monster specific action (these should end the fight) + if (this.default_action) { + var macro = (_a = resources.getMacro(this.default_action)) !== null && _a !== void 0 ? _a : (_b = defaults === null || defaults === void 0 ? void 0 : defaults[this.default_action]) === null || _b === void 0 ? void 0 : _b.call(defaults, location); + if (macro) result.step(macro); + } + return result; + } + /** + * Compile the autoattack of this combat strategy into a complete macro. + * + * @returns The compiled autoattack macro. + */ + }, { + key: "compileAutoattack", + value: function compileAutoattack() { + var result = new Macro(); + // Perform any monster-specific autoattacks (these may or may not end the fight) + var monster_macros = new CompressedMacro(); + this.autoattacks.forEach((value, key) => { + var _Macro2; + monster_macros.add(key, (_Macro2 = new Macro()).step.apply(_Macro2, dist_combat_toConsumableArray(value.map(undelay)))); + }); + result.step(monster_macros.compile()); + // Perform the non-monster specific macro + if (this.default_autoattack) result.step.apply(result, dist_combat_toConsumableArray(this.default_autoattack.map(undelay))); + return result; + } + /** + * Compile the CCS entries of this combat strategy into a single array. + * + * @returns The lines of a CCS file, not including the [default] macro. + */ + }, { + key: "compileCcs", + value: function compileCcs() { + var result = []; + var _iterator9 = dist_combat_createForOfIteratorHelper(this.ccs_entries), + _step9; + try { + for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) { + var ccs_entry = _step9.value; + result.push.apply(result, ["[".concat(ccs_entry[0].name, "]")].concat(dist_combat_toConsumableArray(ccs_entry[1]))); + } + } catch (err) { + _iterator9.e(err); + } finally { + _iterator9.f(); + } + return result; + } + /** + * For advanced users, this method will generate a fluent API for requesting + * actions. That is, it allows you to do + * combat.banish(monster1).kill(monster2) + * instead of + * combat.action("banish", monster1).action("kill", monster2) + * + * Example usage: + * const myActions = ["kill", "banish"] as const; + * class MyCombatStrategy extends CombatStrategy.withActions(myActions) {} + * + * const foo: MyCombatStrategy = new MyCombatStrategy(); + * const bar: MyCombatStrategy = foo.banish($monster`crate`).kill($monster`tumbleweed`); + */ + }], [{ + key: "withActions", + value: function withActions(actions) { + var CombatStrategyWithActions = /*#__PURE__*/function (_this) { + dist_combat_inherits(CombatStrategyWithActions, _this); + var _super = dist_combat_createSuper(CombatStrategyWithActions); + function CombatStrategyWithActions() { + dist_combat_classCallCheck(this, CombatStrategyWithActions); + return _super.apply(this, arguments); + } + return dist_combat_createClass(CombatStrategyWithActions); + }(this); // eslint-disable-next-line @typescript-eslint/no-explicit-any + var proto = CombatStrategyWithActions.prototype; + var _iterator10 = dist_combat_createForOfIteratorHelper(actions), + _step10; + try { + var _loop = function _loop() { + var action = _step10.value; + proto[action] = function (monsters) { + return this.action(action, monsters); + }; + }; + for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) { + _loop(); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err) { + _iterator10.e(err); + } finally { + _iterator10.f(); + } + return CombatStrategyWithActions; + } + }]); + return CombatStrategy; +}(); +/** + * A class to build a macro that combines if statements (keyed on monster) with + * identical body into a single if statement, to avoid the 37-action limit. + * Ex: [if x; A; if y; B; if z; A;] will turn into [if x || z; A; if y; B] + */ +var CompressedMacro = /*#__PURE__*/function () { + function CompressedMacro() { + dist_combat_classCallCheck(this, CompressedMacro); + this.components = new Map(); + } + /** + * Set the macro for a given monster (replacing any previous macros). + */ + dist_combat_createClass(CompressedMacro, [{ + key: "add", + value: function add(monster, macro) { + var _a; + var macro_text = macro.toString(); + if (macro_text.length === 0) return; + if (!this.components.has(macro_text)) this.components.set(macro_text, [monster]);else (_a = this.components.get(macro_text)) === null || _a === void 0 ? void 0 : _a.push(monster); + } + /** + * Compile the compressed form of the macro. + */ + }, { + key: "compile", + value: function compile() { + var result = new Macro(); + this.components.forEach((monsters, macro) => { + var condition = monsters.map(mon => "monsterid ".concat(mon.id)).join(" || "); + result.if_(condition, macro); + }); + return result; + } + }]); + return CompressedMacro; +}(); +/** + * A class for providing resources to fulfil combat actions. + */ +var CombatResources = /*#__PURE__*/function () { + function CombatResources() { + dist_combat_classCallCheck(this, CombatResources); + this.resources = new Map(); + } + /** + * Use the provided resource to fulfil the provided action. + * (If the resource is undefined, this does nothing). + */ + dist_combat_createClass(CombatResources, [{ + key: "provide", + value: function provide(action, resource) { + if (resource === undefined) return; + this.resources.set(action, resource); + } + /** + * Return true if the provided action has a resource provided. + */ + }, { + key: "has", + value: function has(action) { + return this.resources.has(action); + } + /** + * Return all provided combat resources. + */ + }, { + key: "all", + value: function all() { + return Array.from(this.resources.values()); + } + /** + * Get the macro provided by the resource for this action, or undefined if + * no resource was provided. + */ + }, { + key: "getMacro", + value: function getMacro(action) { + var resource = this.resources.get(action); + if (resource === undefined) return undefined; + if (resource.do instanceof external_kolmafia_.Item) return new Macro().item(resource.do); + if (resource.do instanceof external_kolmafia_.Skill) return new Macro().skill(resource.do); + return undelay(resource.do); + } + }]); + return CombatResources; +}(); +;// CONCATENATED MODULE: ./node_modules/libram/dist/logger.js +var _defaultHandlers; +function logger_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function logger_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, logger_toPropertyKey(descriptor.key), descriptor); } } +function logger_createClass(Constructor, protoProps, staticProps) { if (protoProps) logger_defineProperties(Constructor.prototype, protoProps); if (staticProps) logger_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function logger_defineProperty(obj, key, value) { key = logger_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function logger_toPropertyKey(arg) { var key = logger_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function logger_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } + +var LogLevels; +(function (LogLevels) { + LogLevels[LogLevels["NONE"] = 0] = "NONE"; + LogLevels[LogLevels["ERROR"] = 1] = "ERROR"; + LogLevels[LogLevels["WARNING"] = 2] = "WARNING"; + LogLevels[LogLevels["INFO"] = 3] = "INFO"; + LogLevels[LogLevels["DEBUG"] = 4] = "DEBUG"; +})(LogLevels || (LogLevels = {})); +var defaultHandlers = (_defaultHandlers = {}, logger_defineProperty(_defaultHandlers, LogLevels.INFO, message => { + (0,external_kolmafia_.printHtml)("[Libram Info] ".concat(message)); + (0,external_kolmafia_.logprint)("[Libram] ".concat(message)); + return; +}), logger_defineProperty(_defaultHandlers, LogLevels.WARNING, message => { + (0,external_kolmafia_.printHtml)("[Libram Warning] ".concat(message, "")); + (0,external_kolmafia_.logprint)("[Libram] ".concat(message)); + return; +}), logger_defineProperty(_defaultHandlers, LogLevels.ERROR, error => { + (0,external_kolmafia_.printHtml)("[Libram Error] ".concat(error.toString(), "")); + (0,external_kolmafia_.logprint)("[Libram] ".concat(error)); + return; +}), logger_defineProperty(_defaultHandlers, LogLevels.DEBUG, message => { + (0,external_kolmafia_.printHtml)("[Libram Debug] ".concat(message, "")); + (0,external_kolmafia_.logprint)("[Libram] ".concat(message)); + return; +}), _defaultHandlers); +var Logger = /*#__PURE__*/function () { + function Logger() { + logger_classCallCheck(this, Logger); + logger_defineProperty(this, "handlers", defaultHandlers); + } + logger_createClass(Logger, [{ + key: "level", + get: function get() { + return Logger.currentLevel; + } + }, { + key: "setLevel", + value: function setLevel(level) { + Logger.currentLevel = level; + } + }, { + key: "setHandler", + value: function setHandler(level, callback) { + this.handlers[level] = callback; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }, { + key: "log", + value: function log(level, message) { + if (this.level >= level) this.handlers[level](message); + } + }, { + key: "info", + value: function info(message) { + this.log(LogLevels.INFO, message); + } + }, { + key: "warning", + value: function warning(message) { + this.log(LogLevels.WARNING, message); + } + }, { + key: "error", + value: function error(message) { + this.log(LogLevels.ERROR, message); + } + }, { + key: "debug", + value: function debug(message) { + this.log(LogLevels.DEBUG, message); + } + }]); + return Logger; +}(); +logger_defineProperty(Logger, "currentLevel", LogLevels.ERROR); +/* harmony default export */ const dist_logger = (new Logger()); +;// CONCATENATED MODULE: ./node_modules/libram/dist/maximize.js +var maximize_templateObject, maximize_templateObject2, maximize_templateObject3, maximize_templateObject4, maximize_templateObject5, maximize_templateObject6, maximize_templateObject7, maximize_templateObject8, maximize_templateObject9, maximize_templateObject10, maximize_templateObject11, maximize_templateObject12, maximize_templateObject13, maximize_templateObject14, maximize_templateObject15, maximize_templateObject16, maximize_templateObject17, maximize_templateObject18, maximize_templateObject19, maximize_templateObject20, maximize_templateObject21, maximize_templateObject22, maximize_templateObject23, maximize_templateObject24, maximize_templateObject25, maximize_templateObject26, maximize_templateObject27, maximize_templateObject28, maximize_templateObject29, maximize_templateObject30, maximize_templateObject31, maximize_templateObject32, maximize_templateObject33, maximize_templateObject34, maximize_templateObject35, maximize_templateObject36, maximize_templateObject37, maximize_templateObject38, maximize_templateObject39, maximize_templateObject40, maximize_templateObject41, maximize_templateObject42, maximize_templateObject43, maximize_templateObject44, maximize_templateObject45, _templateObject46, _templateObject47, _templateObject48, _templateObject49; +function maximize_slicedToArray(arr, i) { return maximize_arrayWithHoles(arr) || maximize_iterableToArrayLimit(arr, i) || maximize_unsupportedIterableToArray(arr, i) || maximize_nonIterableRest(); } +function maximize_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function maximize_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function maximize_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); } +function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } } +function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); } +function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } +function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; } +function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } +function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } +function maximize_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, maximize_toPropertyKey(descriptor.key), descriptor); } } +function maximize_createClass(Constructor, protoProps, staticProps) { if (protoProps) maximize_defineProperties(Constructor.prototype, protoProps); if (staticProps) maximize_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function maximize_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function maximize_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = maximize_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function maximize_taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } +function maximize_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function maximize_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? maximize_ownKeys(Object(t), !0).forEach(function (r) { maximize_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : maximize_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function maximize_defineProperty(obj, key, value) { key = maximize_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function maximize_toPropertyKey(arg) { var key = maximize_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function maximize_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function maximize_toConsumableArray(arr) { return maximize_arrayWithoutHoles(arr) || maximize_iterableToArray(arr) || maximize_unsupportedIterableToArray(arr) || maximize_nonIterableSpread(); } +function maximize_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function maximize_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return maximize_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return maximize_arrayLikeToArray(o, minLen); } +function maximize_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function maximize_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return maximize_arrayLikeToArray(arr); } +function maximize_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + + + + + +function toMaximizerName(_ref) { + var name = _ref.name, + id = _ref.id; + return name.includes(";") ? "\xB6".concat(id) : name; +} +/** + * Merges a partial set of maximizer options onto a full set maximizer options. We merge via overriding for all boolean properties and for onlySlot, and concat all other array properties. + * + * @param defaultOptions MaximizeOptions to use as a "base." + * @param addendums Options to attempt to merge onto defaultOptions. + * @returns Merged maximizer options + */ +function mergeMaximizeOptions(defaultOptions, addendums) { + var _addendums$updateOnFa, _addendums$updateOnCa, _addendums$useOutfitC, _addendums$forceEquip, _addendums$preventEqu, _addendums$bonusEquip, _addendums$onlySlot, _addendums$preventSlo, _addendums$forceUpdat, _addendums$modes; + return { + updateOnFamiliarChange: (_addendums$updateOnFa = addendums.updateOnFamiliarChange) !== null && _addendums$updateOnFa !== void 0 ? _addendums$updateOnFa : defaultOptions.updateOnFamiliarChange, + updateOnCanEquipChanged: (_addendums$updateOnCa = addendums.updateOnCanEquipChanged) !== null && _addendums$updateOnCa !== void 0 ? _addendums$updateOnCa : defaultOptions.updateOnCanEquipChanged, + useOutfitCaching: (_addendums$useOutfitC = addendums.useOutfitCaching) !== null && _addendums$useOutfitC !== void 0 ? _addendums$useOutfitC : defaultOptions.useOutfitCaching, + forceEquip: [].concat(maximize_toConsumableArray(defaultOptions.forceEquip), maximize_toConsumableArray((_addendums$forceEquip = addendums.forceEquip) !== null && _addendums$forceEquip !== void 0 ? _addendums$forceEquip : [])), + preventEquip: [].concat(maximize_toConsumableArray(defaultOptions.preventEquip), maximize_toConsumableArray((_addendums$preventEqu = addendums.preventEquip) !== null && _addendums$preventEqu !== void 0 ? _addendums$preventEqu : [])).filter(item => { + var _addendums$forceEquip2; + return !defaultOptions.forceEquip.includes(item) && !((_addendums$forceEquip2 = addendums.forceEquip) !== null && _addendums$forceEquip2 !== void 0 && _addendums$forceEquip2.includes(item)); + }), + bonusEquip: new Map([].concat(maximize_toConsumableArray(defaultOptions.bonusEquip), maximize_toConsumableArray((_addendums$bonusEquip = addendums.bonusEquip) !== null && _addendums$bonusEquip !== void 0 ? _addendums$bonusEquip : []))), + onlySlot: (_addendums$onlySlot = addendums.onlySlot) !== null && _addendums$onlySlot !== void 0 ? _addendums$onlySlot : defaultOptions.onlySlot, + preventSlot: [].concat(maximize_toConsumableArray(defaultOptions.preventSlot), maximize_toConsumableArray((_addendums$preventSlo = addendums.preventSlot) !== null && _addendums$preventSlo !== void 0 ? _addendums$preventSlo : [])), + forceUpdate: (_addendums$forceUpdat = addendums.forceUpdate) !== null && _addendums$forceUpdat !== void 0 ? _addendums$forceUpdat : defaultOptions.forceUpdate, + modes: maximize_objectSpread(maximize_objectSpread({}, defaultOptions.modes), (_addendums$modes = addendums.modes) !== null && _addendums$modes !== void 0 ? _addendums$modes : {}) + }; +} +var defaultMaximizeOptions = { + updateOnFamiliarChange: true, + updateOnCanEquipChanged: true, + useOutfitCaching: true, + forceEquip: [], + preventEquip: [], + bonusEquip: new Map(), + onlySlot: [], + preventSlot: [], + forceUpdate: false, + modes: {} +}; +/** + * + * @param options Default options for each maximizer run. + * @param options.updateOnFamiliarChange Re-run the maximizer if familiar has changed. Default true. + * @param options.updateOnCanEquipChanged Re-run the maximizer if stats have changed what can be equipped. Default true. + * @param options.forceEquip Equipment to force-equip ("equip X"). + * @param options.preventEquip Equipment to prevent equipping ("-equip X"). + * @param options.bonusEquip Equipment to apply a bonus to ("200 bonus X"). + */ +function setDefaultMaximizeOptions(options) { + Object.assign(defaultMaximizeOptions, options); +} +var modeableCommands = ["backupcamera", "umbrella", "snowsuit", "edpiece", "retrocape", "parka"]; +var modeableItems = { + backupcamera: template_string_$item(maximize_templateObject || (maximize_templateObject = maximize_taggedTemplateLiteral(["backup camera"]))), + umbrella: template_string_$item(maximize_templateObject2 || (maximize_templateObject2 = maximize_taggedTemplateLiteral(["unbreakable umbrella"]))), + snowsuit: template_string_$item(maximize_templateObject3 || (maximize_templateObject3 = maximize_taggedTemplateLiteral(["Snow Suit"]))), + edpiece: template_string_$item(maximize_templateObject4 || (maximize_templateObject4 = maximize_taggedTemplateLiteral(["The Crown of Ed the Undying"]))), + retrocape: template_string_$item(maximize_templateObject5 || (maximize_templateObject5 = maximize_taggedTemplateLiteral(["unwrapped knock-off retro superhero cape"]))), + parka: template_string_$item(maximize_templateObject6 || (maximize_templateObject6 = maximize_taggedTemplateLiteral(["Jurassic Parka"]))) +}; +var modeableState = { + backupcamera: () => (0,external_kolmafia_.getProperty)("backupCameraMode"), + umbrella: () => (0,external_kolmafia_.getProperty)("umbrellaState"), + snowsuit: () => (0,external_kolmafia_.getProperty)("snowsuit"), + edpiece: () => (0,external_kolmafia_.getProperty)("edPiece"), + retrocape: () => (0,external_kolmafia_.getProperty)("retroCapeSuperhero") + " " + (0,external_kolmafia_.getProperty)("retroCapeWashingInstructions"), + parka: () => (0,external_kolmafia_.getProperty)("parkaMode") +}; +/** + * Get set of current modes for modeables + * + * @returns Set of modes + */ +function getCurrentModes() { + var modes = {}; + var _iterator = maximize_createForOfIteratorHelper(modeableCommands), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var key = _step.value; + if ((0,external_kolmafia_.haveEquipped)(modeableItems[key])) { + modes[key] = modeableState[key](); + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return modes; +} +/** + * Apply set of modes + * + * @param modes Modes to apply + */ +function applyModes(modes) { + var _iterator2 = maximize_createForOfIteratorHelper(modeableCommands), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var command = _step2.value; + if ((0,external_kolmafia_.haveEquipped)(modeableItems[command]) && modes[command] !== undefined) { + if (modeableState[command]() !== modes[command]) { + (0,external_kolmafia_.cliExecute)(command + " " + modes[command]); + } + } + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } +} +// Subset of slots that are valid for caching. +var cachedSlots = $slots(maximize_templateObject7 || (maximize_templateObject7 = maximize_taggedTemplateLiteral(["hat, weapon, off-hand, back, shirt, pants, acc1, acc2, acc3, familiar"]))); +var CacheEntry = /*#__PURE__*/maximize_createClass(function CacheEntry(equipment, rider, familiar, canEquipItemCount, modes) { + maximize_classCallCheck(this, CacheEntry); + maximize_defineProperty(this, "equipment", void 0); + maximize_defineProperty(this, "rider", void 0); + maximize_defineProperty(this, "familiar", void 0); + maximize_defineProperty(this, "canEquipItemCount", void 0); + maximize_defineProperty(this, "modes", void 0); + this.equipment = equipment; + this.rider = rider; + this.familiar = familiar; + this.canEquipItemCount = canEquipItemCount; + this.modes = modes; +}); +var _outfitSlots = /*#__PURE__*/new WeakMap(); +var _useHistory = /*#__PURE__*/new WeakMap(); +var _maxSize = /*#__PURE__*/new WeakMap(); +var OutfitLRUCache = /*#__PURE__*/function () { + function OutfitLRUCache(maxSize) { + maximize_classCallCheck(this, OutfitLRUCache); + // Current outfits allocated + _classPrivateFieldInitSpec(this, _outfitSlots, { + writable: true, + value: [] + }); + // Array of indices into #outfitSlots in order of use. Most recent at the front. + _classPrivateFieldInitSpec(this, _useHistory, { + writable: true, + value: [] + }); + _classPrivateFieldInitSpec(this, _maxSize, { + writable: true, + value: void 0 + }); + _classPrivateFieldSet(this, _maxSize, maxSize); + } + maximize_createClass(OutfitLRUCache, [{ + key: "checkConsistent", + value: function checkConsistent() { + if (_classPrivateFieldGet(this, _useHistory).length !== _classPrivateFieldGet(this, _outfitSlots).length || !maximize_toConsumableArray(_classPrivateFieldGet(this, _useHistory)).sort().every((value, index) => value === index)) { + throw new Error("Outfit cache consistency failed."); + } + } + }, { + key: "promote", + value: function promote(index) { + _classPrivateFieldSet(this, _useHistory, [index].concat(maximize_toConsumableArray(_classPrivateFieldGet(this, _useHistory).filter(i => i !== index)))); + this.checkConsistent(); + } + }, { + key: "get", + value: function get(key) { + var index = _classPrivateFieldGet(this, _outfitSlots).indexOf(key); + if (index < 0) return undefined; + this.promote(index); + return "".concat(OutfitLRUCache.OUTFIT_PREFIX, " ").concat(index); + } + }, { + key: "insert", + value: function insert(key) { + var lastUseIndex = undefined; + if (_classPrivateFieldGet(this, _outfitSlots).length >= _classPrivateFieldGet(this, _maxSize)) { + lastUseIndex = _classPrivateFieldGet(this, _useHistory).pop(); + if (lastUseIndex === undefined) { + throw new Error("Outfit cache consistency failed."); + } + _classPrivateFieldGet(this, _useHistory).splice(0, 0, lastUseIndex); + _classPrivateFieldGet(this, _outfitSlots)[lastUseIndex] = key; + this.checkConsistent(); + return "".concat(OutfitLRUCache.OUTFIT_PREFIX, " ").concat(lastUseIndex); + } else { + var index = _classPrivateFieldGet(this, _outfitSlots).push(key) - 1; + _classPrivateFieldGet(this, _useHistory).splice(0, 0, index); + this.checkConsistent(); + return "".concat(OutfitLRUCache.OUTFIT_PREFIX, " ").concat(index); + } + } + }, { + key: "clear", + value: function clear() { + _classPrivateFieldSet(this, _outfitSlots, []); + _classPrivateFieldSet(this, _useHistory, []); + } + }]); + return OutfitLRUCache; +}(); +/** + * Save current equipment as KoL-native outfit. + * + * @param name Name of new outfit. + */ +maximize_defineProperty(OutfitLRUCache, "OUTFIT_PREFIX", "Script Outfit"); +function saveOutfit(name) { + (0,external_kolmafia_.cliExecute)("outfit save ".concat(name)); +} +// Objective cache entries. +var cachedObjectives = {}; +// Outfit cache entries. Keep 6 by default to avoid cluttering list. +var outfitCache = new OutfitLRUCache(6); +// Cache to prevent rescanning all items unnecessarily +var cachedStats = [0, 0, 0]; +var cachedCanEquipItemCount = 0; +/** + * Count the number of unique items that can be equipped. + * + * @returns The count of unique items. + */ +function canEquipItemCount() { + var stats = $stats(maximize_templateObject8 || (maximize_templateObject8 = maximize_taggedTemplateLiteral(["Muscle, Mysticality, Moxie"]))).map(stat => Math.min((0,external_kolmafia_.myBasestat)(stat), 300)); + if (stats.every((value, index) => value === cachedStats[index])) { + return cachedCanEquipItemCount; + } + cachedStats = stats; + cachedCanEquipItemCount = external_kolmafia_.Item.all().filter(item => (0,external_kolmafia_.canEquip)(item)).length; + return cachedCanEquipItemCount; +} +/** + * Checks the objective cache for a valid entry. + * + * @param cacheKey The cache key to check. + * @param options Set of maximizer options + * @returns A valid CacheEntry or null. + */ +function checkCache(cacheKey, options) { + var entry = cachedObjectives[cacheKey]; + if (!entry) { + return null; + } + if (options.updateOnFamiliarChange && (0,external_kolmafia_.myFamiliar)() !== entry.familiar) { + dist_logger.warning("Equipment found in maximize cache but familiar is different."); + return null; + } + if (options.updateOnCanEquipChanged && entry.canEquipItemCount !== canEquipItemCount()) { + dist_logger.warning("Equipment found in maximize cache but equippable item list is out of date."); + return null; + } + return entry; +} +/** + * Applies equipment that was found in the cache. + * + * @param entry The CacheEntry to apply + * @param options Set of maximizer options + */ +function applyCached(entry, options) { + var outfitName = options.useOutfitCaching ? outfitCache.get(entry) : undefined; + if (outfitName) { + if (!(0,external_kolmafia_.isWearingOutfit)(outfitName)) { + (0,external_kolmafia_.outfit)(outfitName); + } + var familiarEquip = entry.equipment.get($slot(maximize_templateObject9 || (maximize_templateObject9 = maximize_taggedTemplateLiteral(["familiar"])))); + if (familiarEquip) (0,external_kolmafia_.equip)($slot(maximize_templateObject10 || (maximize_templateObject10 = maximize_taggedTemplateLiteral(["familiar"]))), familiarEquip); + } else { + var _iterator3 = maximize_createForOfIteratorHelper(entry.equipment), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var _step3$value = maximize_slicedToArray(_step3.value, 2), + slot = _step3$value[0], + item = _step3$value[1]; + if ((0,external_kolmafia_.equippedItem)(slot) !== item && (0,external_kolmafia_.availableAmount)(item) > 0) { + (0,external_kolmafia_.equip)(slot, item); + } + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + if (verifyCached(entry) && options.useOutfitCaching) { + var _outfitName = outfitCache.insert(entry); + dist_logger.info("Saving equipment to outfit ".concat(_outfitName, ".")); + saveOutfit(_outfitName); + } + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject11 || (maximize_templateObject11 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))) > 0 && entry.rider.get(template_string_$item(maximize_templateObject12 || (maximize_templateObject12 = maximize_taggedTemplateLiteral(["Crown of Thrones"]))))) { + (0,external_kolmafia_.enthroneFamiliar)(entry.rider.get(template_string_$item(maximize_templateObject13 || (maximize_templateObject13 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))) || template_string_$familiar.none); + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject14 || (maximize_templateObject14 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))) > 0 && entry.rider.get(template_string_$item(maximize_templateObject15 || (maximize_templateObject15 = maximize_taggedTemplateLiteral(["Buddy Bjorn"]))))) { + (0,external_kolmafia_.bjornifyFamiliar)(entry.rider.get(template_string_$item(maximize_templateObject16 || (maximize_templateObject16 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))) || template_string_$familiar.none); + } + applyModes(maximize_objectSpread(maximize_objectSpread({}, entry.modes), options.modes)); +} +var slotStructure = [$slots(maximize_templateObject17 || (maximize_templateObject17 = maximize_taggedTemplateLiteral(["hat"]))), $slots(maximize_templateObject18 || (maximize_templateObject18 = maximize_taggedTemplateLiteral(["back"]))), $slots(maximize_templateObject19 || (maximize_templateObject19 = maximize_taggedTemplateLiteral(["shirt"]))), $slots(maximize_templateObject20 || (maximize_templateObject20 = maximize_taggedTemplateLiteral(["weapon, off-hand"]))), $slots(maximize_templateObject21 || (maximize_templateObject21 = maximize_taggedTemplateLiteral(["pants"]))), $slots(maximize_templateObject22 || (maximize_templateObject22 = maximize_taggedTemplateLiteral(["acc1, acc2, acc3"]))), $slots(maximize_templateObject23 || (maximize_templateObject23 = maximize_taggedTemplateLiteral(["familiar"])))]; +/** + * Verifies that a CacheEntry was applied successfully. + * + * @param entry The CacheEntry to verify + * @param warn Whether to warn if the cache could not be applied + * @returns If all desired equipment was appliedn in the correct slots. + */ +function verifyCached(entry) { + var warn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var success = true; + var _iterator4 = maximize_createForOfIteratorHelper(slotStructure), + _step4; + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var slotGroup = _step4.value; + var desiredSlots = slotGroup.map(slot => { + var _entry$equipment$get; + return [slot, (_entry$equipment$get = entry.equipment.get(slot)) !== null && _entry$equipment$get !== void 0 ? _entry$equipment$get : null]; + }).filter(_ref2 => { + var _ref3 = maximize_slicedToArray(_ref2, 2), + item = _ref3[1]; + return item !== null; + }); + var desiredSet = desiredSlots.map(_ref4 => { + var _ref5 = maximize_slicedToArray(_ref4, 2), + item = _ref5[1]; + return item; + }); + var equippedSet = desiredSlots.map(_ref6 => { + var _ref7 = maximize_slicedToArray(_ref6, 1), + slot = _ref7[0]; + return (0,external_kolmafia_.equippedItem)(slot); + }); + if (!setEqual(desiredSet, equippedSet)) { + if (warn) { + dist_logger.warning("Failed to apply cached ".concat(desiredSet.join(", "), " in ").concat(slotGroup.join(", "), ".")); + } + success = false; + } + } + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject24 || (maximize_templateObject24 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))) > 0 && entry.rider.get(template_string_$item(maximize_templateObject25 || (maximize_templateObject25 = maximize_taggedTemplateLiteral(["Crown of Thrones"]))))) { + if (entry.rider.get(template_string_$item(maximize_templateObject26 || (maximize_templateObject26 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))) !== (0,external_kolmafia_.myEnthronedFamiliar)()) { + if (warn) { + dist_logger.warning("Failed to apply ".concat(entry.rider.get(template_string_$item(maximize_templateObject27 || (maximize_templateObject27 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))), " in ").concat(template_string_$item(maximize_templateObject28 || (maximize_templateObject28 = maximize_taggedTemplateLiteral(["Crown of Thrones"]))), ".")); + } + success = false; + } + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject29 || (maximize_templateObject29 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))) > 0 && entry.rider.get(template_string_$item(maximize_templateObject30 || (maximize_templateObject30 = maximize_taggedTemplateLiteral(["Buddy Bjorn"]))))) { + if (entry.rider.get(template_string_$item(maximize_templateObject31 || (maximize_templateObject31 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))) !== (0,external_kolmafia_.myBjornedFamiliar)()) { + if (warn) { + dist_logger.warning("Failed to apply".concat(entry.rider.get(template_string_$item(maximize_templateObject32 || (maximize_templateObject32 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))), " in ").concat(template_string_$item(maximize_templateObject33 || (maximize_templateObject33 = maximize_taggedTemplateLiteral(["Buddy Bjorn"]))), ".")); + } + success = false; + } + } + return success; +} +/** + * Save current equipment to the objective cache. + * + * @param cacheKey The cache key to save. + * @param options Set of maximizer options + */ +function saveCached(cacheKey, options) { + var equipment = new Map(); + var rider = new Map(); + var _iterator5 = maximize_createForOfIteratorHelper(cachedSlots), + _step5; + try { + for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { + var _slot2 = _step5.value; + equipment.set(_slot2, (0,external_kolmafia_.equippedItem)(_slot2)); + } + } catch (err) { + _iterator5.e(err); + } finally { + _iterator5.f(); + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject34 || (maximize_templateObject34 = maximize_taggedTemplateLiteral(["card sleeve"])))) > 0) { + equipment.set($slot(maximize_templateObject35 || (maximize_templateObject35 = maximize_taggedTemplateLiteral(["card-sleeve"]))), (0,external_kolmafia_.equippedItem)($slot(maximize_templateObject36 || (maximize_templateObject36 = maximize_taggedTemplateLiteral(["card-sleeve"]))))); + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject37 || (maximize_templateObject37 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))) > 0) { + rider.set(template_string_$item(maximize_templateObject38 || (maximize_templateObject38 = maximize_taggedTemplateLiteral(["Crown of Thrones"]))), (0,external_kolmafia_.myEnthronedFamiliar)()); + } + if ((0,external_kolmafia_.equippedAmount)(template_string_$item(maximize_templateObject39 || (maximize_templateObject39 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))) > 0) { + rider.set(template_string_$item(maximize_templateObject40 || (maximize_templateObject40 = maximize_taggedTemplateLiteral(["Buddy Bjorn"]))), (0,external_kolmafia_.myBjornedFamiliar)()); + } + if (options.preventSlot && options.preventSlot.length > 0) { + var _iterator6 = maximize_createForOfIteratorHelper(options.preventSlot), + _step6; + try { + for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { + var slot = _step6.value; + equipment.delete(slot); + } + } catch (err) { + _iterator6.e(err); + } finally { + _iterator6.f(); + } + if (options.preventSlot.includes($slot(maximize_templateObject41 || (maximize_templateObject41 = maximize_taggedTemplateLiteral(["buddy-bjorn"]))))) { + rider.delete(template_string_$item(maximize_templateObject42 || (maximize_templateObject42 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))); + } + if (options.preventSlot.includes($slot(maximize_templateObject43 || (maximize_templateObject43 = maximize_taggedTemplateLiteral(["crown-of-thrones"]))))) { + rider.delete(template_string_$item(maximize_templateObject44 || (maximize_templateObject44 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))); + } + } + if (options.onlySlot && options.onlySlot.length > 0) { + var _iterator7 = maximize_createForOfIteratorHelper(external_kolmafia_.Slot.all()), + _step7; + try { + for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { + var _slot = _step7.value; + if (!options.onlySlot.includes(_slot)) { + equipment.delete(_slot); + } + } + } catch (err) { + _iterator7.e(err); + } finally { + _iterator7.f(); + } + if (!options.onlySlot.includes($slot(maximize_templateObject45 || (maximize_templateObject45 = maximize_taggedTemplateLiteral(["buddy-bjorn"]))))) { + rider.delete(template_string_$item(_templateObject46 || (_templateObject46 = maximize_taggedTemplateLiteral(["Buddy Bjorn"])))); + } + if (!options.onlySlot.includes($slot(_templateObject47 || (_templateObject47 = maximize_taggedTemplateLiteral(["crown-of-thrones"]))))) { + rider.delete(template_string_$item(_templateObject48 || (_templateObject48 = maximize_taggedTemplateLiteral(["Crown of Thrones"])))); + } + } + var entry = new CacheEntry(equipment, rider, (0,external_kolmafia_.myFamiliar)(), canEquipItemCount(), maximize_objectSpread(maximize_objectSpread({}, getCurrentModes()), options.modes)); + cachedObjectives[cacheKey] = entry; + if (options.useOutfitCaching) { + var outfitName = outfitCache.insert(entry); + dist_logger.info("Saving equipment to outfit ".concat(outfitName, ".")); + saveOutfit(outfitName); + } +} +/** + * Run the maximizer, but only if the objective and certain pieces of game state haven't changed since it was last run. + * + * @param objectives Objectives to maximize for. + * @param options Options for this run of the maximizer. + * @param options.updateOnFamiliarChange Re-run the maximizer if familiar has changed. Default true. + * @param options.updateOnCanEquipChanged Re-run the maximizer if stats have changed what can be equipped. Default true. + * @param options.forceEquip Equipment to force-equip ("equip X"). + * @param options.preventEquip Equipment to prevent equipping ("-equip X"). + * @param options.bonusEquip Equipment to apply a bonus to ("200 bonus X"). + * @returns Whether the maximize call succeeded. + */ +function maximizeCached(objectives) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var fullOptions = mergeMaximizeOptions(defaultMaximizeOptions, options); + var forceEquip = fullOptions.forceEquip, + preventEquip = fullOptions.preventEquip, + bonusEquip = fullOptions.bonusEquip, + onlySlot = fullOptions.onlySlot, + preventSlot = fullOptions.preventSlot, + forceUpdate = fullOptions.forceUpdate; + // Sort each group in objective to ensure consistent ordering in string + var objective = maximize_toConsumableArray(new Set([].concat(maximize_toConsumableArray(objectives.sort()), maximize_toConsumableArray(forceEquip.map(item => "\"equip ".concat(toMaximizerName(item), "\"")).sort()), maximize_toConsumableArray(preventEquip.map(item => "-\"equip ".concat(toMaximizerName(item), "\"")).sort()), maximize_toConsumableArray(onlySlot.map(slot => "".concat(slot)).sort()), maximize_toConsumableArray(preventSlot.map(slot => "-".concat(slot)).sort()), maximize_toConsumableArray(Array.from(bonusEquip.entries()).filter(_ref8 => { + var _ref9 = maximize_slicedToArray(_ref8, 2), + bonus = _ref9[1]; + return bonus !== 0; + }).map(_ref10 => { + var _ref11 = maximize_slicedToArray(_ref10, 2), + item = _ref11[0], + bonus = _ref11[1]; + return "".concat(Math.round(bonus * 100) / 100, " \"bonus ").concat(toMaximizerName(item), "\""); + }).sort())))).join(", "); + // Items equipped in slots not touched by the maximizer must be in the cache key + var untouchedSlots = cachedSlots.filter(slot => preventSlot.includes(slot) || onlySlot.length > 0 && !onlySlot.includes(slot)); + var cacheKey = [objective].concat(maximize_toConsumableArray(untouchedSlots.map(slot => "".concat(slot, ":").concat((0,external_kolmafia_.equippedItem)(slot))).sort()), [have(template_string_$effect(_templateObject49 || (_templateObject49 = maximize_taggedTemplateLiteral(["Offhand Remarkable"]))))]).join("; "); + var cacheEntry = checkCache(cacheKey, fullOptions); + if (cacheEntry && !forceUpdate) { + if (verifyCached(cacheEntry, false)) return true; + dist_logger.info("Equipment found in maximize cache, equipping..."); + applyCached(cacheEntry, fullOptions); + if (verifyCached(cacheEntry)) { + dist_logger.info("Equipped cached ".concat(cacheKey)); + return true; + } + dist_logger.warning("Maximize cache application failed, maximizing..."); + } + var result = (0,external_kolmafia_.maximize)(objective, false); + saveCached(cacheKey, fullOptions); + return result; +} +var _maximizeParameters = /*#__PURE__*/new WeakMap(); +var _maximizeOptions = /*#__PURE__*/new WeakMap(); +var Requirement = /*#__PURE__*/function () { + /** + * A convenient way of combining maximization parameters and options + * + * @param maximizeParameters Parameters you're attempting to maximize + * @param maximizeOptions Object potentially containing forceEquips, bonusEquips, preventEquips, and preventSlots + */ + function Requirement(maximizeParameters, maximizeOptions) { + maximize_classCallCheck(this, Requirement); + _classPrivateFieldInitSpec(this, _maximizeParameters, { + writable: true, + value: void 0 + }); + _classPrivateFieldInitSpec(this, _maximizeOptions, { + writable: true, + value: void 0 + }); + _classPrivateFieldSet(this, _maximizeParameters, maximizeParameters); + _classPrivateFieldSet(this, _maximizeOptions, maximizeOptions); + } + maximize_createClass(Requirement, [{ + key: "maximizeParameters", + get: function get() { + return _classPrivateFieldGet(this, _maximizeParameters); + } + }, { + key: "maximizeOptions", + get: function get() { + return _classPrivateFieldGet(this, _maximizeOptions); + } + /** + * Merges two requirements, concanating relevant arrays. Typically used in static form. + * + * @param other Requirement to merge with. + */ + }, { + key: "merge", + value: function merge(other) { + var _optionsA$forceEquip, _other$maximizeOption, _optionsA$preventEqui, _other$maximizeOption3, _optionsA$bonusEquip$, _optionsA$bonusEquip, _optionsB$bonusEquip$, _optionsB$bonusEquip, _optionsA$onlySlot, _optionsB$onlySlot, _optionsA$preventSlot, _optionsB$preventSlot; + var optionsA = this.maximizeOptions; + var optionsB = other.maximizeOptions; + return new Requirement([].concat(maximize_toConsumableArray(this.maximizeParameters), maximize_toConsumableArray(other.maximizeParameters)), { + updateOnFamiliarChange: optionsA.updateOnFamiliarChange || other.maximizeOptions.updateOnFamiliarChange, + updateOnCanEquipChanged: optionsA.updateOnCanEquipChanged || other.maximizeOptions.updateOnCanEquipChanged, + forceEquip: [].concat(maximize_toConsumableArray((_optionsA$forceEquip = optionsA.forceEquip) !== null && _optionsA$forceEquip !== void 0 ? _optionsA$forceEquip : []), maximize_toConsumableArray((_other$maximizeOption = other.maximizeOptions.forceEquip) !== null && _other$maximizeOption !== void 0 ? _other$maximizeOption : [])).filter(x => { + var _other$maximizeOption2; + return !((_other$maximizeOption2 = other.maximizeOptions.preventEquip) !== null && _other$maximizeOption2 !== void 0 && _other$maximizeOption2.includes(x)); + }), + preventEquip: [].concat(maximize_toConsumableArray((_optionsA$preventEqui = optionsA.preventEquip) !== null && _optionsA$preventEqui !== void 0 ? _optionsA$preventEqui : []), maximize_toConsumableArray((_other$maximizeOption3 = other.maximizeOptions.preventEquip) !== null && _other$maximizeOption3 !== void 0 ? _other$maximizeOption3 : [])).filter(x => { + var _other$maximizeOption4; + return !((_other$maximizeOption4 = other.maximizeOptions.forceEquip) !== null && _other$maximizeOption4 !== void 0 && _other$maximizeOption4.includes(x)); + }), + bonusEquip: new Map([].concat(maximize_toConsumableArray((_optionsA$bonusEquip$ = (_optionsA$bonusEquip = optionsA.bonusEquip) === null || _optionsA$bonusEquip === void 0 ? void 0 : _optionsA$bonusEquip.entries()) !== null && _optionsA$bonusEquip$ !== void 0 ? _optionsA$bonusEquip$ : []), maximize_toConsumableArray((_optionsB$bonusEquip$ = (_optionsB$bonusEquip = optionsB.bonusEquip) === null || _optionsB$bonusEquip === void 0 ? void 0 : _optionsB$bonusEquip.entries()) !== null && _optionsB$bonusEquip$ !== void 0 ? _optionsB$bonusEquip$ : []))), + onlySlot: [].concat(maximize_toConsumableArray((_optionsA$onlySlot = optionsA.onlySlot) !== null && _optionsA$onlySlot !== void 0 ? _optionsA$onlySlot : []), maximize_toConsumableArray((_optionsB$onlySlot = optionsB.onlySlot) !== null && _optionsB$onlySlot !== void 0 ? _optionsB$onlySlot : [])), + preventSlot: [].concat(maximize_toConsumableArray((_optionsA$preventSlot = optionsA.preventSlot) !== null && _optionsA$preventSlot !== void 0 ? _optionsA$preventSlot : []), maximize_toConsumableArray((_optionsB$preventSlot = optionsB.preventSlot) !== null && _optionsB$preventSlot !== void 0 ? _optionsB$preventSlot : [])), + forceUpdate: optionsA.forceUpdate || optionsB.forceUpdate + }); + } + /** + * Merges a set of requirements together, starting with an empty requirement. + * + * @param allRequirements Requirements to merge + * @returns Merged requirements + */ + }, { + key: "maximize", + value: + /** + * Runs maximizeCached, using the maximizeParameters and maximizeOptions contained by this requirement. + * + * @returns Whether the maximize call succeeded. + */ + function maximize() { + return maximizeCached(this.maximizeParameters, this.maximizeOptions); + } + /** + * Merges requirements, and then runs maximizeCached on the combined requirement. + * + * @param requirements Requirements to maximize on + */ + }], [{ + key: "merge", + value: function merge(allRequirements) { + return allRequirements.reduce((x, y) => x.merge(y), new Requirement([], {})); + } + }, { + key: "maximize", + value: function maximize() { + for (var _len = arguments.length, requirements = new Array(_len), _key = 0; _key < _len; _key++) { + requirements[_key] = arguments[_key]; + } + Requirement.merge(requirements).maximize(); + } + }]); + return Requirement; +}(); +/** + * Clear all outfits cached by the maximizer. + */ +function clearMaximizerCache() { + outfitCache.clear(); + for (var member in cachedObjectives) delete cachedObjectives[member]; +} +;// CONCATENATED MODULE: ./node_modules/grimoire-kolmafia/dist/outfit.js +var outfit_templateObject, outfit_templateObject2, outfit_templateObject3, outfit_templateObject4, outfit_templateObject5, outfit_templateObject6, outfit_templateObject7, outfit_templateObject8, outfit_templateObject9, outfit_templateObject10, outfit_templateObject11, outfit_templateObject12, outfit_templateObject13, outfit_templateObject14, outfit_templateObject15, outfit_templateObject16, outfit_templateObject17, outfit_templateObject18, outfit_templateObject19, outfit_templateObject20, outfit_templateObject21, outfit_templateObject22, outfit_templateObject23, outfit_templateObject24, outfit_templateObject25, outfit_templateObject26, outfit_templateObject27, outfit_templateObject28, outfit_templateObject29, outfit_templateObject30, outfit_templateObject31, outfit_templateObject32, outfit_templateObject33, outfit_templateObject34, outfit_templateObject35, outfit_templateObject36, outfit_templateObject37, outfit_templateObject38, outfit_templateObject39, outfit_templateObject40, outfit_templateObject41, outfit_templateObject42, outfit_templateObject43, outfit_templateObject44, outfit_templateObject45, outfit_templateObject46, outfit_templateObject47, outfit_templateObject48, outfit_templateObject49, _templateObject50, _templateObject51, _templateObject52, _templateObject53, _templateObject54, _templateObject55, _templateObject56, _templateObject57, _templateObject58, _templateObject59, _templateObject60, _templateObject61, _templateObject62, _templateObject63, _templateObject64, _templateObject65, _templateObject66, _templateObject67; +function outfit_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function outfit_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? outfit_ownKeys(Object(t), !0).forEach(function (r) { outfit_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : outfit_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function outfit_defineProperty(obj, key, value) { key = outfit_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function outfit_slicedToArray(arr, i) { return outfit_arrayWithHoles(arr) || outfit_iterableToArrayLimit(arr, i) || outfit_unsupportedIterableToArray(arr, i) || outfit_nonIterableRest(); } +function outfit_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function outfit_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function outfit_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function outfit_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = outfit_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function outfit_taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } +function outfit_toConsumableArray(arr) { return outfit_arrayWithoutHoles(arr) || outfit_iterableToArray(arr) || outfit_unsupportedIterableToArray(arr) || outfit_nonIterableSpread(); } +function outfit_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function outfit_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return outfit_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return outfit_arrayLikeToArray(o, minLen); } +function outfit_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function outfit_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return outfit_arrayLikeToArray(arr); } +function outfit_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function outfit_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function outfit_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, outfit_toPropertyKey(descriptor.key), descriptor); } } +function outfit_createClass(Constructor, protoProps, staticProps) { if (protoProps) outfit_defineProperties(Constructor.prototype, protoProps); if (staticProps) outfit_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function outfit_toPropertyKey(arg) { var key = outfit_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function outfit_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } + + +var FORCE_REFRESH_REQUIREMENT = new Requirement([], { + forceUpdate: true +}); +var outfitSlots = ["hat", "back", "weapon", "offhand", "shirt", "pants", "acc1", "acc2", "acc3", "famequip"]; +var riderSlots = (/* unused pure expression or super */ null && (["buddy-bjorn", "crown-of-thrones"])); +var weaponHands = i => i ? (0,external_kolmafia_.weaponHands)(i) : 0; +var outfit_modeableCommands = ["backupcamera", "umbrella", "snowsuit", "edpiece", "retrocape", "parka"]; +var Outfit = /*#__PURE__*/function () { + function Outfit() { + outfit_classCallCheck(this, Outfit); + this.equips = new Map(); + this.riders = new Map(); + this.modes = {}; + this.skipDefaults = false; + this.modifier = []; + this.avoid = []; + this.bonuses = new Map(); + this.postActions = []; + this.preActions = []; + } + /** + * Create an outfit from your current player state. + */ + outfit_createClass(Outfit, [{ + key: "equippedAmount", + value: + /** + * Check how many of an item is equipped on the outfit. + */ + function equippedAmount(item) { + return outfit_toConsumableArray(this.equips.values()).filter(i => i === item).length; + } + }, { + key: "isAvailable", + value: function isAvailable(item) { + var _a; + if ((_a = this.avoid) === null || _a === void 0 ? void 0 : _a.includes(item)) return false; + if (!have(item, this.equippedAmount(item) + 1)) return false; + if ((0,external_kolmafia_.booleanModifier)(item, "Single Equip") && this.equippedAmount(item) > 0) return false; + return true; + } + /** + * Check whether an item is equipped on the outfit, optionally in a specific slot. + */ + }, { + key: "haveEquipped", + value: function haveEquipped(item, slot) { + if (slot === undefined) return this.equippedAmount(item) > 0; + return this.equips.get(slot) === item; + } + }, { + key: "equipItemNone", + value: function equipItemNone(item, slot) { + if (item !== template_string_$item.none) return false; + if (slot === undefined) return true; + if (this.equips.has(slot)) return false; + this.equips.set(slot, item); + return true; + } + }, { + key: "equipNonAccessory", + value: function equipNonAccessory(item, slot) { + if ($slots(outfit_templateObject || (outfit_templateObject = outfit_taggedTemplateLiteral(["acc1, acc2, acc3"]))).includes((0,external_kolmafia_.toSlot)(item))) return false; + if (slot !== undefined && slot !== (0,external_kolmafia_.toSlot)(item)) return false; + if (this.equips.has((0,external_kolmafia_.toSlot)(item))) return false; + switch ((0,external_kolmafia_.toSlot)(item)) { + case $slot(outfit_templateObject2 || (outfit_templateObject2 = outfit_taggedTemplateLiteral(["off-hand"]))): + if (this.equips.has($slot(outfit_templateObject3 || (outfit_templateObject3 = outfit_taggedTemplateLiteral(["weapon"])))) && weaponHands(this.equips.get($slot(outfit_templateObject4 || (outfit_templateObject4 = outfit_taggedTemplateLiteral(["weapon"]))))) !== 1) { + return false; + } + break; + case $slot(outfit_templateObject5 || (outfit_templateObject5 = outfit_taggedTemplateLiteral(["familiar"]))): + if (this.familiar !== undefined && !(0,external_kolmafia_.canEquip)(this.familiar, item)) return false; + } + if ((0,external_kolmafia_.toSlot)(item) !== $slot(outfit_templateObject6 || (outfit_templateObject6 = outfit_taggedTemplateLiteral(["familiar"]))) && !(0,external_kolmafia_.canEquip)(item)) return false; + this.equips.set((0,external_kolmafia_.toSlot)(item), item); + return true; + } + }, { + key: "equipAccessory", + value: function equipAccessory(item, slot) { + if (![undefined].concat(outfit_toConsumableArray($slots(outfit_templateObject7 || (outfit_templateObject7 = outfit_taggedTemplateLiteral(["acc1, acc2, acc3"]))))).includes(slot)) return false; + if ((0,external_kolmafia_.toSlot)(item) !== $slot(outfit_templateObject8 || (outfit_templateObject8 = outfit_taggedTemplateLiteral(["acc1"])))) return false; + if (!(0,external_kolmafia_.canEquip)(item)) return false; + if (slot === undefined) { + // We don't care which of the accessory slots we equip in + var empty = $slots(outfit_templateObject9 || (outfit_templateObject9 = outfit_taggedTemplateLiteral(["acc1, acc2, acc3"]))).find(s => !this.equips.has(s)); + if (empty === undefined) return false; + this.equips.set(empty, item); + } else { + if (this.equips.has(slot)) return false; + this.equips.set(slot, item); + } + return true; + } + }, { + key: "equipUsingDualWield", + value: function equipUsingDualWield(item, slot) { + if (![undefined, $slot(outfit_templateObject10 || (outfit_templateObject10 = outfit_taggedTemplateLiteral(["off-hand"])))].includes(slot)) return false; + if ((0,external_kolmafia_.toSlot)(item) !== $slot(outfit_templateObject11 || (outfit_templateObject11 = outfit_taggedTemplateLiteral(["weapon"])))) return false; + if (this.equips.has($slot(outfit_templateObject12 || (outfit_templateObject12 = outfit_taggedTemplateLiteral(["weapon"])))) && weaponHands(this.equips.get($slot(outfit_templateObject13 || (outfit_templateObject13 = outfit_taggedTemplateLiteral(["weapon"]))))) !== 1) { + return false; + } + if (this.equips.has($slot(outfit_templateObject14 || (outfit_templateObject14 = outfit_taggedTemplateLiteral(["off-hand"]))))) return false; + if (!have(template_string_$skill(outfit_templateObject15 || (outfit_templateObject15 = outfit_taggedTemplateLiteral(["Double-Fisted Skull Smashing"]))))) return false; + if (weaponHands(item) !== 1) return false; + if (!(0,external_kolmafia_.canEquip)(item)) return false; + this.equips.set($slot(outfit_templateObject16 || (outfit_templateObject16 = outfit_taggedTemplateLiteral(["off-hand"]))), item); + return true; + } + }, { + key: "getHoldingFamiliar", + value: function getHoldingFamiliar(item) { + switch ((0,external_kolmafia_.toSlot)(item)) { + case $slot(outfit_templateObject17 || (outfit_templateObject17 = outfit_taggedTemplateLiteral(["weapon"]))): + return template_string_$familiar(outfit_templateObject18 || (outfit_templateObject18 = outfit_taggedTemplateLiteral(["Disembodied Hand"]))); + case $slot(outfit_templateObject19 || (outfit_templateObject19 = outfit_taggedTemplateLiteral(["off-hand"]))): + return template_string_$familiar(outfit_templateObject20 || (outfit_templateObject20 = outfit_taggedTemplateLiteral(["Left-Hand Man"]))); + default: + return undefined; + } + } + /** + * Returns the bonus value associated with a given item. + * + * @param item The item to check the bonus of. + * @returns The bonus assigned to that item. + */ + }, { + key: "getBonus", + value: function getBonus(item) { + var _a; + return (_a = this.bonuses.get(item)) !== null && _a !== void 0 ? _a : 0; + } + /** + * Applies a value to any existing bonus this item has, using a rule assigned by the `reducer` parameter + * + * @param item The item to try to apply a bonus to. + * @param value The value to try to apply. + * @param reducer Function that combines new and current bonus + * @returns The total assigned bonus to that item. + */ + }, { + key: "applyBonus", + value: function applyBonus(item, value, reducer) { + var previous = this.getBonus(item); + return this.setBonus(item, reducer(value, previous)); + } + /** + * Sets the bonus value of an item equal to a given value, overriding any current bonus assigned. + * + * @param item The item to try to apply a bonus to. + * @param value The value to try to apply. + * @returns The total assigned bonus to that item. + */ + }, { + key: "setBonus", + value: function setBonus(item, value) { + this.bonuses.set(item, value); + return value; + } + /** + * Adds a value to any existing bonus this item has + * + * @param item The item to try to add a bonus to. + * @param value The value to try to add. + * @returns The total assigned bonus to that item. + */ + }, { + key: "addBonus", + value: function addBonus(item, value) { + return this.applyBonus(item, value, (a, b) => a + b); + } + /** + * Apply the given items' bonuses to the outfit, using a rule given by the reducer + * + * @param items A map containing items and their bonuses + * @param reducer A way of combining new bonuses with existing bonuses + */ + }, { + key: "applyBonuses", + value: function applyBonuses(items, reducer) { + var _iterator = outfit_createForOfIteratorHelper(items), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var _step$value = outfit_slicedToArray(_step.value, 2), + item = _step$value[0], + value = _step$value[1]; + this.applyBonus(item, value, reducer); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } + /** + * Sets the bonuses of the given items, overriding existing bonuses + * + * @param items Map containing items and bonuses + */ + }, { + key: "setBonuses", + value: function setBonuses(items) { + this.applyBonuses(items, a => a); + } + /** + * Adds the bonuses of the given items to any existing bonuses they ahave + * + * @param items Map containing items and bonuses + */ + }, { + key: "addBonuses", + value: function addBonuses(items) { + this.applyBonuses(items, (a, b) => a + b); + } + }, { + key: "equipUsingFamiliar", + value: function equipUsingFamiliar(item, slot) { + if (![undefined, $slot(outfit_templateObject21 || (outfit_templateObject21 = outfit_taggedTemplateLiteral(["familiar"])))].includes(slot)) return false; + if (this.equips.has($slot(outfit_templateObject22 || (outfit_templateObject22 = outfit_taggedTemplateLiteral(["familiar"]))))) return false; + if ((0,external_kolmafia_.booleanModifier)(item, "Single Equip")) return false; + var familiar = this.getHoldingFamiliar(item); + if (familiar === undefined || !this.equip(familiar)) return false; + this.equips.set($slot(outfit_templateObject23 || (outfit_templateObject23 = outfit_taggedTemplateLiteral(["familiar"]))), item); + return true; + } + }, { + key: "equipItem", + value: function equipItem(item, slot) { + return this.haveEquipped(item, slot) || this.equipItemNone(item, slot) || this.isAvailable(item) && (this.equipNonAccessory(item, slot) || this.equipAccessory(item, slot) || this.equipUsingDualWield(item, slot) || this.equipUsingFamiliar(item, slot)); + } + }, { + key: "equipFamiliar", + value: function equipFamiliar(familiar) { + if (familiar === this.familiar) return true; + if (this.familiar !== undefined) return false; + if (familiar !== template_string_$familiar.none) { + if (!have(familiar)) return false; + if (Array.from(this.riders.values()).includes(familiar)) return false; + } + var item = this.equips.get($slot(outfit_templateObject24 || (outfit_templateObject24 = outfit_taggedTemplateLiteral(["familiar"])))); + if (item !== undefined && item !== template_string_$item.none && !(0,external_kolmafia_.canEquip)(familiar, item)) return false; + this.familiar = familiar; + return true; + } + }, { + key: "equipSpec", + value: function equipSpec(spec) { + var _this$avoid; + var _a, _b, _c, _d, _e, _f; + var succeeded = true; + for (var _i = 0, _outfitSlots = outfitSlots; _i < _outfitSlots.length; _i++) { + var slotName = _outfitSlots[_i]; + var slot = (_a = new Map([["famequip", $slot(outfit_templateObject25 || (outfit_templateObject25 = outfit_taggedTemplateLiteral(["familiar"])))], ["offhand", $slot(outfit_templateObject26 || (outfit_templateObject26 = outfit_taggedTemplateLiteral(["off-hand"])))]]).get(slotName)) !== null && _a !== void 0 ? _a : (0,external_kolmafia_.toSlot)(slotName); + var itemOrItems = spec[slotName]; + if (itemOrItems !== undefined && !this.equip(itemOrItems, slot)) succeeded = false; + } + var _iterator2 = outfit_createForOfIteratorHelper((_b = spec === null || spec === void 0 ? void 0 : spec.equip) !== null && _b !== void 0 ? _b : []), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var item = _step2.value; + if (!this.equip(item)) succeeded = false; + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + if ((spec === null || spec === void 0 ? void 0 : spec.familiar) !== undefined) { + if (!this.equip(spec.familiar)) succeeded = false; + } + (_this$avoid = this.avoid).push.apply(_this$avoid, outfit_toConsumableArray((_c = spec === null || spec === void 0 ? void 0 : spec.avoid) !== null && _c !== void 0 ? _c : [])); + this.skipDefaults = this.skipDefaults || ((_d = spec.skipDefaults) !== null && _d !== void 0 ? _d : false); + if (spec.modifier) { + var _this$modifier; + if (Array.isArray(spec.modifier)) (_this$modifier = this.modifier).push.apply(_this$modifier, outfit_toConsumableArray(spec.modifier));else this.modifier.push(spec.modifier); + } + if (spec.modes) { + if (!this.setModes(spec.modes)) { + succeeded = false; + } + } + if (spec.riders) { + if (spec.riders["buddy-bjorn"] && !this.bjornify(spec.riders["buddy-bjorn"])) succeeded = false; + if (spec.riders["crown-of-thrones"] && !this.enthrone(spec.riders["crown-of-thrones"])) succeeded = false; + } + if (spec.bonuses) { + this.addBonuses(spec.bonuses); + } + this.beforeDress.apply(this, outfit_toConsumableArray((_e = spec.beforeDress) !== null && _e !== void 0 ? _e : [])); + this.afterDress.apply(this, outfit_toConsumableArray((_f = spec.afterDress) !== null && _f !== void 0 ? _f : [])); + return succeeded; + } + /** + * Equip the first thing that can be equipped to the outfit. + * + * @param things The things to equip. + * @param slot The slot to equip them. + * @returns True if one of the things is equipped, and false otherwise. + */ + }, { + key: "equipFirst", + value: function equipFirst(things, slot) { + // some() returns false on an empty array, yet every() returns true. + // This keeps behavior consistent between slotful and slotless equipping. + if (things.length === 0) return true; + return things.some(val => this.equip(val, slot)); + } + /** + * Equip a thing to the outfit. + * + * If no slot is given, then the thing will be equipped wherever possible + * (possibly using dual-wielding, any of the accessory slots, or as + * familiar equipment). If it is impossible to add this thing anywhere to + * the outfit, this function will return false. + * + * If a slot is given, the item will be equipped only in that slot. If the + * slot is filled with a different item, this function will return false. + * + * If the thing is already equipped in the provided slot, or if no slot is + * given and the thing is already equipped in any slot, this function will + * return true and not change the outfit. + * + * @param thing The thing or things to equip. + * @param slot The slot to equip them. + * @returns True if the thing was sucessfully equipped, and false otherwise. + */ + }, { + key: "equip", + value: function equip(thing, slot) { + if (Array.isArray(thing)) { + if (slot !== undefined) return this.equipFirst(thing, slot); + return thing.every(val => this.equip(val)); + } + if (thing instanceof external_kolmafia_.Item) return this.equipItem(thing, slot); + if (thing instanceof external_kolmafia_.Familiar) return this.equipFamiliar(thing); + if (thing instanceof Outfit) return this.equipSpec(thing.spec()); + return this.equipSpec(thing); + } + }, { + key: "bjornify", + value: + /** + * Add a bjornified familiar to the outfit. + * + * This function does *not* equip the buddy bjorn itself; it must be equipped separately. + * + * If a familiar is already specified for the buddy bjorn that is different from the provided target, this function will return false and not change the buddy bjorn. + * @param target The familiar to bjornify, or a ranked list of familiars to try to bjornify. + * @returns True if we successfully set the bjorn to a valid target. + */ + function bjornify(target) { + var current = this.riders.get($slot(outfit_templateObject27 || (outfit_templateObject27 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))); + if (current) { + if (Array.isArray(target) ? target.includes(current) : current === target) { + return true; + } + return false; + } + if (Array.isArray(target)) { + var fam = target.find(f => have(f) && this.familiar !== f && this.riders.get($slot(outfit_templateObject28 || (outfit_templateObject28 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))) !== f); + if (fam) { + this.riders.set($slot(outfit_templateObject29 || (outfit_templateObject29 = outfit_taggedTemplateLiteral(["buddy-bjorn"]))), fam); + return true; + } + return false; + } else { + if (have(target) && this.familiar !== target && !Array.from(this.riders.values()).includes(target)) { + this.riders.set($slot(outfit_templateObject30 || (outfit_templateObject30 = outfit_taggedTemplateLiteral(["buddy-bjorn"]))), target); + return true; + } + return false; + } + } + /** + * Add anenthroned familiar to the outfit. + * + * This function does *not* equip the crown of thrones itself; it must be equipped separately. + * + * If a familiar is already specified for the crown of thrones that is different from the provided target, this function will return false and not change the crown of thrones. + * @param target The familiar to enthrone, or a ranked list of familiars to try to enthrone. + * @returns True if we successfully set the enthrone to a valid target. + */ + }, { + key: "enthrone", + value: function enthrone(target) { + var current = this.riders.get($slot(outfit_templateObject31 || (outfit_templateObject31 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))); + if (current) { + if (Array.isArray(target) ? target.includes(current) : current === target) { + return true; + } + return false; + } + if (Array.isArray(target)) { + var fam = target.find(f => have(f) && this.familiar !== f && this.riders.get($slot(outfit_templateObject32 || (outfit_templateObject32 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))) !== f); + if (fam) { + this.riders.set($slot(outfit_templateObject33 || (outfit_templateObject33 = outfit_taggedTemplateLiteral(["crown-of-thrones"]))), fam); + return true; + } + return false; + } else { + if (have(target) && this.familiar !== target && !Array.from(this.riders.values()).includes(target)) { + this.riders.set($slot(outfit_templateObject34 || (outfit_templateObject34 = outfit_taggedTemplateLiteral(["crown-of-thrones"]))), target); + return true; + } + return false; + } + } + /** + * Set the provided modes for items that may be equipped in the outfit. + * + * This function does *not* equip items for the set modes; they must be + * equipped separately. + * + * If a mode is already set for an item that is different from the provided + * mode, this function will return false and not change the mode for that + * item. (But other modes might still be changed if they are compatible.) + * + * Note that the superhero and instuctions of a retrocape can be set + * independently (`undefined` is treated as "don't care"). + * + * @param modes Modes to set in this outfit. + * @returns True if all modes were sucessfully set, and false otherwise. + */ + }, { + key: "setModes", + value: function setModes(modes) { + var _a, _b; + var compatible = true; + // Check if the new modes are compatible with existing modes + for (var _i2 = 0, _modeableCommands = outfit_modeableCommands; _i2 < _modeableCommands.length; _i2++) { + var mode = _modeableCommands[_i2]; + if (mode === "retrocape") continue; // checked below + if (this.modes[mode] && modes[mode] && this.modes[mode] !== modes[mode]) { + compatible = false; + } + } + // Check if retrocape modes are compatible + // (Parts that are undefined are compatible with everything) + if (this.modes["retrocape"] && modes["retrocape"]) { + if (this.modes["retrocape"][0] && modes["retrocape"][0] && this.modes["retrocape"][0] !== modes["retrocape"][0]) { + compatible = false; + } + if (this.modes["retrocape"][1] && modes["retrocape"][1] && this.modes["retrocape"][1] !== modes["retrocape"][1]) { + compatible = false; + } + this.modes["retrocape"][0] = (_a = this.modes["retrocape"][0]) !== null && _a !== void 0 ? _a : modes["retrocape"][0]; + this.modes["retrocape"][1] = (_b = this.modes["retrocape"][1]) !== null && _b !== void 0 ? _b : modes["retrocape"][1]; + } + this.modes = outfit_objectSpread(outfit_objectSpread({}, modes), this.modes); + return compatible; + } + /** + * Check if it is possible to equip a thing to this outfit using .equip(). + * + * This does not change the current outfit. + * + * @param thing The thing to equip. + * @param slot The slot to equip them. + * @returns True if this thing can be equipped. + */ + }, { + key: "canEquip", + value: function canEquip(thing, slot) { + var outfit = this.clone(); + return outfit.equip(thing, slot); + } + /** + * Check if it is possible to equip a thing to this outfit using .equip(); if it is, do so. + * + * This does change the current outfit. + * @param thing The thing to equip. + * @param slot The slot to equip them. + * @returns True if this thing was successfully equipped. + */ + }, { + key: "tryEquip", + value: function tryEquip(thing, slot) { + return this.canEquip(thing, slot) && this.equip(thing, slot); + } + }, { + key: "afterDress", + value: function afterDress() { + var _this$postActions; + (_this$postActions = this.postActions).push.apply(_this$postActions, arguments); + } + }, { + key: "beforeDress", + value: function beforeDress() { + var _this$preActions; + (_this$preActions = this.preActions).push.apply(_this$preActions, arguments); + } + /** + * Equip this outfit. + */ + }, { + key: "_dress", + value: function _dress(refreshed) { + if (this.familiar) (0,external_kolmafia_.useFamiliar)(this.familiar); + var targetEquipment = Array.from(this.equips.values()); + var usedSlots = new Set(); + // First, we equip non-accessory equipment. + var nonaccessorySlots = $slots(outfit_templateObject35 || (outfit_templateObject35 = outfit_taggedTemplateLiteral(["weapon, off-hand, hat, back, shirt, pants, familiar"]))); + var bjorn = this.riders.get($slot(outfit_templateObject36 || (outfit_templateObject36 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))); + if (bjorn && (this.equips.get($slot(outfit_templateObject37 || (outfit_templateObject37 = outfit_taggedTemplateLiteral(["back"])))) === template_string_$item(outfit_templateObject38 || (outfit_templateObject38 = outfit_taggedTemplateLiteral(["Buddy Bjorn"]))) || this.getBonus(template_string_$item(outfit_templateObject39 || (outfit_templateObject39 = outfit_taggedTemplateLiteral(["Buddy Bjorn"])))))) { + usedSlots.add($slot(outfit_templateObject40 || (outfit_templateObject40 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))); + usedSlots.add($slot(outfit_templateObject41 || (outfit_templateObject41 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))); + } + var crown = this.riders.get($slot(outfit_templateObject42 || (outfit_templateObject42 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))); + if (crown && (this.equips.get($slot(outfit_templateObject43 || (outfit_templateObject43 = outfit_taggedTemplateLiteral(["hat"])))) === template_string_$item(outfit_templateObject44 || (outfit_templateObject44 = outfit_taggedTemplateLiteral(["Crown of Thrones"]))) || this.getBonus(template_string_$item(outfit_templateObject45 || (outfit_templateObject45 = outfit_taggedTemplateLiteral(["Crown of Thrones"])))))) { + usedSlots.add($slot(outfit_templateObject46 || (outfit_templateObject46 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))); + usedSlots.add($slot(outfit_templateObject47 || (outfit_templateObject47 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))); + } + // We must manually remove equipment that we want to use in a different + // slot than where it is currently equipped, to avoid a mafia issue. + // Order is anchored here to prevent DFSS shenanigans + var _iterator3 = outfit_createForOfIteratorHelper(nonaccessorySlots), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var slot = _step3.value; + if (targetEquipment.includes((0,external_kolmafia_.equippedItem)(slot)) && this.equips.get(slot) !== (0,external_kolmafia_.equippedItem)(slot) || this.avoid.includes((0,external_kolmafia_.equippedItem)(slot)) || slot === $slot(_templateObject55 || (_templateObject55 = outfit_taggedTemplateLiteral(["weapon"]))) && weaponHands((0,external_kolmafia_.equippedItem)(slot)) !== 1 && this.equips.has($slot(_templateObject56 || (_templateObject56 = outfit_taggedTemplateLiteral(["offhand"])))) && !this.equips.has($slot(_templateObject57 || (_templateObject57 = outfit_taggedTemplateLiteral(["weapon"]))))) (0,external_kolmafia_.equip)(slot, template_string_$item.none); + } + // Then we equip all the non-accessory equipment. + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + var _iterator4 = outfit_createForOfIteratorHelper(nonaccessorySlots), + _step4; + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var _slot = _step4.value; + var equipment = this.equips.get(_slot); + if (equipment) { + (0,external_kolmafia_.equip)(_slot, equipment); + usedSlots.add(_slot); + } + } + // Next, we equip accessories + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + var accessorySlots = $slots(outfit_templateObject48 || (outfit_templateObject48 = outfit_taggedTemplateLiteral(["acc1, acc2, acc3"]))); + var accessoryEquips = accessorySlots.map(slot => this.equips.get(slot)).filter(item => item !== undefined); + // To plan how to equip accessories, first check which accessories are + // already equipped in some accessory slot. There is no need to move them, + // since KoL doesn't care what order accessories are equipped in. + var missingAccessories = []; // accessories that are not already equipped + var _iterator5 = outfit_createForOfIteratorHelper(accessoryEquips), + _step5; + try { + var _loop = function _loop() { + var accessory = _step5.value; + var alreadyEquipped = accessorySlots.find(slot => !usedSlots.has(slot) && (0,external_kolmafia_.equippedItem)(slot) === accessory); + if (alreadyEquipped) { + usedSlots.add(alreadyEquipped); + } else { + missingAccessories.push(accessory); + } + }; + for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { + _loop(); + } + // Then, for all accessories that are not currently equipped, use the first + // open slot to place them. + } catch (err) { + _iterator5.e(err); + } finally { + _iterator5.f(); + } + for (var _i3 = 0, _missingAccessories = missingAccessories; _i3 < _missingAccessories.length; _i3++) { + var accessory = _missingAccessories[_i3]; + var unusedSlot = accessorySlots.find(slot => !usedSlots.has(slot)); + if (unusedSlot === undefined) { + // This should only occur if there is a bug in .dress() + throw "No accessory slots remaining"; + } + (0,external_kolmafia_.equip)(unusedSlot, accessory); + usedSlots.add(unusedSlot); + } + // Remaining slots are filled by the maximizer + var modes = convertToLibramModes(this.modes); + if (this.modifier.length > 0) { + var allRequirements = [new Requirement(this.modifier, { + preventSlot: outfit_toConsumableArray(usedSlots), + preventEquip: this.avoid, + modes: modes, + bonusEquip: this.bonuses + })]; + if (refreshed) allRequirements.push(FORCE_REFRESH_REQUIREMENT); + if (!Requirement.merge(allRequirements).maximize()) { + if (!refreshed) { + (0,external_kolmafia_.cliExecute)("refresh inventory"); + this._dress(true); + return; + } else throw new Error("Failed to maximize properly!"); + } + (0,external_kolmafia_.logprint)("Maximize: ".concat(this.modifier)); + } + // Set the modes of any equipped items. + applyModes(modes); + // Handle the rider slots next + if (bjorn && (0,external_kolmafia_.haveEquipped)(template_string_$item(outfit_templateObject49 || (outfit_templateObject49 = outfit_taggedTemplateLiteral(["Buddy Bjorn"]))))) { + if ((0,external_kolmafia_.myEnthronedFamiliar)() === bjorn) (0,external_kolmafia_.enthroneFamiliar)(template_string_$familiar.none); + if ((0,external_kolmafia_.myBjornedFamiliar)() !== bjorn) (0,external_kolmafia_.bjornifyFamiliar)(bjorn); + } + if (crown && (0,external_kolmafia_.haveEquipped)(template_string_$item(_templateObject50 || (_templateObject50 = outfit_taggedTemplateLiteral(["Crown of Thrones"]))))) { + if ((0,external_kolmafia_.myBjornedFamiliar)() === crown) (0,external_kolmafia_.bjornifyFamiliar)(template_string_$familiar.none); + if ((0,external_kolmafia_.myEnthronedFamiliar)() !== crown) (0,external_kolmafia_.enthroneFamiliar)(crown); + } + // Verify that all equipment was indeed equipped + if (this.familiar !== undefined && (0,external_kolmafia_.myFamiliar)() !== this.familiar) throw "Failed to fully dress (expected: familiar ".concat(this.familiar, ")"); + var _iterator6 = outfit_createForOfIteratorHelper(nonaccessorySlots), + _step6; + try { + for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { + var _slot2 = _step6.value; + if (this.equips.has(_slot2) && (0,external_kolmafia_.equippedItem)(_slot2) !== this.equips.get(_slot2)) { + throw "Failed to fully dress (expected: ".concat(_slot2, " ").concat(this.equips.get(_slot2), ")"); + } + } + } catch (err) { + _iterator6.e(err); + } finally { + _iterator6.f(); + } + var _iterator7 = outfit_createForOfIteratorHelper(accessoryEquips), + _step7; + try { + var _loop2 = function _loop2() { + var accessory = _step7.value; + if ((0,external_kolmafia_.equippedAmount)(accessory) < accessoryEquips.filter(acc => acc === accessory).length) { + throw "Failed to fully dress (expected: acc ".concat(accessory, ")"); + } + }; + for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { + _loop2(); + } + } catch (err) { + _iterator7.e(err); + } finally { + _iterator7.f(); + } + for (var _i4 = 0, _arr = [[$slot(_templateObject51 || (_templateObject51 = outfit_taggedTemplateLiteral(["buddy-bjorn"]))), template_string_$item(_templateObject52 || (_templateObject52 = outfit_taggedTemplateLiteral(["Buddy Bjorn"]))), external_kolmafia_.myBjornedFamiliar], [$slot(_templateObject53 || (_templateObject53 = outfit_taggedTemplateLiteral(["crown-of-thrones"]))), template_string_$item(_templateObject54 || (_templateObject54 = outfit_taggedTemplateLiteral(["Crown of Thrones"]))), external_kolmafia_.myEnthronedFamiliar]]; _i4 < _arr.length; _i4++) { + var _arr$_i = outfit_slicedToArray(_arr[_i4], 3), + rider = _arr$_i[0], + throne = _arr$_i[1], + checkingFunction = _arr$_i[2]; + var wanted = this.riders.get(rider); + if (outfit_toConsumableArray(this.equips.values()).includes(throne) && wanted && checkingFunction() !== wanted) { + throw "Failed to fully dress: (expected ".concat(rider, " ").concat(wanted, ")"); + } + } + } + }, { + key: "dress", + value: function dress() { + var _iterator8 = outfit_createForOfIteratorHelper(this.preActions), + _step8; + try { + for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) { + var action = _step8.value; + action(); + } + } catch (err) { + _iterator8.e(err); + } finally { + _iterator8.f(); + } + this._dress(false); + var _iterator9 = outfit_createForOfIteratorHelper(this.postActions), + _step9; + try { + for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) { + var _action = _step9.value; + _action(); + } + } catch (err) { + _iterator9.e(err); + } finally { + _iterator9.f(); + } + } + /** + * Build an Outfit identical to this outfit. + */ + }, { + key: "clone", + value: function clone() { + var result = new Outfit(); + result.equips = new Map(this.equips); + result.skipDefaults = this.skipDefaults; + result.familiar = this.familiar; + result.modifier = outfit_toConsumableArray(this.modifier); + result.avoid = outfit_toConsumableArray(this.avoid); + result.modes = outfit_objectSpread({}, this.modes); + result.riders = new Map(this.riders); + result.bonuses = new Map(this.bonuses); + result.beforeDress.apply(result, outfit_toConsumableArray(this.preActions)); + result.afterDress.apply(result, outfit_toConsumableArray(this.postActions)); + return result; + } + /** + * Build an OutfitSpec identical to this outfit. + */ + }, { + key: "spec", + value: function spec() { + var _a; + var result = { + modifier: outfit_toConsumableArray(this.modifier), + avoid: outfit_toConsumableArray(this.avoid), + skipDefaults: this.skipDefaults, + modes: outfit_objectSpread({}, this.modes), + bonuses: new Map(this.bonuses) + }; + if (this.familiar) result.familiar = this.familiar; + // Add all equipment forced in a particular slot + for (var _i5 = 0, _outfitSlots2 = outfitSlots; _i5 < _outfitSlots2.length; _i5++) { + var slotName = _outfitSlots2[_i5]; + var entry = this.equips.get((_a = new Map([["famequip", $slot(_templateObject58 || (_templateObject58 = outfit_taggedTemplateLiteral(["familiar"])))], ["offhand", $slot(_templateObject59 || (_templateObject59 = outfit_taggedTemplateLiteral(["off-hand"])))]]).get(slotName)) !== null && _a !== void 0 ? _a : (0,external_kolmafia_.toSlot)(slotName)); + if (entry) result[slotName] = entry; + } + // Include the riders + var riders = {}; + var buddyRider = this.riders.get($slot(_templateObject60 || (_templateObject60 = outfit_taggedTemplateLiteral(["buddy-bjorn"])))); + if (buddyRider !== undefined) riders["buddy-bjorn"] = buddyRider; + var throneRider = this.riders.get($slot(_templateObject61 || (_templateObject61 = outfit_taggedTemplateLiteral(["crown-of-thrones"])))); + if (throneRider !== undefined) riders["crown-of-thrones"] = throneRider; + if (buddyRider !== undefined || throneRider !== undefined) result.riders = riders; + if (this.preActions.length) result.beforeDress = this.preActions; + if (this.postActions.length) result.afterDress = this.postActions; + return result; + } + }], [{ + key: "current", + value: function current() { + var _a; + var outfit = new Outfit(); + var familiar = (0,external_kolmafia_.myFamiliar)(); + if (outfit.equip(familiar)) { + throw "Failed to create outfit from current state (expected: familiar ".concat(familiar, ")"); + } + for (var _i6 = 0, _outfitSlots3 = outfitSlots; _i6 < _outfitSlots3.length; _i6++) { + var slotName = _outfitSlots3[_i6]; + var slot = (_a = new Map([["famequip", $slot(_templateObject62 || (_templateObject62 = outfit_taggedTemplateLiteral(["familiar"])))], ["offhand", $slot(_templateObject63 || (_templateObject63 = outfit_taggedTemplateLiteral(["off-hand"])))]]).get(slotName)) !== null && _a !== void 0 ? _a : (0,external_kolmafia_.toSlot)(slotName); + var item = (0,external_kolmafia_.equippedItem)(slot); + if (!outfit.equip(item, slot)) { + throw "Failed to create outfit from current state (expected: ".concat(slot, " ").concat(item, ")"); + } + } + if ((0,external_kolmafia_.haveEquipped)(template_string_$item(_templateObject64 || (_templateObject64 = outfit_taggedTemplateLiteral(["Crown of Thrones"]))))) outfit.riders.set($slot(_templateObject65 || (_templateObject65 = outfit_taggedTemplateLiteral(["crown-of-thrones"]))), (0,external_kolmafia_.myEnthronedFamiliar)()); + if ((0,external_kolmafia_.haveEquipped)(template_string_$item(_templateObject66 || (_templateObject66 = outfit_taggedTemplateLiteral(["Buddy Bjorn"]))))) outfit.riders.set($slot(_templateObject67 || (_templateObject67 = outfit_taggedTemplateLiteral(["buddy-bjorn"]))), (0,external_kolmafia_.myBjornedFamiliar)()); + outfit.setModes(outfit_getCurrentModes()); + return outfit; + } + }, { + key: "from", + value: function from(spec) { + var error = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + var _a; + var outfit = new Outfit(); + if (spec instanceof Requirement) { + var result = {}; + result.modifier = spec.maximizeParameters; + if ((_a = spec.maximizeOptions.forceEquip) === null || _a === void 0 ? void 0 : _a.length) { + result.equip = spec.maximizeOptions.forceEquip; + } + result.avoid = spec.maximizeOptions.preventEquip; + result.bonuses = spec.maximizeOptions.bonusEquip; + if (spec.maximizeOptions.modes) { + result.modes = convertFromLibramModes(spec.maximizeOptions.modes); + } + // Not sure if this is necessary + var cleanedResult = Object.fromEntries(outfit_toConsumableArray(Object.entries(result)).filter(_ref => { + var _ref2 = outfit_slicedToArray(_ref, 2), + v = _ref2[1]; + return v !== undefined; + })); + return Outfit.from(cleanedResult); + } + var success = outfit.equip(spec); + if (!success && error) throw error; + return success ? outfit : null; + } + }]); + return Outfit; +}(); +/** + * Get the modes of this outfit in a type compatible with Libram. + * + * This conversion is needed since we store the retrocape modes + * internally as an array, but libram uses a string. + * + * @returns The modes equipped to this outfit. + */ +function convertToLibramModes(modes) { + var _a; + return { + backupcamera: modes["backupcamera"], + umbrella: modes["umbrella"], + snowsuit: modes["snowsuit"], + edpiece: modes["edpiece"], + retrocape: (_a = modes["retrocape"]) === null || _a === void 0 ? void 0 : _a.filter(s => s !== undefined).join(" "), + parka: modes["parka"] + }; +} +function convertFromLibramModes(modes) { + return modes.retrocape ? outfit_objectSpread(outfit_objectSpread({}, modes), {}, { + retrocape: modes.retrocape.split(" ") + }) : modes; +} +/** + * Get the current modes of all items. + * + * @returns The current mode settings for all items, equipped or not. + */ +function outfit_getCurrentModes() { + return { + backupcamera: getMode("backupCameraMode", ["ml", "meat", "init"]), + umbrella: getMode("umbrellaState", ["broken", "forward-facing", "bucket style", "pitchfork style", "constantly twirling", "cocoon"]), + snowsuit: getMode("snowsuit", ["eyebrows", "smirk", "nose", "goatee", "hat"]), + edpiece: getMode("edPiece", ["bear", "owl", "puma", "hyena", "mouse", "weasel", "fish"]), + retrocape: [getMode("retroCapeSuperhero", ["vampire", "heck", "robot"]), getMode("retroCapeWashingInstructions", ["hold", "thrill", "kiss", "kill"])], + parka: getMode("parkaMode", ["kachungasaur", "dilophosaur", "ghostasaurus", "spikolodon", "pterodactyl"]) + }; +} +/** + * Get the current value for a mode in a type-safe way. + * + * @param property The mafia property for the mode. + * @param options A typed list of options for the mode. + * @returns The mode if the property value matched a valid option, or undefined. + */ +function getMode(property, options) { + var val = property_get(property, ""); + return options.find(s => s === val); // .includes has type issues +} +;// CONCATENATED MODULE: ./node_modules/grimoire-kolmafia/dist/engine.js +var engine_templateObject; +function engine_taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } +function engine_toConsumableArray(arr) { return engine_arrayWithoutHoles(arr) || engine_iterableToArray(arr) || engine_unsupportedIterableToArray(arr) || engine_nonIterableSpread(); } +function engine_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function engine_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function engine_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return engine_arrayLikeToArray(arr); } +function engine_slicedToArray(arr, i) { return engine_arrayWithHoles(arr) || engine_iterableToArrayLimit(arr, i) || engine_unsupportedIterableToArray(arr, i) || engine_nonIterableRest(); } +function engine_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function engine_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function engine_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function engine_createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = engine_unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } +function engine_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return engine_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return engine_arrayLikeToArray(o, minLen); } +function engine_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function engine_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function engine_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? engine_ownKeys(Object(t), !0).forEach(function (r) { engine_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : engine_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function engine_defineProperty(obj, key, value) { key = engine_toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function engine_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, engine_toPropertyKey(descriptor.key), descriptor); } } +function engine_createClass(Constructor, protoProps, staticProps) { if (protoProps) engine_defineProperties(Constructor.prototype, protoProps); if (staticProps) engine_defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function engine_toPropertyKey(arg) { var key = engine_toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function engine_toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function engine_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + + + +var EngineOptions = /*#__PURE__*/(/* unused pure expression or super */ null && (engine_createClass(function EngineOptions() { + engine_classCallCheck(this, EngineOptions); +}))); +var grimoireCCS = "grimoire_macro"; +var Engine = /*#__PURE__*/function () { + /** + * Create the engine. + * @param tasks A list of tasks for looking up task dependencies. + * @param options Basic configuration of the engine. + */ + function Engine(tasks, options) { + engine_classCallCheck(this, Engine); + this.attempts = {}; + this.propertyManager = new PropertiesManager(); + this.tasks_by_name = new Map(); + this.cachedCcsContents = ""; + this.options = options !== null && options !== void 0 ? options : {}; + this.tasks = tasks.map(task => engine_objectSpread(engine_objectSpread({}, this.options.default_task_options), task)); + var _iterator = engine_createForOfIteratorHelper(this.tasks), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var task = _step.value; + this.tasks_by_name.set(task.name, task); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + this.initPropertiesManager(this.propertyManager); + } + /** + * Determine the next task to perform. + * By default, this is the first task in the task list that is available. + * @returns The next task to perform, or undefined if no tasks are available. + */ + engine_createClass(Engine, [{ + key: "getNextTask", + value: function getNextTask() { + return this.tasks.find(task => this.available(task)); + } + /** + * Continually get the next task and execute it. + * @param actions If given, only perform up to this many tasks. + */ + }, { + key: "run", + value: function run(actions) { + for (var i = 0; i < (actions !== null && actions !== void 0 ? actions : Infinity); i++) { + var task = this.getNextTask(); + if (!task) return; + this.execute(task); + } + } + /** + * Close the engine and reset all properties. + * After this has been called, this object should not be used. + */ + }, { + key: "destruct", + value: function destruct() { + this.propertyManager.resetAll(); + (0,external_kolmafia_.setAutoAttack)(0); + } + /** + * Check if the given task is available at this moment. + * @returns true if all dependencies are complete and the task is ready. + * Note that dependencies are not checked transitively. That is, if + * A depends on B which depends on C, then A is ready if B is complete + * (regardless of if C is complete or not). + */ + }, { + key: "available", + value: function available(task) { + var _a, _b; + if (((_a = task.limit) === null || _a === void 0 ? void 0 : _a.skip) !== undefined && this.attempts[task.name] >= task.limit.skip) return false; + var _iterator2 = engine_createForOfIteratorHelper((_b = task.after) !== null && _b !== void 0 ? _b : []), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var after = _step2.value; + var after_task = this.tasks_by_name.get(after); + if (after_task === undefined) throw "Unknown task dependency ".concat(after, " on ").concat(task.name); + if (!after_task.completed()) return false; + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + if (task.ready && !task.ready()) return false; + if (task.completed()) return false; + return true; + } + /** + * Perform all steps to execute the provided task. + * This is the main entry point for the Engine. + * @param task The current executing task. + */ + }, { + key: "execute", + value: function execute(task) { + var _a, _b, _c, _d, _e; + (0,external_kolmafia_.print)(""); + (0,external_kolmafia_.print)("Executing ".concat(task.name), "blue"); + // Determine the proper postcondition for after the task executes. + var postcondition = (_b = (_a = task.limit) === null || _a === void 0 ? void 0 : _a.guard) === null || _b === void 0 ? void 0 : _b.call(_a); + // Acquire any items and effects first, possibly for later execution steps. + this.acquireItems(task); + this.acquireEffects(task); + // Prepare the outfit, with resources. + var task_combat = (_d = (_c = task.combat) === null || _c === void 0 ? void 0 : _c.clone()) !== null && _d !== void 0 ? _d : new CombatStrategy(); + var outfit = this.createOutfit(task); + var task_resources = new CombatResources(); + this.customize(task, outfit, task_combat, task_resources); + this.dress(task, outfit); + // Prepare combat and choices + this.setCombat(task, task_combat, task_resources); + this.setChoices(task, this.propertyManager); + // Actually perform the task + var _iterator3 = engine_createForOfIteratorHelper(task_resources.all()), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var resource = _step3.value; + (_e = resource.prepare) === null || _e === void 0 ? void 0 : _e.call(resource); + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); + } + this.prepare(task); + this.do(task); + while (this.shouldRepeatAdv(task)) { + _set("lastEncounter", ""); + this.do(task); + } + this.post(task); + // Mark that we tried the task, and apply limits + this.markAttempt(task); + this.checkLimits(task, postcondition); + } + /** + * Acquire all items for the task. + * @param task The current executing task. + */ + }, { + key: "acquireItems", + value: function acquireItems(task) { + var _a; + var acquire = undelay(task.acquire); + var _iterator4 = engine_createForOfIteratorHelper(acquire || []), + _step4; + try { + for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { + var to_get = _step4.value; + var num_needed = (_a = to_get.num) !== null && _a !== void 0 ? _a : 1; + var num_have = (0,external_kolmafia_.itemAmount)(to_get.item) + (0,external_kolmafia_.equippedAmount)(to_get.item); + if (num_needed <= num_have) continue; + if (to_get.useful !== undefined && !to_get.useful()) continue; + if (to_get.get) { + to_get.get(); + } else if (to_get.price !== undefined) { + (0,external_kolmafia_.buy)(to_get.item, num_needed - num_have, to_get.price); + } else if (Object.keys((0,external_kolmafia_.getRelated)(to_get.item, "fold")).length > 0) { + (0,external_kolmafia_.cliExecute)("fold ".concat(to_get.item)); + } else { + (0,external_kolmafia_.retrieveItem)(to_get.item, num_needed); + } + if ((0,external_kolmafia_.itemAmount)(to_get.item) + (0,external_kolmafia_.equippedAmount)(to_get.item) < num_needed && !to_get.optional) { + throw "Task ".concat(task.name, " was unable to acquire ").concat(num_needed, " ").concat(to_get.item); + } + } + } catch (err) { + _iterator4.e(err); + } finally { + _iterator4.f(); + } + } + /** + * Acquire all effects for the task. + * @param task The current executing task. + */ + }, { + key: "acquireEffects", + value: function acquireEffects(task) { + var _a; + var effects = (_a = undelay(task.effects)) !== null && _a !== void 0 ? _a : []; + var songs = effects.filter(effect => isSong(effect)); + if (songs.length > maxSongs()) throw "Too many AT songs"; + var extraSongs = Object.keys((0,external_kolmafia_.myEffects)()).map(effectName => (0,external_kolmafia_.toEffect)(effectName)).filter(effect => isSong(effect) && !songs.includes(effect)); + while (songs.length + extraSongs.length > maxSongs()) { + var toRemove = extraSongs.pop(); + if (toRemove === undefined) { + break; + } else { + uneffect(toRemove); + } + } + var _iterator5 = engine_createForOfIteratorHelper(effects), + _step5; + try { + for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { + var effect = _step5.value; + ensureEffect(effect); + } + } catch (err) { + _iterator5.e(err); + } finally { + _iterator5.f(); + } + } + /** + * Create an outfit for the task with all required equipment. + * @param task The current executing task. + */ + }, { + key: "createOutfit", + value: function createOutfit(task) { + var spec = undelay(task.outfit); + if (spec instanceof Outfit) return spec.clone(); + var outfit = new Outfit(); + if (spec !== undefined) { + if (!outfit.equip(spec) && !this.options.allow_partial_outfits) { + throw "Unable to equip all items for ".concat(task.name); + } + } + return outfit; + } + /** + * Equip the outfit for the task. + * @param task The current executing task. + * @param outfit The outfit for the task, possibly augmented by the engine. + */ + }, { + key: "dress", + value: function dress(task, outfit) { + if (task.do instanceof external_kolmafia_.Location) (0,external_kolmafia_.setLocation)(task.do); + outfit.dress(); + } + /* eslint-disable @typescript-eslint/no-unused-vars */ + /** + * Perform any engine-specific customization for the outfit and combat plan. + * + * This is a natural method to override in order to: + * * Enable the use of any resources in the outfit or combat (e.g., allocate banishers). + * * Equip a default outfit. + * * Determine additional monster macros at a global level (e.g., use flyers). + * @param task The current executing task. + * @param outfit The outfit for the task. + * @param combat The combat strategy so far for the task. + * @param resources The combat resources assigned so far for the task. + */ + }, { + key: "customize", + value: function customize(task, outfit, combat, resources) { + // do nothing by default + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /** + * Set the choice settings for the task. + * @param task The current executing task. + * @param manager The property manager to use. + */ + }, { + key: "setChoices", + value: function setChoices(task, manager) { + var _a; + for (var _i = 0, _Object$entries = Object.entries((_a = task.choices) !== null && _a !== void 0 ? _a : {}); _i < _Object$entries.length; _i++) { + var _Object$entries$_i = engine_slicedToArray(_Object$entries[_i], 2), + key = _Object$entries$_i[0], + func = _Object$entries$_i[1]; + if (func === undefined) continue; + manager.setChoice(parseInt(key), undelay(func)); + } + } + /** + * Save the combat macro for this task. + * @param task The current executing task. + * @param task_combat The completed combat strategy far for the task. + * @param task_resources The combat resources assigned for the task. + */ + }, { + key: "setCombat", + value: function setCombat(task, task_combat, task_resources) { + var _a; + // Save regular combat macro + var macro = task_combat.compile(task_resources, (_a = this.options) === null || _a === void 0 ? void 0 : _a.combat_defaults, task.do instanceof external_kolmafia_.Location ? task.do : undefined); + macro.save(); + if (!this.options.ccs) { + // Use the macro through a CCS file + var otherCCSEntries = task_combat.compileCcs(); + var ccsContents = ["[default]", "\"".concat(macro.toString(), "\"")].concat(engine_toConsumableArray(otherCCSEntries)).join("\n"); + // Log Macro + other CCS + (0,external_kolmafia_.logprint)("CCS: ".concat(ccsContents.replace("\n", "\\n "))); + if (ccsContents !== this.cachedCcsContents) { + (0,external_kolmafia_.writeCcs)(ccsContents, grimoireCCS); + (0,external_kolmafia_.cliExecute)("ccs ".concat(grimoireCCS)); // force Mafia to reparse the ccs + this.cachedCcsContents = ccsContents; + } + } + // Save autoattack combat macro + var autoattack = task_combat.compileAutoattack(); + if (autoattack.toString().length > 1) { + (0,external_kolmafia_.logprint)("Autoattack macro: ".concat(autoattack.toString())); + autoattack.setAutoAttack(); + } else { + (0,external_kolmafia_.setAutoAttack)(0); + } + } + /** + * Do any task-specific preparation. + * @param task The current executing task. + */ + }, { + key: "prepare", + value: function prepare(task) { + var _a; + (_a = task.prepare) === null || _a === void 0 ? void 0 : _a.call(task); + } + /** + * Actually perform the task. + * @param task The current executing task. + */ + }, { + key: "do", + value: function _do(task) { + var result = typeof task.do === "function" ? task.do() : task.do; + if (result instanceof external_kolmafia_.Location) (0,external_kolmafia_.adv1)(result, -1, ""); + (0,external_kolmafia_.runCombat)(); + while ((0,external_kolmafia_.inMultiFight)()) (0,external_kolmafia_.runCombat)(); + if ((0,external_kolmafia_.choiceFollowsFight)()) (0,external_kolmafia_.runChoice)(-1); + } + /** + * Check if the task.do should be immediately repeated without any prep. + * + * By default, this is only used to repeat a task if we hit one of: + * 1. Halloweener dog noncombats, + * 2. June cleaver noncombats, + * 3. Lil' Doctor™ bag noncombat, or + * 4. Turtle taming noncombats. + * @param task The current executing task. + * @returns True if the task should be immediately repeated. + */ + }, { + key: "shouldRepeatAdv", + value: function shouldRepeatAdv(task) { + return task.do instanceof external_kolmafia_.Location && lastEncounterWasWanderingNC(); + } + /** + * Do any task-specific wrapup activities. + * @param task The current executing task. + */ + }, { + key: "post", + value: function post(task) { + var _a; + (_a = task.post) === null || _a === void 0 ? void 0 : _a.call(task); + } + /** + * Mark that an attempt was made on the current task. + * @param task The current executing task. + */ + }, { + key: "markAttempt", + value: function markAttempt(task) { + if (!(task.name in this.attempts)) this.attempts[task.name] = 0; + this.attempts[task.name]++; + } + /** + * Check if the task has passed any of its internal limits. + * @param task The task to check. + * @throws An error if any of the internal limits have been passed. + */ + }, { + key: "checkLimits", + value: function checkLimits(task, postcondition) { + var _a; + if (!task.limit) return; + var failureMessage = task.limit.message ? " ".concat(task.limit.message) : ""; + if (!task.completed()) { + if (task.limit.tries && this.attempts[task.name] >= task.limit.tries) throw "Task ".concat(task.name, " did not complete within ").concat(task.limit.tries, " attempts. Please check what went wrong.").concat(failureMessage); + if (task.limit.soft && this.attempts[task.name] >= task.limit.soft) throw "Task ".concat(task.name, " did not complete within ").concat(task.limit.soft, " attempts. Please check what went wrong (you may just be unlucky).").concat(failureMessage); + if (task.limit.turns && task.do instanceof external_kolmafia_.Location && task.do.turnsSpent >= task.limit.turns) throw "Task ".concat(task.name, " did not complete within ").concat(task.limit.turns, " turns. Please check what went wrong.").concat(failureMessage); + if (task.limit.unready && ((_a = task.ready) === null || _a === void 0 ? void 0 : _a.call(task))) throw "Task ".concat(task.name, " is still ready, but it should not be. Please check what went wrong.").concat(failureMessage); + if (task.limit.completed) throw "Task ".concat(task.name, " is not completed, but it should be. Please check what went wrong.").concat(failureMessage); + } + if (postcondition && !postcondition()) { + throw "Task ".concat(task.name, " failed its guard. Please check what went wrong.").concat(failureMessage); + } + } + }, { + key: "getDefaultSettings", + value: function getDefaultSettings() { + return this.constructor.defaultSettings; + } + /** + * Initialize properties for the script. + * @param manager The properties manager to use. + */ + }, { + key: "initPropertiesManager", + value: function initPropertiesManager(manager) { + var _a; + // Properties adapted from garbo + manager.set(this.getDefaultSettings()); + if (this.options.ccs !== "") { + if (this.options.ccs === undefined && (0,external_kolmafia_.readCcs)(grimoireCCS) === "") { + // Write a simple CCS so we can switch to it + (0,external_kolmafia_.writeCcs)("[ default ]\nabort", grimoireCCS); + } + manager.set({ + customCombatScript: (_a = this.options.ccs) !== null && _a !== void 0 ? _a : grimoireCCS + }); + } + } + }]); + return Engine; +}(); +Engine.defaultSettings = { + logPreferenceChange: true, + logPreferenceChangeFilter: engine_toConsumableArray(new Set([].concat(engine_toConsumableArray(property_get("logPreferenceChangeFilter").split(",")), ["libram_savedMacro", "maximizerMRUList", "testudinalTeachings", "_lastCombatStarted"]))).sort().filter(a => a).join(","), + battleAction: "custom combat script", + autoSatisfyWithMall: true, + autoSatisfyWithNPCs: true, + autoSatisfyWithCoinmasters: true, + autoSatisfyWithStash: false, + dontStopForCounters: true, + maximizerFoldables: true, + hpAutoRecovery: "-0.05", + hpAutoRecoveryTarget: "0.0", + mpAutoRecovery: "-0.05", + mpAutoRecoveryTarget: "0.0", + afterAdventureScript: "", + betweenBattleScript: "", + choiceAdventureScript: "", + familiarScript: "", + currentMood: "apathetic", + autoTuxedo: true, + autoPinkyRing: true, + autoGarish: true, + allowNonMoodBurning: false, + allowSummonBurning: true, + libramSkillsSoftcore: "none" +}; +function maxSongs() { + return have(template_string_$skill(engine_templateObject || (engine_templateObject = engine_taggedTemplateLiteral(["Mariachi Memory"])))) ? 4 : 3; +} +var wanderingNCs = new Set([ +// Halloweener dog noncombats +"Wooof! Wooooooof!", "Playing Fetch*", +// June cleaver noncombats +"Aunts not Ants", "Bath Time", "Beware of Aligator", "Delicious Sprouts", "Hypnotic Master", "Lost and Found", "Poetic Justice", "Summer Days", "Teacher's Pet", +// Lil' Doctor™ bag noncombat +"A Pound of Cure", +// Turtle taming noncombats +"Nantucket Snapper", "Blue Monday", "Capital!", "Training Day", "Boxed In", "Duel Nature", "Slow Food", "A Rolling Turtle Gathers No Moss", "Slow Road to Hell", "C'mere, Little Fella", "The Real Victims", "Like That Time in Tortuga", "Cleansing your Palette", "Harem Scarum", "Turtle in peril", "No Man, No Hole", "Slow and Steady Wins the Brawl", "Stormy Weather", "Turtles of the Universe", "O Turtle Were Art Thou", "Allow 6-8 Weeks For Delivery", "Kick the Can", "Turtles All The Way Around", "More eXtreme Than Usual", "Jewel in the Rough", "The worst kind of drowning", "Even Tamer Than Usual", "Never Break the Chain", "Close, but Yes Cigar", "Armchair Quarterback", "This Turtle Rocks!", "Really Sticking Her Neck Out", "It Came from Beneath the Sewer? Great!", "Don't Be Alarmed, Now", "Puttin' it on Wax", "More Like... Hurtle", "Musk! Musk! Musk!", "Silent Strolling"]); +var zoneSpecificNCs = new Map([["The Horror...", ["Frat House"]] // Duplicate choice name +]); +/** + * Return true if the last adv was one of: + * 1. Halloweener dog noncombats, + * 2. June cleaver noncombats, + * 3. Lil' Doctor™ bag noncombat, or + * 4. Turtle taming noncombats. + */ +function lastEncounterWasWanderingNC() { + var _a; + var last = property_get("lastEncounter"); + if (zoneSpecificNCs.has(last)) { + // Handle NCs with a duplicated name + var zones = (_a = zoneSpecificNCs.get(last)) !== null && _a !== void 0 ? _a : []; + return zones.includes(property_get("lastAdventure")); + } else { + return wanderingNCs.has(last); + } +} +;// CONCATENATED MODULE: ./node_modules/grimoire-kolmafia/dist/index.js + + + + + + + +// EXTERNAL MODULE: ./node_modules/mafia-shared-relay/dist/index.js +var dist = __webpack_require__(139); +;// CONCATENATED MODULE: ./src/args.ts + +var args = Args.create("FolgerCS", "Written by Seraphiii, branched from InstantSCCS by Pantocyclus. This is a CS-script with a focus on maximum aftercore profits", { + version: Args.flag({ + help: "Output script version number and exit.", + default: false, + setting: "" + }), + motherclan: Args.string({ + help: "Name of the mother clan for your slime fighting needs", + default: "" + }), + fortifiedwine: Args.flag({ + help: "Do not grab the DFW lucky adventure (if you have numberology or are using skip-BT route)", + default: true + }), + perfectfreeze: Args.flag({ + help: "Do not craft and drink a perfect drink", + default: true + }), + beesknees: Args.flag({ + help: "Do not buy and drink Bee's Knees", + default: true + }), + sacramentowine: Args.flag({ + help: "Do not drink a Sacramento Wine for the item test", + default: true + }), + synthxp: Args.flag({ + help: "Do not use synth for the Xp% buff", + default: true + }), + bodyspradium: Args.flag({ + help: "Do not chew the body spradium if we have it", + default: true + }), + deepdish: Args.flag({ + help: "Skip Deep Dish of Legend", + default: false + }), + latedeepdish: Args.flag({ + help: "Eat a deep dish, but only before the familiar test", + default: false + }), + calzone: Args.flag({ + help: "Skip Calzone of Legend", + default: false + }), + pizza: Args.flag({ + help: "Skip Pizza of Legend", + default: false + }), + asdon: Args.flag({ + help: "Should we use Asdon Martin? Incompatible with Skipping Borrowed Time", + default: false + }), + astralpils: Args.number({ + help: "How many astral pilsners should we save?", + default: 0 + }), + savepulls: Args.number({ + help: "How many pulls should we save?", + default: 0 + }), + witchess: Args.flag({ + help: "Do not fight witchess monsters nor acquire Puzzle Champ", + default: false + }), + savebackups: Args.number({ + help: "How many backup camera backups should we save?", + default: 11 + }), + savehabitats: Args.number({ + help: "How many Book of Facts habitats should we save?", + default: 3 + }), + redskeleton: Args.flag({ + help: "Do not locket a red skeleton", + default: false + }), + witchessking: Args.flag({ + help: "Do not locket a witchess king", + default: false + }), + factoryworker: Args.flag({ + help: "Do not locket a factory worker (female)", + default: false + }), + ninjamap: Args.flag({ + help: "Do not attempt to grab a li'l ninja costume for your tot", + default: false + }), + savesnokebomb: Args.flag({ + help: "Should we save snokebombs for Inner Elf?", + default: false + }), + skipbishop: Args.flag({ + help: "Save 3 Witchess fights for the Queen, King and Witch", + default: false + }), + savepurqoise: Args.flag({ + help: "Do not autosell your porquoise", + default: false + }), + savefloundry: Args.flag({ + help: "Do not create a carpe", + default: false + }), + savefortune: Args.flag({ + help: "Do not consult Zatara for the stat buff", + default: false + }), + savesnack: Args.flag({ + help: "Do not use your snack voucher", + default: false + }), + savebarrel: Args.flag({ + help: "Do not get the barrel shrine buff", + default: false + }), + saveterminal: Args.flag({ + help: "Do not acquire items.enh and substats.enh", + default: false + }), + savecopdollar: Args.flag({ + help: "Do not acquire shoe gum with cop dollars", + default: false + }), + savekgb: Args.flag({ + help: "Do not use any KGB clicks", + default: false + }), + savepantogramming: Args.flag({ + help: "Do not use your pantogram", + default: false + }), + savemumming: Args.flag({ + help: "Do not use your mumming trunk", + default: false + }), + saveglove: Args.flag({ + help: "Do not acquire Triple-Sized and Invisible Avatar", + default: false + }), + savemayday: Args.flag({ + help: "Do not use your Mayday survival package", + default: false + }), + savepumpkin: Args.flag({ + help: "Do not use harvested pumpkins", + default: false + }), + savesugar: Args.flag({ + help: "Do not spend tome uses on sugar shorts/chapeau/shank", + default: false + }), + savegarden: Args.flag({ + help: "Do not harvest your garden", + default: false + }), + savemoontune: Args.flag({ + help: "Do not tune the moon for familiar weight test", + default: false + }), + savecinch: Args.number({ + help: "How much cinch should we save?", + default: 0 + }), + saverests: Args.number({ + help: "How many rests should we use (not save, use!)?", + default: 0 + }), + savenumberology: Args.number({ + help: "How many numberology casts should we use (not save, use!)?", + default: 0 + }), + savelimitedat: Args.flag({ + help: "Should we use limited Accordian Thief songs?", + default: false + }), + savegovernment: Args.flag({ + help: "Do not attempt to unlock the beach with meat to grab an anticheese", + default: false + }), + savedeck: Args.flag({ + help: "Do not use Giant Growth or any other deck cheats", + default: false + }), + savecyclops: Args.flag({ + help: "Do not get and use cyclops eyedrops", + default: false + }), + dopulls: Args.flag({ + help: "Automatically use excess pulls for good stuff?", + default: true + }), + camelhat: Args.flag({ + help: "Use a box of familiar jacks to ensure camel is charged faster?", + default: false + }), + skipbt: Args.flag({ + help: "Route around using borrowed time? Note this requires using at least one legendary pizza.", + default: false + }), + experimentalsynth: Args.flag({ + help: "Try using Synth for item%? Requires Sugar Shummoning", + default: false + }), + explicitlyexcludedefs: Args.string({ + help: "Effects that we should not acquire throughout the run.", + default: "" + }), + explicitlyexcludedfams: Args.string({ + help: "Familiars that we should not use throughout the run.", + default: "" + }), + boozelimit: Args.number({ + help: "Set default booze test limit", + default: 10 + }), + familiarlimit: Args.number({ + help: "Set default booze test limit", + default: 40 + }), + hotlimit: Args.number({ + help: "Set default booze test limit", + default: 10 + }), + noncomlimit: Args.number({ + help: "Set default nomcombat test limit", + default: 10 + }), + spelldmglimit: Args.number({ + help: "Set default spell damage test limit", + default: 40 + }), + moxielimit: Args.number({ + help: "Set default moxie test limit", + default: 1 + }), + musclelimit: Args.number({ + help: "Set default muscle test limit", + default: 1 + }), + myslimit: Args.number({ + help: "Set default mys test limit", + default: 1 + }), + hplimit: Args.number({ + help: "Set default hp test limit", + default: 1 + }), + weaponlimit: Args.number({ + help: "Set default weapon damage test limit", + default: 12 + }) +}); +;// CONCATENATED MODULE: ./src/relay.ts +function relay_slicedToArray(arr, i) { return relay_arrayWithHoles(arr) || relay_iterableToArrayLimit(arr, i) || relay_unsupportedIterableToArray(arr, i) || relay_nonIterableRest(); } +function relay_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function relay_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return relay_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return relay_arrayLikeToArray(o, minLen); } +function relay_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function relay_iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } +function relay_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + + + +function convertArgsToHtml() { + var _metadata$options$def; + var metadata = Args.getMetadata(args); + var pages = [{ + page: (_metadata$options$def = metadata.options.defaultGroupName) !== null && _metadata$options$def !== void 0 ? _metadata$options$def : "Options", + components: [] + }]; + metadata.traverse((key, name) => { + var _key$key, _key$setting, _key$key2; + if (key.setting === "" || key.hidden) return; + var component = { + type: "string", + name: (_key$key = key.key) !== null && _key$key !== void 0 ? _key$key : name, + description: key.help || "No Description Provided", + preference: (_key$setting = key.setting) !== null && _key$setting !== void 0 ? _key$setting : "".concat(metadata.scriptName, "_").concat((_key$key2 = key.key) !== null && _key$key2 !== void 0 ? _key$key2 : name), + default: "default" in key ? "".concat(key["default"]) : "block" + }; + if (key.valueHelpName === "FLAG" || key.valueHelpName === "BOOLEAN") { + component.type = "boolean"; + } else if (key.options !== undefined) { + component.type = "dropdown"; + component.dropdown = key.options.map(_ref => { + var _ref2 = relay_slicedToArray(_ref, 2), + k = _ref2[0], + desc = _ref2[1]; + return { + display: desc !== null && desc !== void 0 ? desc : k, + value: k + }; + }); + } + pages[0].components.push(component); + }, (group, name) => { + pages.push({ + page: name, + components: [] + }); + }); + pages.filter(p => p.components.length > 0).forEach(p => { + var html = { + type: "html", + data: "

FolgerCS ".concat(p.page, "") + }; + p.components.splice(0, 0, html); + }); + return pages.filter(page => page.components.length > 0); +} +function main() { + if ((0,dist.handleApiRequest)()) return; + (0,external_kolmafia_.write)((0,dist.generateHTML)(convertArgsToHtml())); +} +})(); + +var __webpack_export_target__ = exports; +for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; +if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); +/******/ })() +; \ No newline at end of file diff --git a/package.json b/package.json index 3503721..d0e7365 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "garbo-lib": "^0.0.1", "grimoire-kolmafia": "^0.3.18", "kolmafia": "^5.27681.0", - "libram": "^0.8.16" + "libram": "^0.8.16", + "mafia-shared-relay": "0.0.7" }, "author": "Pantocyclus/Seraphiii", "license": "MIT", diff --git a/src/args.ts b/src/args.ts new file mode 100644 index 0000000..36ba3f7 --- /dev/null +++ b/src/args.ts @@ -0,0 +1,271 @@ +import { Args } from "grimoire-kolmafia"; + +export const args = Args.create( + "FolgerCS", + `Written by Seraphiii, branched from InstantSCCS by Pantocyclus. This is a CS-script with a focus on maximum aftercore profits`, + { + version: Args.flag({ + help: "Output script version number and exit.", + default: false, + setting: "", + }), + confirm: Args.boolean({ + help: "If the user must confirm execution of each task.", + default: false, + }), + sim: Args.flag({ help: "Check if you have the requirements to run this script.", setting: "" }), + savedresources: Args.flag({ + help: "Check which resources you have current set to be saved.", + setting: "", + }), + recap: Args.flag({ help: "Recap of today's run.", setting: "" }), + motherclan: Args.string({ + help: `Name of the mother clan for your slime fighting needs`, + default: "", + }), + fortifiedwine: Args.flag({ + help: `Do not grab the DFW lucky adventure (if you have numberology or are using skip-BT route)`, + default: true, + }), + perfectfreeze: Args.flag({ + help: `Do not craft and drink a perfect drink`, + default: true, + }), + beesknees: Args.flag({ + help: `Do not buy and drink Bee's Knees`, + default: true, + }), + sacramentowine: Args.flag({ + help: `Do not drink a Sacramento Wine for the item test`, + default: true, + }), + synthxp: Args.flag({ + help: `Do not use synth for the Xp% buff`, + default: true, + }), + bodyspradium: Args.flag({ + help: `Do not chew the body spradium if we have it`, + default: true, + }), + deepdish: Args.flag({ + help: `Skip Deep Dish of Legend`, + default: false, + }), + latedeepdish: Args.flag({ + help: `Eat a deep dish, but only before the familiar test`, + default: false, + }), + calzone: Args.flag({ + help: `Skip Calzone of Legend`, + default: false, + }), + pizza: Args.flag({ + help: `Skip Pizza of Legend`, + default: false, + }), + asdon: Args.flag({ + help: `Should we use Asdon Martin? Incompatible with Skipping Borrowed Time`, + default: false, + }), + astralpils: Args.number({ + help: "How many astral pilsners should we save?", + default: 0, + }), + savepulls: Args.number({ + help: "How many pulls should we save?", + default: 0, + }), + witchess: Args.flag({ + help: `Do not fight witchess monsters nor acquire Puzzle Champ`, + default: false, + }), + savebackups: Args.number({ + help: "How many backup camera backups should we save?", + default: 11, + }), + savehabitats: Args.number({ + help: "How many Book of Facts habitats should we save?", + default: 3, + }), + redskeleton: Args.flag({ + help: `Do not locket a red skeleton`, + default: false, + }), + witchessking: Args.flag({ + help: `Do not locket a witchess king`, + default: false, + }), + factoryworker: Args.flag({ + help: `Do not locket a factory worker (female)`, + default: false, + }), + ninjamap: Args.flag({ + help: `Do not attempt to grab a li'l ninja costume for your tot`, + default: false, + }), + savesnokebomb: Args.flag({ + help: `Should we save snokebombs for Inner Elf?`, + default: false, + }), + skipbishop: Args.flag({ + help: `Save 3 Witchess fights for the Queen, King and Witch`, + default: false, + }), + savepurqoise: Args.flag({ + help: `Do not autosell your porquoise`, + default: false, + }), + savefloundry: Args.flag({ + help: `Do not create a carpe`, + default: false, + }), + savefortune: Args.flag({ + help: `Do not consult Zatara for the stat buff`, + default: false, + }), + savesnack: Args.flag({ + help: `Do not use your snack voucher`, + default: false, + }), + savebarrel: Args.flag({ + help: `Do not get the barrel shrine buff`, + default: false, + }), + saveterminal: Args.flag({ + help: `Do not acquire items.enh and substats.enh`, + default: false, + }), + savecopdollar: Args.flag({ + help: `Do not acquire shoe gum with cop dollars`, + default: false, + }), + savekgb: Args.flag({ + help: `Do not use any KGB clicks`, + default: false, + }), + savepantogramming: Args.flag({ + help: `Do not use your pantogram`, + default: false, + }), + savemumming: Args.flag({ + help: `Do not use your mumming trunk`, + default: false, + }), + saveglove: Args.flag({ + help: `Do not acquire Triple-Sized and Invisible Avatar`, + default: false, + }), + savemayday: Args.flag({ + help: `Do not use your Mayday survival package`, + default: false, + }), + savepumpkin: Args.flag({ + help: `Do not use harvested pumpkins`, + default: false, + }), + savesugar: Args.flag({ + help: `Do not spend tome uses on sugar shorts/chapeau/shank`, + default: false, + }), + savegarden: Args.flag({ + help: `Do not harvest your garden`, + default: false, + }), + savemoontune: Args.flag({ + help: `Do not tune the moon for familiar weight test`, + default: false, + }), + savecinch: Args.number({ + help: `How much cinch should we save?`, + default: 0, + }), + saverests: Args.number({ + help: `How many rests should we use (not save, use!)?`, + default: 0, + }), + savenumberology: Args.number({ + help: `How many numberology casts should we use (not save, use!)?`, + default: 0, + }), + savelimitedat: Args.flag({ + help: `Should we use limited Accordian Thief songs?`, + default: false, + }), + savegovernment: Args.flag({ + help: `Do not attempt to unlock the beach with meat to grab an anticheese`, + default: false, + }), + savedeck: Args.flag({ + help: `Do not use Giant Growth or any other deck cheats`, + default: false, + }), + savecyclops: Args.flag({ + help: `Do not get and use cyclops eyedrops`, + default: false, + }), + dopulls: Args.flag({ + help: `Automatically use excess pulls for good stuff?`, + default: true, + }), + camelhat: Args.flag({ + help: `Use a box of familiar jacks to ensure camel is charged faster?`, + default: false, + }), + skipbt: Args.flag({ + help: `Route around using borrowed time? Note this requires using at least one legendary pizza.`, + default: false, + }), + experimentalsynth: Args.flag({ + help: `Try using Synth for item%? Requires Sugar Shummoning`, + default: false, + }), + explicitlyexcludedefs: Args.string({ + help: `Effects that we should not acquire throughout the run.`, + default: "", + }), + explicitlyexcludedfams: Args.string({ + help: `Familiars that we should not use throughout the run.`, + default: "", + }), + boozelimit: Args.number({ + help: `Set default booze test limit`, + default: 10, + }), + familiarlimit: Args.number({ + help: `Set default booze test limit`, + default: 40, + }), + hotlimit: Args.number({ + help: `Set default booze test limit`, + default: 10, + }), + noncomlimit: Args.number({ + help: `Set default nomcombat test limit`, + default: 10, + }), + spelldmglimit: Args.number({ + help: `Set default spell damage test limit`, + default: 40, + }), + moxielimit: Args.number({ + help: `Set default moxie test limit`, + default: 1, + }), + musclelimit: Args.number({ + help: `Set default muscle test limit`, + default: 1, + }), + myslimit: Args.number({ + help: `Set default mys test limit`, + default: 1, + }), + hplimit: Args.number({ + help: `Set default hp test limit`, + default: 1, + }), + weaponlimit: Args.number({ + help: `Set default weapon damage test limit`, + default: 12, + }), + } +); diff --git a/src/engine/outfit.ts b/src/engine/outfit.ts index b3b0569..10ece31 100644 --- a/src/engine/outfit.ts +++ b/src/engine/outfit.ts @@ -13,6 +13,7 @@ import { maxBy, } from "libram"; import { camelFightsLeft, statToMaximizerString } from "../lib"; +import { args } from "../args"; export function garbageShirt(): void { if ( @@ -104,9 +105,7 @@ function melodramedary(): Familiar { } export function chooseFamiliar(allowAttackingFamiliars = true): Familiar { - const ignoredFamiliars = get("instant_explicitlyExcludedFamiliars", "") - .split(",") - .map((i) => toInt(i)); + const ignoredFamiliars = args.explicitlyexcludedfams.split(",").map((i) => toInt(i)); const defaultFam = have($familiar`Cookbookbat`) ? $familiar`Cookbookbat` : $familiar.none; const familiars = [ melodramedary, @@ -151,7 +150,7 @@ export function baseOutfit(allowAttackingFamiliars = true): OutfitSpec { acc2: have($item`Cincho de Mayo`) && get("_cinchUsed", 0) < 95 && - 100 - get("_cinchUsed", 0) > get("instant_saveCinch", 0) + 100 - get("_cinchUsed", 0) > args.savecinch ? $item`Cincho de Mayo` : undefined, familiar: chooseFamiliar(allowAttackingFamiliars), diff --git a/src/lib.ts b/src/lib.ts index 8e727c4..89c5c9b 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -30,6 +30,7 @@ import { Stat, storageAmount, sweetSynthesis, + toEffect, toInt, toItem, toSkill, @@ -62,8 +63,8 @@ import { Witchess, } from "libram"; import { printModtrace } from "libram/dist/modifier"; -import { forbiddenEffects } from "./resources"; import { mainStat } from "./combat"; +import { args } from "./args"; export const startingClan = getClanName(); @@ -102,6 +103,26 @@ export const testModifiers = new Map([ } }*/ +export const forbiddenEffects: Effect[] = []; + +if (args.bodyspradium) forbiddenEffects.push($effect`Boxing Day Glow`); +if (args.witchess) forbiddenEffects.push($effect`Puzzle Champ`); +if (args.savesnack) forbiddenEffects.push($effect`Wasabi With You`, $effect`Pisces in the Skyces`); +if (args.savebarrel) forbiddenEffects.push($effect`Warlock, Warstock, and Warbarrel`); +if (args.saveterminal) forbiddenEffects.push($effect`items.enh`, $effect`substats.enh`); +if (args.savecopdollar) forbiddenEffects.push($effect`Gummed Shoes`); +if (args.saveglove) forbiddenEffects.push($effect`Triple-Sized`, $effect`Invisible Avatar`); +if (args.savelimitedat) forbiddenEffects.push($effect`Chorale of Companionship`); +const manuallyExcludedBuffs = args.explicitlyexcludedefs + .split(",") + .filter((s) => s.length > 0) + .map((s) => toEffect(s)); +if (manuallyExcludedBuffs !== undefined) { + manuallyExcludedBuffs.forEach((ef) => { + forbiddenEffects.push(ef); + }); +} + export function fuelUp(): void { buy(1, $item`all-purpose flower`); use(1, $item`all-purpose flower`); @@ -416,12 +437,12 @@ export function camelFightsLeft(): number { !Witchess.have() && CombatLoversLocket.availableLocketMonsters().includes($monster`Witchess King`) && !CombatLoversLocket.monstersReminisced().includes($monster`Witchess King`) && - !get("instant_saveLocketWitchessKing", false) + !args.witchessking ? 1 : 0; const backups = Witchess.have() || have($item`Kramco Sausage-o-Matic™`) - ? Math.max(11 - get("instant_saveBackups", 0) - get("_backUpUses"), 0) + ? Math.max(11 - args.savebackups - get("_backUpUses"), 0) : 0; // No guarantee that we hit a tentacle, so we ignore that here // Currently does not consider gregs (require free banish + free fight source) @@ -466,24 +487,18 @@ export function computeCombatFrequency(): number { const umbrella = have($item`unbreakable umbrella`) ? -10 : 0; const offhand = umbrella; - const pantogram = - have($item`portable pantogram`) && !get("instant_savePantogram", false) ? -5 : 0; + const pantogram = have($item`portable pantogram`) && !args.savepantogramming ? -5 : 0; const pants = pantogram; - const kgb = - have($item`Kremlin's Greatest Briefcase`) && !get("instant_saveKGBClicks", false) ? -5 : 0; - const codpiece = - have($item`Clan VIP Lounge key`) && !get("instant_saveFloundry", false) ? -10 : 0; - const atlas = get("hasMaydayContract") && !get("instant_saveMayday", false) ? -5 : 0; - const accessories = sumNumbers([kgb, codpiece, atlas]); + const kgb = have($item`Kremlin's Greatest Briefcase`) && !args.savekgb ? -5 : 0; + const atlas = get("hasMaydayContract") && !args.savemayday ? -5 : 0; + const accessories = sumNumbers([kgb, atlas]); const rose = -20; const smoothMovements = have($skill`Smooth Movement`) ? -5 : 0; const sonata = have($skill`The Sonata of Sneakiness`) ? -5 : 0; const favoriteBird = - have($item`Bird-a-Day calendar`) && - get("yourFavoriteBirdMods").includes("Combat Frequency") && - !get("instant_saveFavoriteBird", false) + have($item`Bird-a-Day calendar`) && get("yourFavoriteBirdMods").includes("Combat Frequency") ? toInt( get("yourFavoriteBirdMods") .split(", ") @@ -493,9 +508,8 @@ export function computeCombatFrequency(): number { ) : 0; const shadowWaters = have($item`closed-circuit pay phone`) ? -10 : 0; - const powerfulGlove = - have($item`Powerful Glove`) && !forbiddenEffects.includes($effect`Invisible Avatar`) ? -10 : 0; - const shoeGum = get("hasDetectiveSchool") && !get("instant_saveCopDollars", false) ? -5 : 0; + const powerfulGlove = have($item`Powerful Glove`) && !args.saveglove ? -10 : 0; + const shoeGum = get("hasDetectiveSchool") && !args.savecopdollar ? -5 : 0; const silentRunning = -5; const feelingLonely = have($skill`Feel Lonely`) ? -5 : 0; const effects = sumNumbers([ @@ -634,9 +648,7 @@ export const generalStoreXpEffect: Effect = { export function checkLocketAvailable(): number { const locketAvailable = - (get("instant_saveLocketRedSkeleton", false) ? 1 : 0) + - (get("instant_saveLocketWitchessKing", false) ? 1 : 0) + - (get("instant_saveLocketFactoryWorker", false) ? 1 : 0); + (args.redskeleton ? 1 : 0) + (args.witchessking ? 1 : 0) + (args.factoryworker ? 1 : 0); return locketAvailable; } @@ -805,7 +817,7 @@ export function checkPull(item: Item): boolean { have(item) || get("_roninStoragePulls").split(",").includes(toInt(item).toString()) || storageAmount(item) === 0 || - 5 - get("_roninStoragePulls").split(",").length <= get("instant_savePulls", 0) + 5 - get("_roninStoragePulls").split(",").length <= args.savepulls ) return true; return false; diff --git a/src/main.ts b/src/main.ts index 04664a8..b872195 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,23 +33,10 @@ import { WeaponDamageQuest } from "./tasks/weapondamage"; import { DonateQuest, logResourceUsage } from "./tasks/donate"; import { SpellDamageQuest } from "./tasks/spelldamage"; import { checkRequirements } from "./sim"; -import { checkResources } from "./resources"; +import { args } from "./args"; const timeProperty = "fullday_elapsedTime"; -export const args = Args.create("FolgerCS", "An automated mid-shiny SCCS script.", { - confirm: Args.boolean({ - help: "If the user must confirm execution of each task.", - default: false, - }), - sim: Args.flag({ help: "Check if you have the requirements to run this script.", setting: "" }), - savedresources: Args.flag({ - help: "Check which resources you have current set to be saved.", - setting: "", - }), - recap: Args.flag({ help: "Recap of today's run.", setting: "" }), -}); - export function main(command?: string): void { sinceKolmafiaRevision(27675); @@ -62,10 +49,6 @@ export function main(command?: string): void { checkRequirements(); return; } - if (args.savedresources) { - checkResources(); - return; - } if (args.recap) { logResourceUsage(); diff --git a/src/relay.ts b/src/relay.ts new file mode 100644 index 0000000..c915611 --- /dev/null +++ b/src/relay.ts @@ -0,0 +1,63 @@ +import { Args } from "grimoire-kolmafia"; +import { write } from "kolmafia"; +import { + ComponentHtml, + ComponentSetting, + generateHTML, + handleApiRequest, + RelayPage, +} from "mafia-shared-relay"; +import { args } from "./args"; + +function convertArgsToHtml(): RelayPage[] { + const metadata = Args.getMetadata(args); + + const pages: RelayPage[] = [ + { page: metadata.options.defaultGroupName ?? "Options", components: [] }, + ]; + + metadata.traverse( + (key, name: string) => { + if (key.setting === "" || key.hidden) return; + + const component: ComponentSetting = { + type: "string", + name: key.key ?? name, + description: key.help || "No Description Provided", + preference: key.setting ?? `${metadata.scriptName}_${key.key ?? name}`, + default: "default" in key ? `${key["default"]}` : "block", + }; + + if (key.valueHelpName === "FLAG" || key.valueHelpName === "BOOLEAN") { + component.type = "boolean"; + } else if (key.options !== undefined) { + component.type = "dropdown"; + component.dropdown = key.options.map(([k, desc]) => { + return { display: desc ?? k, value: k }; + }); + } + pages[0].components.push(component); + }, + (group, name: string) => { + pages.push({ page: name, components: [] }); + } + ); + + pages + .filter((p) => p.components.length > 0) + .forEach((p) => { + const html: ComponentHtml = { + type: "html", + data: `

FolgerCS ${p.page}`, + }; + p.components.splice(0, 0, html); + }); + + return pages.filter((page) => page.components.length > 0); +} + +export function main() { + if (handleApiRequest()) return; + + write(generateHTML(convertArgsToHtml())); +} diff --git a/src/resources.ts b/src/resources.ts index 45dbee5..2b5e227 100644 --- a/src/resources.ts +++ b/src/resources.ts @@ -1,4 +1,4 @@ -import { Effect, Familiar, print, printHtml, toEffect, toInt, totalFreeRests } from "kolmafia"; +/*import { Effect, Familiar, print, printHtml, toEffect, toInt, totalFreeRests } from "kolmafia"; import { $effect, $effects, get, set } from "libram"; class Resource { @@ -262,4 +262,4 @@ export function checkResources(): void { "Type 'set instant_explicitlyExcludedFamiliars=,...,' to exclude using specific familiars during leveling" ); print("Type 'ash remove_property(\"\")' to delete a preference"); -} +}*/ diff --git a/src/tasks/boozedrop.ts b/src/tasks/boozedrop.ts index 29851c9..e28de4c 100644 --- a/src/tasks/boozedrop.ts +++ b/src/tasks/boozedrop.ts @@ -50,6 +50,7 @@ import { checkLocketAvailable, checkTurnSave, checkValue, + forbiddenEffects, fuelUp, logTestSetup, shouldFeelLost, @@ -59,8 +60,8 @@ import { import { chooseFamiliar, sugarItemsAboutToBreak } from "../engine/outfit"; import { CombatStrategy } from "grimoire-kolmafia"; import Macro, { haveFreeBanish } from "../combat"; -import { forbiddenEffects } from "../resources"; import { drive } from "libram/dist/resources/2017/AsdonMartin"; +import { args } from "../args"; function wishOrSpleen(): boolean { if ( @@ -96,9 +97,7 @@ export const BoozeDropQuest: Quest = { { name: "Acquire Clover", completed: () => - have($item`11-leaf clover`) || - get("_cloversPurchased") >= 2 || - get("instant_skipCyclopsEyedrops", false), + have($item`11-leaf clover`) || get("_cloversPurchased") >= 2 || args.savecyclops, do: (): void => { buy(1, $item`chewing gum on a string`); use(1, $item`chewing gum on a string`); @@ -118,9 +117,7 @@ export const BoozeDropQuest: Quest = { { name: "Acquire Government", completed: () => - !have($item`government cheese`) || - get("lastAnticheeseDay") > 0 || - get("instant_skipGovernment", false), + !have($item`government cheese`) || get("lastAnticheeseDay") > 0 || args.savegovernment, do: (): void => { if (myMeat() >= 15000) retrieveItem($item`Desert Bus pass`); if (!have($item`Desert Bus pass`) && !have($item`bitchin' meatcar`)) { @@ -221,7 +218,7 @@ export const BoozeDropQuest: Quest = { have($effect`Sacré Mental`) || !have($item`Sacramento wine`) || myInebriety() >= inebrietyLimit() || - get("instant_saveSacramentoWine", false), + args.sacramentowine, do: (): void => { if (myInebriety() < inebrietyLimit()) { tryAcquiringEffect($effect`Ode to Booze`); @@ -231,36 +228,12 @@ export const BoozeDropQuest: Quest = { }, limit: { tries: 1 }, }, - { - name: "Drink Cabernet Sauvignon", - ready: () => - checkValue("August Scepter", Math.min(2.6, CommunityService.BoozeDrop.prediction - 1)), - completed: () => - have($effect`Cabernet Hunter`) || - (!have($item`bottle of Cabernet Sauvignon`) && - // eslint-disable-next-line libram/verify-constants - (!have($skill`Aug. 31st: Cabernet Sauvignon Day!`) || - get("instant_saveAugustScepter", false))) || - myInebriety() + 3 > inebrietyLimit() || - get("instant_skipCabernetSauvignon", false), - do: (): void => { - if (!have($item`bottle of Cabernet Sauvignon`)) - // eslint-disable-next-line libram/verify-constants - useSkill($skill`Aug. 31st: Cabernet Sauvignon Day!`); - if (myInebriety() + 3 <= inebrietyLimit()) { - tryAcquiringEffect($effect`Ode to Booze`); - drink($item`bottle of Cabernet Sauvignon`); - uneffect($effect`Ode to Booze`); - } - }, - limit: { tries: 1 }, - }, { name: "Pumpkin Juice", completed: () => have($effect`Juiced and Jacked`) || (!have($item`pumpkin`) && !have($item`pumpkin juice`)) || - get("instant_savePumpkins", false), + args.savepumpkin, do: (): void => { if (!have($item`pumpkin juice`)) create($item`pumpkin juice`, 1); use($item`pumpkin juice`, 1); @@ -273,17 +246,26 @@ export const BoozeDropQuest: Quest = { completed: () => have($effect`Spitting Rhymes`) || !have($item`2002 Mr. Store Catalog`) || - get("availableMrStore2002Credits", 0) <= get("instant_saveCatalogCredits", 0) || forbiddenEffects.includes($effect`Spitting Rhymes`), do: (): void => { if ( - !have($item`Loathing Idol Microphone`) || - $item`Loathing Idol Microphone (75% charged)` + !have($item`Loathing Idol Microphone`) && + !have($item`Loathing Idol Microphone (75% charged)`) && + !have($item`Loathing Idol Microphone (50% charged)`) && + !have($item`Loathing Idol Microphone (25% charged)`) ) { buy($coinmaster`Mr. Store 2002`, 1, $item`Loathing Idol Microphone`); } withChoice(1505, 3, () => - use($item`Loathing Idol Microphone` || $item`Loathing Idol Microphone (75% charged)`) + use( + have($item`Loathing Idol Microphone`) + ? $item`Loathing Idol Microphone` + : have($item`Loathing Idol Microphone (75% charged)`) + ? $item`Loathing Idol Microphone (75% charged)` + : have($item`Loathing Idol Microphone (50% charged)`) + ? $item`Loathing Idol Microphone (50% charged)` + : $item`Loathing Idol Microphone (25% charged)` + ) ); }, limit: { tries: 1 }, @@ -291,10 +273,7 @@ export const BoozeDropQuest: Quest = { { name: "Red-soled high heels", ready: () => checkValue("2002", 3), - completed: () => - have($item`red-soled high heels`) || - !have($item`2002 Mr. Store Catalog`) || - get("availableMrStore2002Credits", 0) <= get("instant_saveCatalogCredits", 0), + completed: () => have($item`red-soled high heels`) || !have($item`2002 Mr. Store Catalog`), do: (): void => { if (!have($item`Letter from Carrie Bradshaw`)) { buy($coinmaster`Mr. Store 2002`, 1, $item`Letter from Carrie Bradshaw`); @@ -308,8 +287,7 @@ export const BoozeDropQuest: Quest = { completed: () => !have($skill`Visit your Favorite Bird`) || get("_favoriteBirdVisited") || - !get("yourFavoriteBirdMods").includes("Item Drops") || - get("instant_saveFavoriteBird", false), + !get("yourFavoriteBirdMods").includes("Item Drops"), do: () => useSkill($skill`Visit your Favorite Bird`), limit: { tries: 1 }, }, @@ -329,7 +307,8 @@ export const BoozeDropQuest: Quest = { }, { name: "Driving Observantly", - completed: () => have($effect`Driving Observantly`) || !get("instant_useAsdon", false), + ready: () => args.asdon, + completed: () => have($effect`Driving Observantly`), do: (): void => { fuelUp(), drive($effect`Driving Observantly`); }, @@ -407,16 +386,13 @@ export const BoozeDropQuest: Quest = { }, completed: () => CommunityService.BoozeDrop.isDone(), do: (): void => { - const maxTurns = get("instant_boozeTestTurnLimit", 30); + const maxTurns = args.boozelimit; const testTurns = CommunityService.BoozeDrop.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); print("Either there was a bug, or you are under-prepared for this test", "red"); print("Manually complete the test if you think this is fine.", "red"); - print( - "You may also increase the turn limit by typing 'set instant_boozeTestTurnLimit='", - "red" - ); + print("You may also increase the turn limit in the relay", "red"); } CommunityService.BoozeDrop.run(() => logTestSetup(CommunityService.BoozeDrop), maxTurns); }, diff --git a/src/tasks/earlyleveling.ts b/src/tasks/earlyleveling.ts index c6fd4e7..8acb4a9 100644 --- a/src/tasks/earlyleveling.ts +++ b/src/tasks/earlyleveling.ts @@ -55,6 +55,7 @@ import Macro from "../combat"; import { mapMonster } from "libram/dist/resources/2020/Cartography"; import { chooseRift } from "libram/dist/resources/2023/ClosedCircuitPayphone"; import { boomBoxProfit } from "../lib"; +import { args } from "../args"; const useParkaSpit = have($item`Fourth of May Cosplay Saber`) && have($skill`Feel Envy`); const baseBoozes = $items`bottle of rum, boxed wine, bottle of gin, bottle of vodka, bottle of tequila, bottle of whiskey`; @@ -135,10 +136,7 @@ function sellMiscellaneousItems(): void { export const earlyLevelingQuest: Quest = { name: "Early Leveling", completed: () => - get("pizzaOfLegendEaten") || - !get("instant_skipBorrowedTime", false) || - get("instant_useAsdon", false) || - CommunityService.CoilWire.isDone(), + get("pizzaOfLegendEaten") || !args.skipbt || args.asdon || CommunityService.CoilWire.isDone(), tasks: [ { name: "Install Trainset", @@ -208,8 +206,7 @@ export const earlyLevelingQuest: Quest = { }, completed: () => CombatLoversLocket.monstersReminisced().includes($monster`red skeleton`) || - !CombatLoversLocket.availableLocketMonsters().includes($monster`red skeleton`) || - get("instant_saveLocketRedSkeleton", false), + !CombatLoversLocket.availableLocketMonsters().includes($monster`red skeleton`), do: () => CombatLoversLocket.reminisce($monster`red skeleton`), combat: get("_daycareGymScavenges") ? new CombatStrategy().macro( @@ -257,7 +254,7 @@ export const earlyLevelingQuest: Quest = { modifier: `${baseOutfit().modifier}, -equip miniature crystal ball`, }), post: (): void => { - if (have($item`MayDay™ supply package`) && !get("instant_saveMayday", false)) + if (have($item`MayDay™ supply package`) && args.savemayday) use($item`MayDay™ supply package`, 1); if (have($item`space blanket`)) autosell($item`space blanket`, 1); use($item`red box`, 1); @@ -337,7 +334,6 @@ export const earlyLevelingQuest: Quest = { !have($skill`Just the Facts`) || get("_monstersMapped") >= 3 || have($item`pocket wish`, 1) || - get("instant_saveGenie", false) || myClass() !== $class`Seal Clubber` || ((get("_shatteringPunchUsed") >= 3 || !have($skill`Shattering Punch`)) && (get("_gingerbreadMobHitUsed") || !have($skill`Gingerbread Mob Hit`))), @@ -435,7 +431,7 @@ export const earlyLevelingQuest: Quest = { get("_roninStoragePulls") .split(",") .includes(toInt($item`Pizza of Legend`).toString()) || - get("instant_skipPizzaOfLegend", false), + args.pizza, do: (): void => { if (storageAmount($item`Pizza of Legend`) === 0) { print("Uh oh! You do not seem to have a Pizza of Legend in Hagnk's", "red"); @@ -474,7 +470,7 @@ export const earlyLevelingQuest: Quest = { get("_roninStoragePulls") .split(",") .includes(toInt($item`Calzone of Legend`).toString()) || - get("instant_skipCalzoneOfLegend", false), + args.calzone, do: (): void => { if (storageAmount($item`Calzone of Legend`) === 0) { print("Uh oh! You do not seem to have a Calzone of Legend in Hagnk's", "red"); diff --git a/src/tasks/familiarweight.ts b/src/tasks/familiarweight.ts index 09ca5d0..3f15cd3 100644 --- a/src/tasks/familiarweight.ts +++ b/src/tasks/familiarweight.ts @@ -37,6 +37,7 @@ import { chooseHeaviestFamiliar, sugarItemsAboutToBreak, } from "../engine/outfit"; +import { args } from "../args"; export const FamiliarWeightQuest: Quest = { name: "Familiar Weight", @@ -47,7 +48,7 @@ export const FamiliarWeightQuest: Quest = { completed: () => !have($item`hewn moon-rune spoon`) || get("moonTuned") || - get("instant_saveMoonTune", false) || + args.savemoontune || mySign() === "Platypus", do: (): void => { cliExecute("spoon platypus"); @@ -55,7 +56,7 @@ export const FamiliarWeightQuest: Quest = { }, { name: "Late Eat Deep Dish", - ready: () => get("instant_lateDeepDish", false), + ready: () => args.latedeepdish, completed: () => get("deepDishOfLegendEaten") || !have($item`Deep Dish of Legend`), do: (): void => { if (have($item`familiar scrapbook`)) { @@ -71,7 +72,7 @@ export const FamiliarWeightQuest: Quest = { !have($skill`Chorale of Companionship`) || have($effect`Chorale of Companionship`) || myClass() !== $class`Accordion Thief` || - get("instant_saveLimitedAT", false), + args.savelimitedat, do: (): void => { shrugAT(); cliExecute("cast 1 Chorale of Companionship"); @@ -171,7 +172,7 @@ export const FamiliarWeightQuest: Quest = { } }, do: (): void => { - const maxTurns = get("instant_famTestTurnLimit", 50); + const maxTurns = args.familiarlimit; const testTurns = CommunityService.FamiliarWeight.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/src/tasks/hotres.ts b/src/tasks/hotres.ts index 7fdc7d5..4f2a9f4 100644 --- a/src/tasks/hotres.ts +++ b/src/tasks/hotres.ts @@ -17,6 +17,7 @@ import { Quest } from "../engine/task"; import { checkTurnSave, checkValue, logTestSetup, tryAcquiringEffect, wishFor } from "../lib"; import { chooseFamiliar, sugarItemsAboutToBreak } from "../engine/outfit"; import Macro from "../combat"; +import { args } from "../args"; export const HotResQuest: Quest = { name: "Hot Res", @@ -57,7 +58,7 @@ export const HotResQuest: Quest = { completed: () => CombatLoversLocket.monstersReminisced().includes($monster`factory worker (female)`) || !CombatLoversLocket.availableLocketMonsters().includes($monster`factory worker (female)`) || - get("instant_saveLocketFactoryWorker", false) || + args.factoryworker || checkValue("Locket", Math.min(14, CommunityService.HotRes.prediction - 1)), do: () => CombatLoversLocket.reminisce($monster`factory worker (female)`), outfit: () => ({ @@ -98,8 +99,7 @@ export const HotResQuest: Quest = { completed: () => !have($skill`Visit your Favorite Bird`) || get("_favoriteBirdVisited") || - !get("yourFavoriteBirdMods").includes("Hot Resistance") || - get("instant_saveFavoriteBird", false), + !get("yourFavoriteBirdMods").includes("Hot Resistance"), do: () => useSkill($skill`Visit your Favorite Bird`), limit: { tries: 1 }, }, @@ -111,7 +111,7 @@ export const HotResQuest: Quest = { if ( get("_kgbClicksUsed") < 22 && have($item`Kremlin's Greatest Briefcase`) && - !get("instant_saveKGBClicks", false) + !args.savekgb ) cliExecute("briefcase e hot"); @@ -146,7 +146,7 @@ export const HotResQuest: Quest = { }, completed: () => CommunityService.HotRes.isDone(), do: (): void => { - const maxTurns = get("instant_hotTestTurnLimit", 35); + const maxTurns = args.hotlimit; const testTurns = CommunityService.HotRes.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/src/tasks/leveling.ts b/src/tasks/leveling.ts index ebb753b..6ea8597 100644 --- a/src/tasks/leveling.ts +++ b/src/tasks/leveling.ts @@ -42,7 +42,6 @@ import { takeStorage, toInt, toItem, - totalFreeRests, use, useSkill, visitUrl, @@ -82,10 +81,10 @@ import { boomBoxProfit, burnLibram, camelFightsLeft, - checkLocketAvailable, checkPull, checkValue, chooseLibram, + forbiddenEffects, fuelUp, generalStoreXpEffect, getSynthExpBuff, @@ -113,7 +112,6 @@ import { unbreakableUmbrella, } from "../engine/outfit"; import Macro, { haveFreeBanish, haveFreeKill } from "../combat"; -import { forbiddenEffects } from "../resources"; import { mapMonster } from "libram/dist/resources/2020/Cartography"; import { chooseQuest, @@ -122,8 +120,9 @@ import { } from "libram/dist/resources/2023/ClosedCircuitPayphone"; import { drive } from "libram/dist/resources/2017/AsdonMartin"; import { cheatCard, getRemainingCheats } from "libram/dist/resources/2015/DeckOfEveryCard"; +import { args } from "../args"; -const useCinch = !get("instant_saveCinch", false); +const useCinch = args.savecinch >= 100 - get("_cinchUsed"); const baseBoozes = $items`bottle of rum, boxed wine, bottle of gin, bottle of vodka, bottle of tequila, bottle of whiskey`; const freeFightMonsters: Monster[] = $monsters`Witchess Bishop, Witchess King, Witchess Witch, sausage goblin, Eldritch Tentacle`; @@ -382,7 +381,6 @@ export const LevelingQuest: Quest = { completed: () => have($effect`Hot in Herre`) || !have($item`2002 Mr. Store Catalog`) || - get("availableMrStore2002Credits", 0) <= get("instant_saveCatalogCredits", 0) || forbiddenEffects.includes($effect`Hot in Herre`), do: (): void => { if (!have($item`Charter: Nellyville`)) { @@ -394,7 +392,8 @@ export const LevelingQuest: Quest = { }, { name: "Driving Recklessly", - completed: () => have($effect`Driving Recklessly`) || !get("instant_useAsdon", false), + ready: () => args.asdon, + completed: () => have($effect`Driving Recklessly`), do: (): void => { fuelUp(), drive($effect`Driving Recklessly`); }, @@ -413,7 +412,7 @@ export const LevelingQuest: Quest = { name: "Synth Exp Buff", completed: () => !have($skill`Sweet Synthesis`) || - get("instant_skipSynthExp", false) || + args.synthxp || have(synthExpBuff) || getValidComplexCandyPairs().length === 0, do: (): void => getSynthExpBuff(), @@ -421,10 +420,9 @@ export const LevelingQuest: Quest = { }, { name: "Pull Deep Dish of Legend", + ready: () => !args.deepdish, completed: () => - checkPull($item`Deep Dish of Legend`) || - have($effect`In the Depths`) || - get("instant_skipDeepDishOfLegend", false), + checkPull($item`Deep Dish of Legend`) || have($effect`In the Depths`) || args.deepdish, prepare: (): void => { cliExecute(`maximize ${myPrimestat()} experience percent`); }, @@ -444,9 +442,7 @@ export const LevelingQuest: Quest = { { name: "Pull Calzone of Legend", completed: () => - checkPull($item`Calzone of Legend`) || - have($effect`In the 'zone zone!`) || - get("instant_skipCalzoneOfLegend", false), + checkPull($item`Calzone of Legend`) || have($effect`In the 'zone zone!`) || args.calzone, prepare: (): void => { cliExecute(`maximize ${myPrimestat()} experience percent`); }, @@ -469,9 +465,7 @@ export const LevelingQuest: Quest = { { name: "Pull Pizza of Legend", completed: () => - checkPull($item`Pizza of Legend`) || - have($effect`Endless Drool`) || - get("instant_skipPizzaOfLegend", false), + checkPull($item`Pizza of Legend`) || have($effect`Endless Drool`) || args.pizza, do: (): void => { if (storageAmount($item`Pizza of Legend`) === 0) { print("Uh oh! You do not seem to have a Pizza of Legend in Hagnk's", "red"); @@ -487,7 +481,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Buddy Bjorn", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`Buddy Bjorn`), do: (): void => { takeStorage($item`Buddy Bjorn`, 1); @@ -496,7 +490,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Stick-Knife", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`Stick-Knife of Loathing`), do: (): void => { takeStorage($item`Stick-Knife of Loathing`, 1); @@ -505,7 +499,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Repaid Diaper", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`Great Wolf's beastly trousers`) || checkPull($item`repaid diaper`), do: (): void => { @@ -515,7 +509,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Beastly Trousers", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`Great Wolf's beastly trousers`) || have($item`astral trousers`), do: (): void => { @@ -525,7 +519,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Staff of Simering Hatred", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`Staff of Simmering Hatred`), do: (): void => { takeStorage($item`Staff of Simmering Hatred`, 1); @@ -534,7 +528,7 @@ export const LevelingQuest: Quest = { }, { name: "Pull Tobiko Marble Soda", - ready: () => get("instant_experimentPulls", true), + ready: () => args.dopulls, completed: () => checkPull($item`tobiko marble soda`), do: (): void => { takeStorage($item`tobiko marble soda`, 1); @@ -561,7 +555,7 @@ export const LevelingQuest: Quest = { ready: () => have($item`Deck of Every Card`), completed: () => getRemainingCheats() <= 2 || - get("instant_saveDeck", false) || + args.savedeck || have($effect`Giant Growth`) || !have($skill`Giant Growth`), do: (): void => { @@ -573,7 +567,7 @@ export const LevelingQuest: Quest = { name: "Pull Daypass", completed: () => powerlevelingLocation() !== $location`Uncle Gator's Country Fun-Time Liquid Waste Sluice` || - 5 - get("_roninStoragePulls").split(",").length >= get("instant_savePulls", 0) || + 5 - get("_roninStoragePulls").split(",").length >= args.savepulls || get("stenchAirportAlways") || get("_stenchAirportToday"), do: (): void => { @@ -614,9 +608,7 @@ export const LevelingQuest: Quest = { }, { name: "Restore mp", - completed: () => - get("timesRested") >= totalFreeRests() - get("instant_saveFreeRests", 0) || - myMp() >= Math.min(200, myMaxmp()), + completed: () => get("timesRested") >= args.saverests || myMp() >= Math.min(200, myMaxmp()), prepare: (): void => { if (have($item`Newbiesport™ tent`)) use($item`Newbiesport™ tent`); }, @@ -658,9 +650,7 @@ export const LevelingQuest: Quest = { { name: "Eat Deep Dish", completed: () => - get("deepDishOfLegendEaten") || - !have($item`Deep Dish of Legend`) || - get("instant_lateDeepDish", false), + get("deepDishOfLegendEaten") || !have($item`Deep Dish of Legend`) || args.deepdish, prepare: (): void => { cliExecute(`maximize ${myPrimestat()} experience percent`); }, @@ -682,9 +672,7 @@ export const LevelingQuest: Quest = { { name: "Cast Perfect Freeze", completed: () => - !have($skill`Perfect Freeze`) || - get("_perfectFreezeUsed") || - get("instant_savePerfectFreeze", false), + !have($skill`Perfect Freeze`) || get("_perfectFreezeUsed") || args.perfectfreeze, prepare: () => restoreMp(mpCost($skill`Perfect Freeze`)), do: () => useSkill($skill`Perfect Freeze`), limit: { tries: 1 }, @@ -695,7 +683,7 @@ export const LevelingQuest: Quest = { myInebriety() >= 3 || !have($item`perfect ice cube`) || !baseBoozes.some((it) => have(it)) || - get("instant_savePerfectFreeze", false), + args.perfectfreeze, prepare: (): void => { cliExecute(`maximize ${myPrimestat()} experience percent`); }, @@ -734,7 +722,7 @@ export const LevelingQuest: Quest = { }, { name: "Consult Fortune Teller", - completed: () => get("_clanFortuneBuffUsed") || get("instant_saveFortuneTeller", false), + completed: () => get("_clanFortuneBuffUsed") || args.savefortune, do: () => cliExecute(`fortune buff ${statToMaximizerString(myPrimestat())}`), limit: { tries: 1 }, }, @@ -767,22 +755,17 @@ export const LevelingQuest: Quest = { ready: () => myLevel() >= 11, completed: () => myInebriety() >= inebrietyLimit() || - (!have($item`astral six-pack`) && - itemAmount($item`astral pilsner`) <= get("instant_saveAstralPilsners", 0)), + (!have($item`astral six-pack`) && itemAmount($item`astral pilsner`) <= args.astralpils), prepare: (): void => { cliExecute(`maximize ${myPrimestat()} experience percent`); tryAcquiringEffect($effect`Ode to Booze`); }, do: (): void => { if (have($item`astral six-pack`)) use($item`astral six-pack`, 1); - if (itemAmount($item`astral pilsner`) > get("instant_saveAstralPilsners", 0)) - drink($item`astral pilsner`, 1); + if (itemAmount($item`astral pilsner`) > args.astralpils) drink($item`astral pilsner`, 1); }, post: (): void => { - if ( - !have($item`astral six-pack`) && - itemAmount($item`astral pilsner`) <= get("instant_saveAstralPilsners", 0) - ) + if (!have($item`astral six-pack`) && itemAmount($item`astral pilsner`) <= args.astralpils) uneffect($effect`Ode to Booze`); }, limit: { tries: 6 }, @@ -828,7 +811,7 @@ export const LevelingQuest: Quest = { get("_monstersMapped") >= 3 || have($item`li'l ninja costume`) || !have($familiar`Trick-or-Treating Tot`) || - get("instant_skipMappingNinja", false), + args.ninjamap, do: () => mapMonster($location`The Haiku Dungeon`, $monster`amateur ninja`), combat: new CombatStrategy().macro( Macro.if_( @@ -854,7 +837,7 @@ export const LevelingQuest: Quest = { { name: "Free Fight Leafy Boys", completed: () => - get("_leafMonstersFought", 0) >= 5 || !have($item`inflammable leaf`, 11) || saveLeafyBoys(), + get("_leafMonstersFought", 0) >= 4 || !have($item`inflammable leaf`, 11) || saveLeafyBoys(), do: (): void => { visitUrl("campground.php?preaction=leaves"); visitUrl("choice.php?pwd&whichchoice=1510&option=1&leaves=11"); @@ -1059,7 +1042,7 @@ export const LevelingQuest: Quest = { completed: () => CombatLoversLocket.monstersReminisced().includes($monster`red skeleton`) || !CombatLoversLocket.availableLocketMonsters().includes($monster`red skeleton`) || - get("instant_saveLocketRedSkeleton", false) || + args.redskeleton || checkValue("Locket", 4), do: () => CombatLoversLocket.reminisce($monster`red skeleton`), combat: new CombatStrategy().macro( @@ -1164,9 +1147,7 @@ export const LevelingQuest: Quest = { { name: "Restore cinch", completed: () => - get("timesRested") >= totalFreeRests() - get("instant_saveFreeRests", 0) || - get("_cinchUsed", 0) <= 95 || - !useCinch, + get("timesRested") >= args.saverests || get("_cinchUsed", 0) <= 95 || !useCinch, prepare: (): void => { if (have($item`Newbiesport™ tent`)) use($item`Newbiesport™ tent`); }, @@ -1200,8 +1181,7 @@ export const LevelingQuest: Quest = { Macro.if_($monster`fluffy bunny`, Macro.banish()) .externalIf( get("_monsterHabitatsFightsLeft") <= 1 && - toInt(get("_monsterHabitatsRecalled")) < - 3 - toInt(get("instant_saveMonsterHabitats", 0)) && + toInt(get("_monsterHabitatsRecalled")) < 3 - toInt(args.savehabitats) && have($skill`Recall Facts: Monster Habitats`) && (haveFreeBanish() || Array.from(getBanishedMonsters().values()).includes($monster`fluffy bunny`)), @@ -1230,7 +1210,7 @@ export const LevelingQuest: Quest = { completed: () => !have($item`backup camera`) || !freeFightMonsters.includes(get("lastCopyableMonster") ?? $monster.none) || - get("_backUpUses") >= 11 - clamp(get("instant_saveBackups", 0), 0, 11) || + get("_backUpUses") >= 11 - clamp(args.savebackups, 0, 11) || myBasestat(myPrimestat()) >= 190, // no longer need to back up Witchess Kings do: $location`The Dire Warren`, combat: new CombatStrategy().macro( @@ -1267,7 +1247,7 @@ export const LevelingQuest: Quest = { combat: new CombatStrategy().macro(() => Macro.externalIf( get("_monsterHabitatsFightsLeft") <= 1 && - toInt(get("_monsterHabitatsRecalled")) < 3 - get("instant_saveMonsterHabitats", 0) && + toInt(get("_monsterHabitatsRecalled")) < 3 - args.savehabitats && have($skill`Recall Facts: Monster Habitats`) && (haveFreeBanish() || Array.from(getBanishedMonsters().values()).includes($monster`fluffy bunny`)), @@ -1369,7 +1349,6 @@ export const LevelingQuest: Quest = { !have($skill`Just the Facts`) || get("_monstersMapped") >= 3 || have($item`pocket wish`, 1) || - get("instant_saveGenie", false) || myClass() !== $class`Seal Clubber` || ((get("_shatteringPunchUsed") >= 3 || !have($skill`Shattering Punch`)) && (get("_gingerbreadMobHitUsed") || !have($skill`Gingerbread Mob Hit`))), @@ -1445,14 +1424,14 @@ export const LevelingQuest: Quest = { restoreMp(50); }, completed: () => - get("_witchessFights") >= 4 - (get("instant_skipBishopsForRoyalty", false) ? 2 : 0) || + get("_witchessFights") >= 4 - (args.skipbishop ? 2 : 0) || !Witchess.have() || - get("instant_saveWitchess", false), + args.witchess, do: () => Witchess.fightPiece($monster`Witchess Bishop`), combat: new CombatStrategy().macro(() => Macro.externalIf( get("_monsterHabitatsFightsLeft") <= 1 && - toInt(get("_monsterHabitatsRecalled")) < 3 - get("instant_saveMonsterHabitats", 0) && + toInt(get("_monsterHabitatsRecalled")) < 3 - args.savehabitats && have($skill`Recall Facts: Monster Habitats`) && (haveFreeBanish() || Array.from(getBanishedMonsters().values()).includes($monster`fluffy bunny`)), @@ -1540,7 +1519,7 @@ export const LevelingQuest: Quest = { { name: "Extra Camelspit Leveling", ready: () => get("camelSpit") >= 94 && myBasestat(myPrimestat()) >= targetBaseMyst, - completed: () => !get("instant_camelExperiment", false) || get("camelSpit") >= 100, + completed: () => !args.camelhat || get("camelSpit") >= 100, do: powerlevelingLocation(), prepare: (): void => { restoreHp(clamp(1000, myMaxhp() / 2, myMaxhp())); @@ -1589,25 +1568,10 @@ export const LevelingQuest: Quest = { boomBoxProfit(); }, }, - { - name: "Acquire Wad of Dough", - completed: () => - have($item`wad of dough`) || - (get("instant_saveHoneyBun", false) && get("instant_saveWileyWheyBar", false)), - do: (): void => { - if (myMeat() < 100) throw new Error("Insufficient Meat to purchase all-purpose flower!"); - if (!have($item`all-purpose flower`)) buy($item`all-purpose flower`, 1); - use($item`all-purpose flower`, 1); - }, - post: (): void => { - if (!have($item`flat dough`)) use($item`wad of dough`, 1); - }, - limit: { tries: 1 }, - }, { name: "Drink Bee's Knees", after: ["Powerlevel"], - completed: () => have($effect`On the Trolley`) || get("instant_saveBeesKnees", false), + completed: () => have($effect`On the Trolley`) || args.beesknees, do: (): void => { if (myMeat() < 500) throw new Error("Insufficient Meat to purchase Bee's Knees!"); tryAcquiringEffect($effect`Ode to Booze`); @@ -1641,11 +1605,7 @@ export const LevelingQuest: Quest = { have($item`dented scepter`) || get("_witchessFights") >= 5 || !Witchess.have() || - get("instant_saveWitchess", false) || - (get("instant_maximizeProfit", false) && - checkLocketAvailable() === 3 && - get("valueOfAdventure", 3750) * get("embezzlerMultiplier", 2.5) > - get("valueOfAdventure", 3750) * 2), + args.witchess, do: () => Witchess.fightPiece($monster`Witchess King`), combat: new CombatStrategy().macro(Macro.default(useCinch)), outfit: baseOutfit, @@ -1674,7 +1634,7 @@ export const LevelingQuest: Quest = { have($item`battle broom`) || get("_witchessFights") >= 5 || !Witchess.have() || - get("instant_saveWitchess", false), + args.witchess, do: () => Witchess.fightPiece($monster`Witchess Witch`), combat: new CombatStrategy().macro( Macro.trySkill($skill`Curse of Weaksauce`) @@ -1715,7 +1675,7 @@ export const LevelingQuest: Quest = { have($item`very pointy crown`) || get("_witchessFights") >= 5 || !Witchess.have() || - get("instant_saveWitchess", false), + args.witchess, do: () => Witchess.fightPiece($monster`Witchess Queen`), combat: new CombatStrategy().macro(Macro.attack().repeat()), outfit: { @@ -1745,13 +1705,13 @@ export const LevelingQuest: Quest = { completed: () => CombatLoversLocket.monstersReminisced().includes($monster`Witchess King`) || !CombatLoversLocket.availableLocketMonsters().includes($monster`Witchess King`) || - get("instant_saveLocketWitchessKing", false) || + args.witchessking || have($item`dented scepter`), do: () => CombatLoversLocket.reminisce($monster`Witchess King`), combat: new CombatStrategy().macro(() => Macro.externalIf( get("_monsterHabitatsFightsLeft") <= 1 && - get("_monsterHabitatsRecalled") < 3 - get("instant_saveMonsterHabitats", 0) && + get("_monsterHabitatsRecalled") < 3 - args.savehabitats && have($skill`Recall Facts: Monster Habitats`) && (haveFreeBanish() || Array.from(getBanishedMonsters().values()).includes($monster`fluffy bunny`)), diff --git a/src/tasks/noncombat.ts b/src/tasks/noncombat.ts index 2daa319..afebb81 100644 --- a/src/tasks/noncombat.ts +++ b/src/tasks/noncombat.ts @@ -22,6 +22,7 @@ import { import { CombatStrategy } from "grimoire-kolmafia"; import Macro from "../combat"; import { drive } from "libram/dist/resources/2017/AsdonMartin"; +import { args } from "../args"; export const NoncombatQuest: Quest = { name: "Noncombat", @@ -46,14 +47,14 @@ export const NoncombatQuest: Quest = { completed: () => !have($skill`Visit your Favorite Bird`) || get("_favoriteBirdVisited") || - !get("yourFavoriteBirdMods").includes("Combat Frequency") || - get("instant_saveFavoriteBird", false), + !get("yourFavoriteBirdMods").includes("Combat Frequency"), do: () => useSkill($skill`Visit your Favorite Bird`), limit: { tries: 1 }, }, { name: "Driving Stealthily", - completed: () => have($effect`Driving Stealthily`) || !get("instant_useAsdon", false), + ready: () => args.asdon, + completed: () => have($effect`Driving Stealthily`), do: (): void => { fuelUp(), drive($effect`Driving Stealthily`); }, @@ -62,9 +63,7 @@ export const NoncombatQuest: Quest = { { name: "Invisible Avatar", completed: () => - have($effect`Invisible Avatar`) || - !have($item`Powerful Glove`) || - get("instant_savePowerfulGlove", false), + have($effect`Invisible Avatar`) || !have($item`Powerful Glove`) || args.saveglove, do: (): void => { equip($slot`acc3`, $item`Powerful Glove`); useSkill($skill`CHEAT CODE: Invisible Avatar`); @@ -89,7 +88,7 @@ export const NoncombatQuest: Quest = { if ( get("_kgbClicksUsed") < 22 && have($item`Kremlin's Greatest Briefcase`) && - !get("instant_saveKGBClicks", false) + !args.savekgb ) cliExecute("briefcase e -combat"); const usefulEffects: Effect[] = [ @@ -116,7 +115,7 @@ export const NoncombatQuest: Quest = { wishFor($effect`Disquiet Riot`); }, do: (): void => { - const maxTurns = get("instant_comTestTurnLimit", 12); + const maxTurns = args.noncomlimit; const testTurns = CommunityService.Noncombat.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/src/tasks/runstart.ts b/src/tasks/runstart.ts index 5aa4245..9a9e6a4 100644 --- a/src/tasks/runstart.ts +++ b/src/tasks/runstart.ts @@ -33,14 +33,12 @@ import { storageAmount, takeStorage, toInt, - totalFreeRests, use, useFamiliar, useSkill, visitUrl, } from "kolmafia"; import { - $coinmaster, $effect, $familiar, $item, @@ -65,6 +63,7 @@ import { getGarden, goVote, statToMaximizerString, tryAcquiringEffect } from ".. import Macro from "../combat"; import { mapMonster } from "libram/dist/resources/2020/Cartography"; import { baseOutfit, chooseFamiliar, unbreakableUmbrella } from "../engine/outfit"; +import { args } from "../args"; const bestSIT = mallPrice($item`hollow rock`) + mallPrice($item`lump of loyal latite`) > @@ -132,8 +131,7 @@ export const RunStartQuest: Quest = { use($item`pork elf goodies sack`); autosell($item`hamethyst`, itemAmount($item`hamethyst`)); autosell($item`baconstone`, itemAmount($item`baconstone`)); - if (!get("instant_savePorquoise", false)) - autosell($item`porquoise`, itemAmount($item`porquoise`)); + if (!args.savepurqoise) autosell($item`porquoise`, itemAmount($item`porquoise`)); }, limit: { tries: 1 }, }, @@ -151,7 +149,7 @@ export const RunStartQuest: Quest = { }, { name: "Get Floundry item", - completed: () => get("_floundryItemCreated") || get("instant_saveFloundry", false), + completed: () => get("_floundryItemCreated") || args.savefloundry, do: (): void => { retrieveItem($item`carpe`); }, @@ -165,33 +163,16 @@ export const RunStartQuest: Quest = { visitUrl(`inv_use.php?whichitem=${toInt($item`2002 Mr. Store Catalog`)}&which=f0&pwd`), limit: { tries: 1 }, }, - { - name: "Use Meat Butler", - completed: () => - !have($item`2002 Mr. Store Catalog`) || - get("availableMrStore2002Credits") <= get("instant_saveCatalogCredits", 0) || - get("instant_skipMeatButler", false) || - haveInCampground($item`Meat Butler`), - do: (): void => { - if (!have($item`Meat Butler`)) buy($coinmaster`Mr. Store 2002`, 1, $item`Meat Butler`); - use($item`Meat Butler`, 1); - }, - limit: { tries: 1 }, - }, { name: "KGB", completed: () => - get("_kgbClicksUsed") > 0 || - !have($item`Kremlin's Greatest Briefcase`) || - get("instant_saveKGBClicks", false), + get("_kgbClicksUsed") > 0 || !have($item`Kremlin's Greatest Briefcase`) || args.savekgb, do: () => cliExecute("briefcase e ml"), limit: { tries: 1 }, }, { name: "Restore mp", - completed: () => - get("timesRested") >= totalFreeRests() - get("instant_saveFreeRests", 0) || - myMp() >= Math.min(200, myMaxmp()), + completed: () => get("timesRested") >= args.saverests || myMp() >= Math.min(200, myMaxmp()), prepare: (): void => { if (have($item`Newbiesport™ tent`)) use($item`Newbiesport™ tent`); }, @@ -217,7 +198,7 @@ export const RunStartQuest: Quest = { create($item`borrowed time`, 1); else takeStorage($item`borrowed time`, 1); }, - completed: () => get("_borrowedTimeUsed") || get("instant_skipBorrowedTime", false), + completed: () => get("_borrowedTimeUsed") || args.skipbt, do: (): void => { if (storageAmount($item`borrowed time`) === 0 && !have($item`borrowed time`)) { print("Uh oh! You do not seem to have a borrowed time in Hagnk's", "red"); @@ -235,17 +216,14 @@ export const RunStartQuest: Quest = { ready: () => Object.keys(reverseNumberology()).includes("69"), completed: () => get("_universeCalculated") >= - (get("skillLevel144") > 3 ? 3 : get("skillLevel144")) - get("instant_saveNumberology", 0), + (get("skillLevel144") > 3 ? 3 : get("skillLevel144")) - args.savenumberology, do: () => cliExecute("numberology 69"), limit: { tries: 3 }, }, { name: "Get Camel Hat", - completed: () => - have($item`dromedary drinking helmet`) || - get("instant_saveClipArt", false) || - !have($familiar`Melodramedary`) || - !get("instant_camelExperiment", false), + ready: () => args.camelhat, + completed: () => have($item`dromedary drinking helmet`) || !have($familiar`Melodramedary`), do: (): void => { if (!have($item`box of Familiar Jacks`)) create($item`box of Familiar Jacks`, 1); @@ -257,10 +235,7 @@ export const RunStartQuest: Quest = { { name: "Summon Sugar Sheets", completed: () => - !have($skill`Summon Sugar Sheets`) || - get("instant_saveSugar", false) || - get("tomeSummons") >= 3 || - (have($skill`Summon Clip Art`) && !get("instant_saveClipArt", false)), + !have($skill`Summon Sugar Sheets`) || args.savesugar || get("tomeSummons") >= 3, do: (): void => { const sheetsToMake = 3 - get("tomeSummons"); restoreMp(2 * sheetsToMake); @@ -319,9 +294,7 @@ export const RunStartQuest: Quest = { { name: "Pantogramming", completed: () => - Pantogram.havePants() || - !have($item`portable pantogram`) || - get("instant_savePantogram", false), + Pantogram.havePants() || !have($item`portable pantogram`) || args.savepantogramming, do: (): void => { Pantogram.makePants( myPrimestat().toString(), @@ -338,7 +311,7 @@ export const RunStartQuest: Quest = { completed: () => get("_mummeryMods").includes(`Experience (${myPrimestat().toString()})`) || !have($item`mumming trunk`) || - get("instant_saveMummingTrunk", false), + args.savemumming, do: (): void => { const statString = statToMaximizerString(myPrimestat()); cliExecute(`mummery ${statString}`); @@ -439,7 +412,7 @@ export const RunStartQuest: Quest = { completed: () => [$item.none, $item`packet of mushroom spores`].includes(getGarden()) || getCampground()[getGarden().name] === 0 || - get("instant_saveGarden", false), + args.savegarden, do: () => cliExecute("garden pick"), limit: { tries: 1 }, }, @@ -458,8 +431,8 @@ export const RunStartQuest: Quest = { }, { name: "Set Asdon Workshed", - completed: () => - getWorkshed() === $item`Asdon Martin keyfob` || !get("instant_useAsdon", false), + ready: () => args.asdon, + completed: () => getWorkshed() === $item`Asdon Martin keyfob`, do: () => use($item`Asdon Martin keyfob`), }, { @@ -474,8 +447,8 @@ export const RunStartQuest: Quest = { completed: () => !have($item`model train set`) || (getWorkshed() === $item`model train set` && !canConfigure()) || - get("instant_skipBorrowedTime", false) || - get("instant_useAsdon", false), + args.skipbt || + args.asdon, do: (): void => { const statStation: Station = { Muscle: Station.BRAWN_SILO, @@ -527,7 +500,7 @@ export const RunStartQuest: Quest = { !have($skill`Map the Monsters`) || get("_monstersMapped") >= 3 || have($item`cherry`) || - get("instant_skipBorrowedTime", false) || + args.skipbt || (() => { // if we have another skeleton in the ice house, we don't need to map a novelty skeleton const banishes = get("banishedMonsters").split(":"); @@ -551,7 +524,7 @@ export const RunStartQuest: Quest = { modifier: `${baseOutfit().modifier}, -equip miniature crystal ball`, }), post: (): void => { - if (have($item`MayDay™ supply package`) && !get("instant_saveMayday", false)) + if (have($item`MayDay™ supply package`) && !args.savemayday) use($item`MayDay™ supply package`, 1); if (have($item`space blanket`)) autosell($item`space blanket`, 1); }, @@ -570,7 +543,7 @@ export const RunStartQuest: Quest = { if (get("_snokebombUsed") === 0) restoreMp(50); if (haveEquipped($item`miniature crystal ball`)) equip($slot`familiar`, $item.none); }, - completed: () => get("instant_skipBorrowedTime", false) || have($item`cherry`), + completed: () => args.skipbt || have($item`cherry`), do: $location`The Skeleton Store`, combat: new CombatStrategy().macro(() => Macro.if_( @@ -604,7 +577,7 @@ export const RunStartQuest: Quest = { }; }, post: (): void => { - if (have($item`MayDay™ supply package`) && !get("instant_saveMayday", false)) + if (have($item`MayDay™ supply package`) && !args.savemayday) use($item`MayDay™ supply package`, 1); if (have($item`space blanket`)) autosell($item`space blanket`, 1); }, @@ -624,7 +597,7 @@ export const RunStartQuest: Quest = { { name: "Get Distilled Fortified Wine", ready: () => have($item`11-leaf clover`) || have($effect`Lucky!`), - completed: () => myInebriety() >= 1 || get("instant_skipDistilledFortifiedWine", false), + completed: () => myInebriety() >= 1 || args.fortifiedwine, do: (): void => { if (!have($effect`Lucky!`)) use($item`11-leaf clover`); if (!have($item`distilled fortified wine`)) adv1($location`The Sleazy Back Alley`, -1); @@ -643,9 +616,7 @@ export const RunStartQuest: Quest = { }, ready: () => getKramcoWandererChance() >= 1.0, completed: () => - getKramcoWandererChance() < 1.0 || - !have($item`Kramco Sausage-o-Matic™`) || - get("instant_skipBorrowedTime", false), + getKramcoWandererChance() < 1.0 || !have($item`Kramco Sausage-o-Matic™`) || args.skipbt, do: $location`Noob Cave`, outfit: () => ({ ...baseOutfit(), diff --git a/src/tasks/spelldamage.ts b/src/tasks/spelldamage.ts index ea50768..9d460e4 100644 --- a/src/tasks/spelldamage.ts +++ b/src/tasks/spelldamage.ts @@ -40,6 +40,7 @@ import { Quest } from "../engine/task"; import { checkTurnSave, checkValue, + forbiddenEffects, logTestSetup, shouldFeelLost, shrugAT, @@ -48,11 +49,11 @@ import { } from "../lib"; import Macro, { haveFreeBanish, haveMotherSlimeBanish } from "../combat"; import { chooseFamiliar, sugarItemsAboutToBreak } from "../engine/outfit"; -import { forbiddenEffects } from "../resources"; +import { args } from "../args"; let triedDeepDark = false; // eslint-disable-next-line no-octal -const barrels = [0o0, 0o1, 0o2, 10, 11, 12, 20, 21, 22]; +const barrels = [10, 11, 12, 20, 21, 22]; export const SpellDamageQuest: Quest = { name: "Spell Damage", @@ -116,13 +117,13 @@ export const SpellDamageQuest: Quest = { prepare: (): void => { restoreHp(clamp(1000, myMaxhp() / 2, myMaxhp())); restoreMp(50); - Clan.join(get("instant_motherSlimeClan", "")); + Clan.join(args.motherclan); }, completed: () => !have($familiar`Machine Elf`) || !haveMotherSlimeBanish() || have($effect`Inner Elf`) || - get("instant_motherSlimeClan", "").length === 0, + get(args.motherclan).length === 0, do: $location`The Slime Tube`, combat: new CombatStrategy().macro( Macro.trySkill($skill`KGB tranquilizer dart`) @@ -275,7 +276,7 @@ export const SpellDamageQuest: Quest = { }, completed: () => CommunityService.SpellDamage.isDone(), do: (): void => { - const maxTurns = get("instant_spellTestTurnLimit", 55); + const maxTurns = args.spelldmglimit; const testTurns = CommunityService.SpellDamage.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/src/tasks/stat.ts b/src/tasks/stat.ts index 6806d2d..db5e549 100644 --- a/src/tasks/stat.ts +++ b/src/tasks/stat.ts @@ -27,12 +27,13 @@ import { import { Quest } from "../engine/task"; import { checkValue, + forbiddenEffects, logTestSetup, reagentBalancerEffect, reagentBalancerItem, tryAcquiringEffect, } from "../lib"; -import { forbiddenEffects } from "../resources"; +import { args } from "../args"; function useBalancerForTest(testStat: Stat): void { if (testStat === myPrimestat()) { @@ -71,7 +72,7 @@ export const HPQuest: Quest = { usefulEffects.forEach((ef) => tryAcquiringEffect(ef, true)); }, do: (): void => { - const maxTurns = get("instant_hpTestTurnLimit", 1); + const maxTurns = args.hplimit; const testTurns = CommunityService.HP.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); @@ -132,7 +133,7 @@ export const MuscleQuest: Quest = { CommunityService.Muscle.turnsSavedBy($effect`Purity of Spirit`) >= 7 && have($skill`Summon Clip Art`) && get("tomeSummons") === 0 && - get("instant_skipBorrowedTime", false) + args.skipbt ) { create($item`cold-filtered water`, 1); use($item`cold-filtered water`, 1); @@ -146,7 +147,7 @@ export const MuscleQuest: Quest = { tryAcquiringEffect($effect`Hulkien`); }, do: (): void => { - const maxTurns = get("instant_musTestTurnLimit", 2); + const maxTurns = args.musclelimit; const testTurns = CommunityService.Muscle.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); @@ -212,7 +213,7 @@ export const MysticalityQuest: Quest = { CommunityService.Mysticality.turnsSavedBy($effect`Purity of Spirit`) >= 7 && have($skill`Summon Clip Art`) && get("tomeSummons") === 0 && - get("instant_skipBorrowedTime", false) + args.skipbt ) { create($item`cold-filtered water`, 1); use($item`cold-filtered water`, 1); @@ -226,7 +227,7 @@ export const MysticalityQuest: Quest = { tryAcquiringEffect($effect`Hulkien`); }, do: (): void => { - const maxTurns = get("instant_mystTestTurnLimit", 1); + const maxTurns = args.myslimit; const testTurns = CommunityService.Mysticality.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); @@ -258,11 +259,7 @@ export const MoxieQuest: Quest = { { // This is also useful for the BoozeDrop test, but we can grab the +10%mox here first name: "High Heels", - completed: () => - have($item`red-soled high heels`) || - !have($item`2002 Mr. Store Catalog`) || - get("availableMrStore2002Credits", 0) <= get("instant_saveCatalogCredits", 0) || - get("instant_skipHighHeels", false), + completed: () => have($item`red-soled high heels`) || !have($item`2002 Mr. Store Catalog`), do: (): void => { if (!have($item`Letter from Carrie Bradshaw`)) { buy($coinmaster`Mr. Store 2002`, 1, $item`Letter from Carrie Bradshaw`); @@ -274,10 +271,8 @@ export const MoxieQuest: Quest = { { name: "Loathing Idol Microphone", completed: () => - have($effect`Spitting Rhymes`) || + have($effect`Poppy Performance`) || !have($item`2002 Mr. Store Catalog`) || - (get("availableMrStore2002Credits", 0) <= get("instant_saveCatalogCredits", 0) && - !have($item`Loathing Idol Microphone`)) || forbiddenEffects.includes($effect`Poppy Performance`) || checkValue("2002", Math.min(2, CommunityService.Moxie.prediction)), do: (): void => { @@ -332,7 +327,7 @@ export const MoxieQuest: Quest = { CommunityService.Moxie.turnsSavedBy($effect`Purity of Spirit`) >= 7 && have($skill`Summon Clip Art`) && get("tomeSummons") === 0 && - get("instant_skipBorrowedTime", false) + args.skipbt ) { create($item`cold-filtered water`, 1); use($item`cold-filtered water`, 1); @@ -346,7 +341,7 @@ export const MoxieQuest: Quest = { tryAcquiringEffect($effect`Hulkien`); }, do: (): void => { - const maxTurns = get("instant_moxTestTurnLimit", 5); + const maxTurns = args.moxielimit; const testTurns = CommunityService.Moxie.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/src/tasks/weapondamage.ts b/src/tasks/weapondamage.ts index 424826d..ed797b1 100644 --- a/src/tasks/weapondamage.ts +++ b/src/tasks/weapondamage.ts @@ -37,13 +37,14 @@ import { checkLocketAvailable, checkTurnSave, checkValue, + forbiddenEffects, logTestSetup, shouldFeelLost, startingClan, tryAcquiringEffect, wishFor, } from "../lib"; -import { forbiddenEffects } from "../resources"; +import { args } from "../args"; export const WeaponDamageQuest: Quest = { name: "Weapon Damage", @@ -89,13 +90,13 @@ export const WeaponDamageQuest: Quest = { prepare: (): void => { restoreHp(clamp(1000, myMaxhp() / 2, myMaxhp())); restoreMp(50); - Clan.join(get("instant_motherSlimeClan", "")); + Clan.join(get(args.motherclan)); }, completed: () => !have($familiar`Machine Elf`) || !haveMotherSlimeBanish() || have($effect`Inner Elf`) || - get("instant_motherSlimeClan", "").length === 0, + args.motherclan.length === 0, do: $location`The Slime Tube`, combat: new CombatStrategy().macro( Macro.trySkill($skill`KGB tranquilizer dart`) @@ -230,15 +231,14 @@ export const WeaponDamageQuest: Quest = { ]; usefulEffects.forEach((ef) => tryAcquiringEffect(ef, true)); - if (get("instant_experimentPulls", false)) - if ( - get("yourFavoriteBirdMods").includes("Weapon Damage Percent") && - checkValue( - "Favorite Bird", - Math.min(4, Math.max(0, CommunityService.WeaponDamage.actualCost())) - ) + if ( + get("yourFavoriteBirdMods").includes("Weapon Damage Percent") && + checkValue( + "Favorite Bird", + Math.min(4, Math.max(0, CommunityService.WeaponDamage.actualCost())) ) - useSkill($skill`Visit your Favorite Bird`); + ) + useSkill($skill`Visit your Favorite Bird`); $effects`Spit Upon, Pyramid Power, Outer Wolf™`.forEach((ef) => { if ( @@ -256,7 +256,7 @@ export const WeaponDamageQuest: Quest = { } if ( - get("instant_synthExperiment", false) && + args.experimentalsynth && CommunityService.WeaponDamage.actualCost() > 2 && get("tomeSummons") <= 1 && have($skill`Summon Sugar Sheets`) @@ -276,7 +276,7 @@ export const WeaponDamageQuest: Quest = { }, completed: () => CommunityService.WeaponDamage.isDone(), do: (): void => { - const maxTurns = get("instant_wpnTestTurnLimit", 35); + const maxTurns = args.weaponlimit; const testTurns = CommunityService.WeaponDamage.actualCost(); if (testTurns > maxTurns) { print(`Expected to take ${testTurns}, which is more than ${maxTurns}.`, "red"); diff --git a/webpack.config.js b/webpack.config.js index 01c1c7a..302547e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,20 +6,16 @@ const path = require("path"); const webpack = require("webpack"); // does this have a purpose? or can it just get deleted? const packageData = require("./package.json"); /* eslint-enable @typescript-eslint/no-var-requires */ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { merge } = require("webpack-merge"); -module.exports = { - entry: { - // Define files webpack will emit, does not need to correspond 1:1 with every typescript file - // You need an emitted file for each entrypoint into your code, e.g. the main script and the ccs or ccs consult script it calls - folgercs: "./src/main.ts", - }, +const shared = { // Turns on tree-shaking and minification in the default Terser minifier // https://webpack.js.org/plugins/terser-webpack-plugin/ mode: "production", devtool: false, output: { - path: path.resolve(__dirname, "KoLmafia", "scripts", packageData.name), - filename: "folgercs.js", + filename: "[name].js", libraryTarget: "commonjs", }, resolve: { @@ -51,3 +47,33 @@ module.exports = { // "canadv.ash": "commonjs canadv.ash", }, }; + +const entry = merge( + { + entry: { + // Define files webpack will emit, does not need to correspond 1:1 with every typescript file + // You need an emitted file for each entrypoint into your code, e.g. the main script and the ccs or ccs consult script it calls + folgerCS: "./src/main.ts", + }, + output: { + path: path.resolve(__dirname, "KoLmafia", "scripts", packageData.name), + }, + }, + shared +); + +const relay = merge( + { + entry: { + // Define files webpack will emit, does not need to correspond 1:1 with every typescript file + // You need an emitted file for each entrypoint into your code, e.g. the main script and the ccs or ccs consult script it calls + relay_folgerCS: "./src/relay.ts", + }, + output: { + path: path.resolve(__dirname, "KoLmafia", "relay"), + }, + }, + shared +); + +module.exports = [entry, relay]; diff --git a/yarn.lock b/yarn.lock index 647937b..04cf914 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2334,6 +2334,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +mafia-shared-relay@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mafia-shared-relay/-/mafia-shared-relay-0.0.7.tgz#1a0d333ebeed8cdbb4506360376099108c72d95b" + integrity sha512-sz5a+5tlI7u8eMasVVUdItPq5VdekLx6cDYCCbIjqhiFLluCE+0I1IqZ4n0JDzwLuiUZmAcsIq0VZfAp9WlbKA== + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"