diff --git a/question_answering/function.yaml b/question_answering/function.yaml index aa225696..a3361415 100644 --- a/question_answering/function.yaml +++ b/question_answering/function.yaml @@ -2,7 +2,7 @@ kind: job metadata: name: question-answering tag: '' - hash: 6e2ec4caae94fa11a49c835ce6ad92029068ce65 + hash: 90e67d116b256a98da7d5819724e43df01d8b4eb project: '' labels: author: yonish @@ -13,7 +13,7 @@ spec: args: [] image: '' build: - functionSourceCode: IyBDb3B5cmlnaHQgMjAyMyBJZ3VhemlvCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMAojCiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQojIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuCiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZAojIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgppbXBvcnQgbG9nZ2luZwppbXBvcnQgb3BlcmF0b3IKaW1wb3J0IHBhdGhsaWIKZnJvbSBmdW5jdG9vbHMgaW1wb3J0IHJlZHVjZSwgd3JhcHMKZnJvbSB0eXBpbmcgaW1wb3J0IEFueSwgRGljdCwgTGlzdCwgVHVwbGUsIFVuaW9uCgppbXBvcnQgcGFuZGFzIGFzIHBkCmltcG9ydCB0cmFuc2Zvcm1lcnMKZnJvbSB0cWRtIGltcG9ydCB0cWRtCgojIEdldCB0aGUgZ2xvYmFsIGxvZ2dlcjoKX0xPR0dFUiA9IGxvZ2dpbmcuZ2V0TG9nZ2VyKCkKCgpkZWYgX2NoZWNrX21scnVuX2FuZF9vcGVuX21waSgpIC0+IFR1cGxlWyJtbHJ1bi5NTENsaWVudEN0eCIsICJtcGk0cHkuTVBJLkludHJhY29tbSJdOgogICAgZ2xvYmFsIF9MT0dHRVIKCiAgICBpc19tcGkgPSBGYWxzZQogICAgdHJ5OgogICAgICAgIGltcG9ydCBtbHJ1bgoKICAgICAgICBjb250ZXh0ID0gbWxydW4uZ2V0X29yX2NyZWF0ZV9jdHgobmFtZT0ibWxydW4iKQogICAgICAgIF9MT0dHRVIgPSBjb250ZXh0LmxvZ2dlcgogICAgICAgIGlzX21waSA9IGNvbnRleHQubGFiZWxzLmdldCgia2luZCIsICJqb2IiKSA9PSAibXBpam9iIgoKICAgICAgICBpZiBpc19tcGk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGZyb20gbXBpNHB5IGltcG9ydCBNUEkKCiAgICAgICAgICAgICAgICByZXR1cm4gY29udGV4dCwgTVBJLkNPTU1fV09STEQKICAgICAgICAgICAgZXhjZXB0IE1vZHVsZU5vdEZvdW5kRXJyb3IgYXMgbXBpNHB5X25vdF9mb3VuZDoKICAgICAgICAgICAgICAgIGNvbnRleHQubG9nZ2VyLmVycm9yKAogICAgICAgICAgICAgICAgICAgICJUbyBkaXN0cmlidXRlIHRoZSBmdW5jdGlvbiB1c2luZyBNTFJ1bidzICdtcGlqb2InIHlvdSBuZWVkIHRvIGhhdmUgYG1waTRweWAgcGFja2FnZSBpbiB5b3VyICIKICAgICAgICAgICAgICAgICAgICAiaW50ZXJwcmV0ZXIuIFBsZWFzZSBydW4gYHBpcCBpbnN0YWxsIG1waTRweWAgYW5kIG1ha2Ugc3VyZSB5b3UgaGF2ZSBvcGVuLW1waS4iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICByYWlzZSBtcGk0cHlfbm90X2ZvdW5kCiAgICBleGNlcHQgTW9kdWxlTm90Rm91bmRFcnJvciBhcyBtb2R1bGVfbm90X2ZvdW5kOgogICAgICAgIGlmIGlzX21waToKICAgICAgICAgICAgcmFpc2UgbW9kdWxlX25vdF9mb3VuZAogICAgcmV0dXJuIE5vbmUsIE5vbmUKCgpkZWYgb3Blbl9tcGlfaGFuZGxlcigKICAgIHdvcmtlcl9pbnB1dHM6IExpc3Rbc3RyXSwgcm9vdF93b3JrZXJfaW5wdXRzOiBEaWN0W3N0ciwgQW55XSA9IE5vbmUKKToKICAgIGdsb2JhbCBfTE9HR0VSCgogICAgIyBDaGVjayBmb3IgTUxSdW4gYW5kIE9wZW5NUEkgYXZhaWxhYmlsaXR5OgogICAgY29udGV4dCwgY29tbSA9IF9jaGVja19tbHJ1bl9hbmRfb3Blbl9tcGkoKQoKICAgIGRlZiBkZWNvcmF0b3IoaGFuZGxlcik6CiAgICAgICAgaWYgY29tbSBpcyBOb25lIG9yIGNvbW0uR2V0X3NpemUoKSA9PSAxOgogICAgICAgICAgICByZXR1cm4gaGFuZGxlcgoKICAgICAgICBAd3JhcHMoaGFuZGxlcikKICAgICAgICBkZWYgd3JhcHBlcigqKmt3YXJncyk6CiAgICAgICAgICAgICMgR2V0IHRoZSBvcGVuIG1waSBlbnZpcm9ubWVudCBwcm9wZXJ0aWVzOgogICAgICAgICAgICBzaXplID0gY29tbS5HZXRfc2l6ZSgpCiAgICAgICAgICAgIHJhbmsgPSBjb21tLkdldF9yYW5rKCkKCiAgICAgICAgICAgICMgR2l2ZSB0aGUgY29ycmVjdCBjaHVuayBvZiB0aGUgd29ya2VycyBpbnB1dHM6CiAgICAgICAgICAgIGZvciB3b3JrZXJfaW5wdXQgaW4gd29ya2VyX2lucHV0czoKICAgICAgICAgICAgICAgIGlucHV0X2FyZ3VtZW50ID0ga3dhcmdzW3dvcmtlcl9pbnB1dF0KICAgICAgICAgICAgICAgIGlmIGlucHV0X2FyZ3VtZW50IGlzIE5vbmU6CiAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoaW5wdXRfYXJndW1lbnQsIHN0cik6CiAgICAgICAgICAgICAgICAgICAgaW5wdXRfYXJndW1lbnQgPSBfZ2V0X3RleHRfZmlsZXMoCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFfcGF0aD1wYXRobGliLlBhdGgoaW5wdXRfYXJndW1lbnQpLmFic29sdXRlKCkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBpZiBsZW4oaW5wdXRfYXJndW1lbnQpIDwgc2l6ZToKICAgICAgICAgICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICAgICAgICAgICAgICBmIkNhbm5vdCBzcGxpdCB0aGUgaW5wdXQgJ3t3b3JrZXJfaW5wdXR9JyBvZiBsZW5ndGgge2xlbihpbnB1dF9hcmd1bWVudCl9IHRvIHtzaXplfSB3b3JrZXJzLiAiCiAgICAgICAgICAgICAgICAgICAgICAgIGYiUGxlYXNlIHJlZHVjZSB0aGUgYW1vdW50IG9mIHdvcmtlcnMgZm9yIHRoaXMgaW5wdXQuIgogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGV2ZW5fY2h1bmtfc2l6ZSA9IGxlbihpbnB1dF9hcmd1bWVudCkgLy8gc2l6ZQogICAgICAgICAgICAgICAgY2h1bmtfc3RhcnQgPSByYW5rICogZXZlbl9jaHVua19zaXplCiAgICAgICAgICAgICAgICBjaHVua19lbmQgPSAoCiAgICAgICAgICAgICAgICAgICAgKHJhbmsgKyAxKSAqIGV2ZW5fY2h1bmtfc2l6ZQogICAgICAgICAgICAgICAgICAgIGlmIHJhbmsgKyAxIDwgc2l6ZQogICAgICAgICAgICAgICAgICAgIGVsc2UgbGVuKGlucHV0X2FyZ3VtZW50KQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgY29udGV4dC5sb2dnZXIuaW5mbygKICAgICAgICAgICAgICAgICAgICBmIlJhbmsgI3tyYW5rfTogUHJvY2Vzc2luZyBpbnB1dCBjaHVuayBvZiAne3dvcmtlcl9pbnB1dH0nICIKICAgICAgICAgICAgICAgICAgICBmImZyb20gaW5kZXgge2NodW5rX3N0YXJ0fSB0byB7Y2h1bmtfZW5kfS4iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBpZiBpc2luc3RhbmNlKGlucHV0X2FyZ3VtZW50LCBsaXN0KToKICAgICAgICAgICAgICAgICAgICBpbnB1dF9hcmd1bWVudCA9IGlucHV0X2FyZ3VtZW50W2NodW5rX3N0YXJ0OmNodW5rX2VuZF0KICAgICAgICAgICAgICAgIGVsaWYgaXNpbnN0YW5jZShpbnB1dF9hcmd1bWVudCwgcGQuRGF0YUZyYW1lKToKICAgICAgICAgICAgICAgICAgICBpbnB1dF9hcmd1bWVudCA9IGlucHV0X2FyZ3VtZW50Lmlsb2NbY2h1bmtfc3RhcnQ6Y2h1bmtfZW5kOiwgOl0KICAgICAgICAgICAgICAgIGt3YXJnc1t3b3JrZXJfaW5wdXRdID0gaW5wdXRfYXJndW1lbnQKCiAgICAgICAgICAgICMgU2V0IHRoZSByb290IHdvcmtlciBvbmx5IGFyZ3VtZW50czoKICAgICAgICAgICAgaWYgcmFuayA9PSAwIGFuZCByb290X3dvcmtlcl9pbnB1dHM6CiAgICAgICAgICAgICAgICBrd2FyZ3MudXBkYXRlKHJvb3Rfd29ya2VyX2lucHV0cykKCiAgICAgICAgICAgICMgUnVuIHRoZSB3b3JrZXI6CiAgICAgICAgICAgIG91dHB1dCA9IGhhbmRsZXIoKiprd2FyZ3MpCgogICAgICAgICAgICAjIFNlbmQgdGhlIG91dHB1dCB0byB0aGUgcm9vdCByYW5rIChyYW5rICMwKToKICAgICAgICAgICAgb3V0cHV0ID0gY29tbS5nYXRoZXIob3V0cHV0LCByb290PTApCiAgICAgICAgICAgIGlmIHJhbmsgPT0gMDoKICAgICAgICAgICAgICAgICMgSm9pbiB0aGUgb3V0cHV0czoKICAgICAgICAgICAgICAgIGNvbnRleHQubG9nZ2VyLmluZm8oIkNvbGxlY3RpbmcgZGF0YSBmcm9tIHdvcmtlcnMgdG8gcm9vdCB3b3JrZXIuIikKICAgICAgICAgICAgICAgIGRhdGFmcmFtZSA9IHBkLmNvbmNhdChvYmpzPVtkZiBmb3IgZGYsIF8gaW4gb3V0cHV0XSwgYXhpcz0wKQogICAgICAgICAgICAgICAgZXJyb3JzX2RpY3Rpb25hcnkgPSByZWR1Y2Uob3BlcmF0b3IuaW9yLCBbZXJyIGZvciBfLCBlcnIgaW4gb3V0cHV0XSwge30pCiAgICAgICAgICAgICAgICByZXR1cm4gZGF0YWZyYW1lLCBlcnJvcnNfZGljdGlvbmFyeQogICAgICAgICAgICByZXR1cm4gTm9uZQoKICAgICAgICByZXR1cm4gd3JhcHBlcgoKICAgIHJldHVybiBkZWNvcmF0b3IKCgpAb3Blbl9tcGlfaGFuZGxlcih3b3JrZXJfaW5wdXRzPVsiZGF0YV9wYXRoIl0sIHJvb3Rfd29ya2VyX2lucHV0cz17InZlcmJvc2UiOiBUcnVlfSkKZGVmIGFuc3dlcl9xdWVzdGlvbnMoCiAgICBkYXRhX3BhdGg6IFVuaW9uW3N0ciwgTGlzdFtzdHJdXSwKICAgIG1vZGVsX25hbWU6IHN0ciwKICAgIHF1ZXN0aW9uczogTGlzdFtzdHJdLAogICAgZGV2aWNlX21hcDogVW5pb25bc3RyLCBkaWN0XSA9IE5vbmUsCiAgICBtb2RlbF9rd2FyZ3M6IGRpY3QgPSBOb25lLAogICAgYXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aDogaW50ID0gTm9uZSwKICAgIHRva2VuaXplcl9uYW1lOiBzdHIgPSBOb25lLAogICAgdG9rZW5pemVyX2t3YXJnczogZGljdCA9IE5vbmUsCiAgICB0ZXh0X3dyYXBwZXI6IHN0ciA9ICIiLAogICAgcXVlc3Rpb25zX3dyYXBwZXI6IHN0ciA9ICIiLAogICAgZ2VuZXJhdGlvbl9jb25maWc6IGRpY3QgPSBOb25lLAogICAgYmF0Y2hfc2l6ZTogaW50ID0gMSwKICAgIHF1ZXN0aW9uc19jb2x1bW5zOiBMaXN0W3N0cl0gPSBOb25lLAogICAgdmVyYm9zZTogYm9vbCA9IEZhbHNlLAopIC0+IFR1cGxlW3BkLkRhdGFGcmFtZSwgZGljdF06CiAgICAiIiIKICAgIEFuc3dlciBxdWVzdGlvbnMgd2l0aCBhIGNvbnRleHQgdG8gdGhlIGdpdmVuIHRleHQgZmlsZXMgY29udGVudHMgYnkgYSBwcmV0cmFpbmVkIExMTSBtb2RlbC4gRWFjaCB0ZXh0IGZpbGUgd2lsbCBoYXZlCiAgICB0aGUgZm9sbG93aW5nIHByb21wdCBidWlsdDoKCiAgICBzdGFydCBvZiBgdGV4dF93cmFwcGVyYAogICAgPHRleHQgZmlsZSBjb250ZW50PgogICAgZW5kIG9mIGB0ZXh0X3dyYXBwZXJgCgogICAgc3RhcnQgb2YgYHF1ZXN0aW9uc193cmFwcGVyYAogICAgMS4gPHF1ZXN0aW9uc1swXT4KICAgIDIuIDxxdWVzdGlvbnNbMV0+CiAgICAuLi4KICAgIG4uIDxxdWVzdGlvbnNbbi0xXT4KICAgIGVuZCBvZiBgcXVlc3Rpb25zX3dyYXBwZXJgCgogICAgOnBhcmFtIGRhdGFfcGF0aDogICAgICAgICAgICAgICAgICAgICAgICAgIEEgcGF0aCB0byBhIGRpcmVjdG9yeSBvZiB0ZXh0IGZpbGVzIG9yIGEgcGF0aCB0byBhIHRleHQgZmlsZSB0byBhc2sKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdGlvbnMgYWJvdXQuCiAgICA6cGFyYW0gbW9kZWxfbmFtZTogICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHByZS10cmFpbmVkIG1vZGVsIG5hbWUgZnJvbSB0aGUgaHVnZ2luZ2ZhY2UgaHViIHRvIHVzZSBmb3IgYXNraW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3Rpb25zLgogICAgOnBhcmFtIHF1ZXN0aW9uczogICAgICAgICAgICAgICAgICAgICAgICAgIFRoZSBxdWVzdGlvbnMgdG8gYXNrLgogICAgOnBhcmFtIGRldmljZV9tYXA6ICAgICAgICAgICAgICAgICAgICAgICAgIEEgbWFwIHRvIHVzZSBmb3IgbG9hZGluZyB0aGUgbW9kZWwgb24gbXVsdGlwbGUgZGV2aWNlcy4KICAgIDpwYXJhbSBtb2RlbF9rd2FyZ3M6ICAgICAgICAgICAgICAgICAgICAgICBLZXl3b3JkIGFyZ3VtZW50cyB0byBwYXNzIGZvciBsb2FkaW5nIHRoZSBtb2RlbCB1c2luZyBIdWdnaW5nRmFjZSdzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYHRyYW5zZm9ybWVycy5BdXRvTW9kZWxGb3JDYXVzYWxMTS5mcm9tX3ByZXRyYWluZWRgIGZ1bmN0aW9uLgogICAgOnBhcmFtIGF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGg6IEZvciBBdXRvR1BUUSBtb2RlbHMgdG8gc2V0IGFuZCBleHRlbmQgdGhlIG1vZGVsJ3MgaW5wdXQgYnVmZmVyIHNpemUuCiAgICA6cGFyYW0gdG9rZW5pemVyX25hbWU6ICAgICAgICAgICAgICAgICAgICAgVGhlIHRva2VuaXplciBuYW1lIGZyb20gdGhlIGh1Z2dpbmdmYWNlIGh1YiB0byB1c2UuIElmIG5vdCBnaXZlbiwgdGhlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgbmFtZSB3aWxsIGJlIHVzZWQuCiAgICA6cGFyYW0gdG9rZW5pemVyX2t3YXJnczogICAgICAgICAgICAgICAgICAgS2V5d29yZCBhcmd1bWVudHMgdG8gcGFzcyBmb3IgbG9hZGluZyB0aGUgdG9rZW5pemVyIHVzaW5nIEh1Z2dpbmdGYWNlJ3MKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgdHJhbnNmb3JtZXJzLkF1dG9Ub2tlbml6ZXIuZnJvbV9wcmV0cmFpbmVkYCBmdW5jdGlvbi4KICAgIDpwYXJhbSB0ZXh0X3dyYXBwZXI6ICAgICAgICAgICAgICAgICAgICAgICBBIHdyYXBwZXIgZm9yIHRoZSBmaWxlJ3MgdGV4dC4gV2lsbCBiZSBhZGRlZCBhdCB0aGUgc3RhcnQgb2YgdGhlIHByb21wdC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNdXN0IGhhdmUgYSBwbGFjZWhvbGRlciAoJ3t9JykgZm9yIHRoZSB0ZXh0IG9mIHRoZSBmaWxlLgogICAgOnBhcmFtIHF1ZXN0aW9uc193cmFwcGVyOiAgICAgICAgICAgICAgICAgIEEgd3JhcHBlciBmb3IgdGhlIHF1ZXN0aW9ucyByZWNlaXZlZC4gV2lsbCBiZSBhZGRlZCBhZnRlciB0aGUgdGV4dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdyYXBwZXIgaW4gdGhlIHByb21wdCB0ZW1wbGF0ZS4gTXVzdCBoYXZlIGEgcGxhY2Vob2xkZXIgKCd7fScpIGZvciB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdGlvbnMuCiAgICA6cGFyYW0gZ2VuZXJhdGlvbl9jb25maWc6ICAgICAgICAgICAgICAgICAgSHVnZ2luZ0ZhY2UncyBgR2VuZXJhdGlvbkNvbmZpZ2Aga2V5d29yZCBhcmd1bWVudHMgdG8gcGFzcyB0byB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgZ2VuZXJhdGVgIG1ldGhvZC4KICAgIDpwYXJhbSBiYXRjaF9zaXplOiAgICAgICAgICAgICAgICAgICAgICAgICBCYXRjaCBzaXplIGZvciBpbmZlcmVuY2UuCiAgICA6cGFyYW0gcXVlc3Rpb25zX2NvbHVtbnM6ICAgICAgICAgICAgICAgICAgQ29sdW1ucyB0byB1c2UgZm9yIHRoZSBkYXRhZnJhbWUgcmV0dXJuZWQuCiAgICA6cGFyYW0gdmVyYm9zZTogICAgICAgICAgICAgICAgICAgICAgICAgICAgV2hldGhlciB0byBwcmVzZW50IGxvZ3Mgb2YgYSBwcm9ncmVzcyBiYXIgYW5kIGVycm9ycy4gRGVmYXVsdDogVHJ1ZS4KCgogICAgOnJldHVybnM6IEEgdHVwbGUgb2Y6CgogICAgICAgICAgICAgICogQSBkYXRhZnJhbWUgZGF0YXNldCBvZiB0aGUgcXVlc3Rpb25zIGFuc3dlcnMuCiAgICAgICAgICAgICAgKiBBIGRpY3Rpb25hcnkgb2YgZXJyb3JlZCBmaWxlcyB0aGF0IHdlcmUgbm90IGluZmVycmVkIG9yIHdlcmUgbm90IGFuc3dlcmVkIHByb3Blcmx5LgogICAgIiIiCiAgICBnbG9iYWwgX0xPR0dFUgoKICAgICMgR2V0IHRoZSBpbnB1dCB0ZXh0IGZpbGVzIHRvIHF1ZXN0aW9uOgogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oIkNvbGxlY3RpbmcgdGV4dCBmaWxlcy4iKQogICAgaWYgaXNpbnN0YW5jZShkYXRhX3BhdGgsIHN0cik6CiAgICAgICAgZGF0YV9wYXRoID0gcGF0aGxpYi5QYXRoKGRhdGFfcGF0aCkuYWJzb2x1dGUoKQogICAgICAgIHRleHRfZmlsZXMgPSBfZ2V0X3RleHRfZmlsZXMoZGF0YV9wYXRoPWRhdGFfcGF0aCkKICAgIGVsc2U6CiAgICAgICAgdGV4dF9maWxlcyA9IGRhdGFfcGF0aAogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oZiJDb2xsZWN0ZWQge2xlbih0ZXh0X2ZpbGVzKX0gdGV4dCBmaWxlcy4iKQoKICAgICMgR2V0IHRoZSBwcm9tcHQgdGVtcGxhdGU6CiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbygiQ3JlYXRpbmcgcHJvbXB0IHRlbXBsYXRlLiIpCiAgICBwcm9tcHRfdGVtcGxhdGUgPSBfZ2V0X3Byb21wdF90ZW1wbGF0ZSgKICAgICAgICB0ZXh0X3dyYXBwZXI9dGV4dF93cmFwcGVyLAogICAgICAgIHF1ZXN0aW9uc193cmFwcGVyPXF1ZXN0aW9uc193cmFwcGVyLAogICAgICAgIHF1ZXN0aW9ucz1xdWVzdGlvbnMsCiAgICApCiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbyhmIlByb21wdCB0ZW1wbGF0ZSBjcmVhdGVkOlxuXG57cHJvbXB0X3RlbXBsYXRlfVxuIikKCiAgICAjIEdldCB0aGUgcXVlc3Rpb25zIGNvbHVtbnM6CiAgICBxdWVzdGlvbnNfY29sdW1ucyA9IHF1ZXN0aW9uc19jb2x1bW5zIG9yIFsKICAgICAgICBmInF7aX0iIGZvciBpIGluIHJhbmdlKDEsIGxlbihxdWVzdGlvbnMpICsgMSkKICAgIF0KICAgIGlmIGxlbihxdWVzdGlvbnNfY29sdW1ucykgIT0gbGVuKHF1ZXN0aW9ucyk6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgZiJUaGUgcHJvdmlkZWQgcXVlc3Rpb25zIGNvbHVtbnMgbGVuZ3RoICh7bGVuKHF1ZXN0aW9uc19jb2x1bW5zKX0pICIKICAgICAgICAgICAgZiJkb2VzIG5vdCBtYXRjaCB0aGUgcXVlc3Rpb25zIGFtb3VudCAoe2xlbihxdWVzdGlvbnMpfSkiCiAgICAgICAgKQoKICAgICMgTG9hZCB0aGUgZ2VuZXJhdGlvbiBjb25maWc6CiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbygiTG9hZGluZyBnZW5lcmF0aW9uIGNvbmZpZ3VyYXRpb24uIikKICAgIGdlbmVyYXRpb25fY29uZmlnID0gdHJhbnNmb3JtZXJzLkdlbmVyYXRpb25Db25maWcoKiooZ2VuZXJhdGlvbl9jb25maWcgb3Ige30pKQogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oZiJHZW5lcmF0aW9uIGNvbmZpZ3VyYXRpb24gbG9hZGVkOiB7Z2VuZXJhdGlvbl9jb25maWd9IikKCiAgICAjIExvYWQgdGhlIG1vZGVsIGFuZCB0b2tlbml6ZXIgaW50byBhIHBpcGVsaW5lIG9iamVjdDoKICAgIGlmIHZlcmJvc2U6CiAgICAgICAgX0xPR0dFUi5pbmZvKGYiTG9hZGluZyBtb2RlbCAne21vZGVsX25hbWV9Jy4iKQogICAgZ2VuZXJhdGlvbl9waXBlbGluZSA9IF9nZXRfZ2VuZXJhdGlvbl9waXBlbGluZSgKICAgICAgICBtb2RlbF9uYW1lPW1vZGVsX25hbWUsCiAgICAgICAgZGV2aWNlX21hcD1kZXZpY2VfbWFwLAogICAgICAgIHRva2VuaXplcl9uYW1lPXRva2VuaXplcl9uYW1lIG9yIG1vZGVsX25hbWUsCiAgICAgICAgbW9kZWxfa3dhcmdzPW1vZGVsX2t3YXJncyBvciB7fSwKICAgICAgICB0b2tlbml6ZXJfa3dhcmdzPXRva2VuaXplcl9rd2FyZ3Mgb3Ige30sCiAgICAgICAgYXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aD1hdXRvX2dwdHFfZXhsbGFtYV9tYXhfaW5wdXRfbGVuZ3RoLAogICAgICAgIGJhdGNoX3NpemU9YmF0Y2hfc2l6ZSwKICAgICkKICAgIGlmIHZlcmJvc2U6CiAgICAgICAgX0xPR0dFUi5pbmZvKCJNb2RlbCBsb2FkZWQuIikKCiAgICAjIFByZXBhcmUgdGhlIHN1Y2Nlc3NlcyBkYXRhZnJhbWUgYW5kIGVycm9ycyBkaWN0aW9uYXJ5IHRvIGJlIHJldHVybmVkOgogICAgc3VjY2Vzc2VzID0gW10KICAgIGVycm9ycyA9IHt9CgogICAgIyBTcGxpdCB0aGUgZmlsZXMgaW50byBiYXRjaGVzOgogICAgZmlsZV9iYXRjaGVzID0gWwogICAgICAgIHRleHRfZmlsZXNbaSA6IGkgKyBiYXRjaF9zaXplXQogICAgICAgIGlmIGkgKyBiYXRjaF9zaXplIDwgbGVuKHRleHRfZmlsZXMpCiAgICAgICAgZWxzZSB0ZXh0X2ZpbGVzW2k6XQogICAgICAgIGZvciBpIGluIHJhbmdlKDAsIGxlbih0ZXh0X2ZpbGVzKSwgYmF0Y2hfc2l6ZSkKICAgIF0KCiAgICAjIEdvIG92ZXIgdGhlIGJhdGNoZXMgb2YgdGV4dCBmaWxlcyBhbmQgcXVlc3Rpb24gdGhlbToKICAgIHF1ZXN0aW9uc19hbW91bnQgPSBsZW4ocXVlc3Rpb25zX2NvbHVtbnMpCiAgICBmb3IgZmlsZV9iYXRjaCBpbiB0cWRtKAogICAgICAgIGZpbGVfYmF0Y2hlcywKICAgICAgICBkZXNjPSJHZW5lcmF0aW5nIGFuc3dlcnMiLAogICAgICAgIHVuaXQ9ZiJmaWxlIChiYXRjaCBvZiB7YmF0Y2hfc2l6ZX0pIiwKICAgICAgICBkaXNhYmxlPW5vdCB2ZXJib3NlLAogICAgKToKICAgICAgICB0cnk6CiAgICAgICAgICAgICMgUmVhZCBiYXRjaCAocmVhZCB0aGUgdGV4dCBmcm9tIHRoZSB0ZXh0IGZpbGVzKToKICAgICAgICAgICAgYmF0Y2hlZF9pbnB1dCA9IF9yZWFkX2ZpbGVfYmF0Y2goCiAgICAgICAgICAgICAgICBmaWxlX2JhdGNoPWZpbGVfYmF0Y2gsIHByb21wdF90ZW1wbGF0ZT1wcm9tcHRfdGVtcGxhdGUKICAgICAgICAgICAgKQogICAgICAgICAgICAjIEluZmVyIGJhdGNoOgogICAgICAgICAgICBiYXRjaGVkX2Fuc3dlcnMgPSBfYW5zd2VyX3F1ZXN0aW9ucygKICAgICAgICAgICAgICAgIHF1ZXN0aW9uc19hbW91bnQ9cXVlc3Rpb25zX2Ftb3VudCwKICAgICAgICAgICAgICAgIGJhdGNoZWRfaW5wdXQ9YmF0Y2hlZF9pbnB1dCwKICAgICAgICAgICAgICAgIGdlbmVyYXRpb25fcGlwZWxpbmU9Z2VuZXJhdGlvbl9waXBlbGluZSwKICAgICAgICAgICAgICAgIGdlbmVyYXRpb25fY29uZmlnPWdlbmVyYXRpb25fY29uZmlnLAogICAgICAgICAgICApCiAgICAgICAgICAgICMgQ29sbGVjdCBpdCB0byB0aGUgc3VjY2Vzc2VzOgogICAgICAgICAgICBzdWNjZXNzZXMgKz0gWwogICAgICAgICAgICAgICAgW2ZpbGUubmFtZSwgKmFuc3dlcnNdCiAgICAgICAgICAgICAgICBmb3IgZmlsZSwgYW5zd2VycyBpbiB6aXAoZmlsZV9iYXRjaCwgYmF0Y2hlZF9hbnN3ZXJzKQogICAgICAgICAgICBdCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGNlcHRpb246CiAgICAgICAgICAgICMgTm90ZSB0aGUgZXhjZXB0aW9uIGFzIGVycm9yIGluIHRoZSBkaWN0aW9uYXJ5OgogICAgICAgICAgICBiYXRjaF9maWxlX25hbWVzID0gIiwgIi5qb2luKFtmaWxlLm5hbWUgZm9yIGZpbGUgaW4gZmlsZV9iYXRjaF0pCiAgICAgICAgICAgIGlmIHZlcmJvc2U6CiAgICAgICAgICAgICAgICBfTE9HR0VSLndhcm5pbmcoCiAgICAgICAgICAgICAgICAgICAgZiJFcnJvciBpbiBiYXRjaCAne2JhdGNoX2ZpbGVfbmFtZXN9Jzoge3N0cihleGNlcHRpb24pfSIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgZXJyb3JzW2JhdGNoX2ZpbGVfbmFtZXNdID0gc3RyKGV4Y2VwdGlvbikKICAgICAgICAgICAgY29udGludWUKCiAgICAjIENvbnN0cnVjdCB0aGUgYW5zd2VycyBkYXRhZnJhbWU6CiAgICBjb2x1bW5zID0gWwogICAgICAgICJ0ZXh0X2ZpbGUiLAogICAgICAgICpxdWVzdGlvbnNfY29sdW1ucywKICAgIF0KICAgIHN1Y2Nlc3NlcyA9IHBkLkRhdGFGcmFtZSgKICAgICAgICBzdWNjZXNzZXMsCiAgICAgICAgY29sdW1ucz1jb2x1bW5zLAogICAgKQoKICAgICMgUHJpbnQgdGhlIGhlYWQgb2YgdGhlIHByb2R1Y2VkIGRhdGFmcmFtZSBhbmQgcmV0dXJuOgogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oCiAgICAgICAgICAgIGYiRG9uZSAoe3N1Y2Nlc3Nlcy5zaGFwZVswXX0ve2xlbih0ZXh0X2ZpbGVzKX0pXG4iCiAgICAgICAgICAgIGYiQW5zd2VycyBzdW1tYXJ5OlxuIgogICAgICAgICAgICBmIntzdWNjZXNzZXMuaGVhZCgpfSIKICAgICAgICApCiAgICByZXR1cm4gc3VjY2Vzc2VzLCBlcnJvcnMKCgpkZWYgX2dldF90ZXh0X2ZpbGVzKAogICAgZGF0YV9wYXRoOiBwYXRobGliLlBhdGgsCikgLT4gTGlzdFtwYXRobGliLlBhdGhdOgogICAgIyBDaGVjayBpZiB0aGUgcGF0aCBpcyBvZiBhIGRpcmVjdG9yeSBvciBhIGZpbGU6CiAgICBpZiBkYXRhX3BhdGguaXNfZGlyKCk6CiAgICAgICAgIyBHZXQgYWxsIGZpbGVzIGluc2lkZSB0aGUgZGlyZWN0b3J5OgogICAgICAgIHRleHRfZmlsZXMgPSBsaXN0KGRhdGFfcGF0aC5nbG9iKCIqLioiKSkKICAgIGVsaWYgZGF0YV9wYXRoLmlzX2ZpbGUoKToKICAgICAgICB0ZXh0X2ZpbGVzID0gW2RhdGFfcGF0aF0KICAgIGVsc2U6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgZiJVbnJlY29nbml6ZWQgZGF0YSBwYXRoLiBUaGUgcGFyYW1ldGVyIGBkYXRhX3BhdGhgIG11c3QgYmUgZWl0aGVyIGEgZGlyZWN0b3J5IHBhdGggb3IgYSBmaWxlIHBhdGguICIKICAgICAgICAgICAgZiJHaXZlbjoge3N0cihkYXRhX3BhdGgpfSAiCiAgICAgICAgKQoKICAgIHJldHVybiB0ZXh0X2ZpbGVzCgoKZGVmIF9nZXRfcHJvbXB0X3RlbXBsYXRlKAogICAgdGV4dF93cmFwcGVyOiBzdHIsCiAgICBxdWVzdGlvbnNfd3JhcHBlcjogc3RyLAogICAgcXVlc3Rpb25zOiBMaXN0W3N0cl0sCikgLT4gc3RyOgogICAgIyBWYWxpZGF0ZSBhbmQgYnVpbGQgdGhlIHRleHQgd3JhcHBlcjoKICAgIHRleHRfd3JhcHBlciA9IHRleHRfd3JhcHBlciBvciAoCiAgICAgICAgIkdpdmVuIHRoZSBmb2xsb3dpbmcgdGV4dDpcbiIgIi0tLS0tXG4iICJ7fVxuIiAiLS0tLS0iCiAgICApCiAgICBpZiB0ZXh0X3dyYXBwZXIuY291bnQoInt9IikgIT0gMToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICAiVGhlIGB0ZXh0X3dyYXBwZXJgIG11c3QgaW5jbHVkZSBvbmUgcGxhY2Vob2xkZXIgJ3t9JyBmb3IgdGhlIHRleHQgb2YgdGhlIGZpbGUgdG8gYmUgYXNrZWQgYWJvdXQuIgogICAgICAgICkKCiAgICAjIFZhbGlkYXRlIGFuZCBidWlsZCB0aGUgcXVlc3Rpb24gd3JhcHBlcjoKICAgIHF1ZXN0aW9uc193cmFwcGVyID0gcXVlc3Rpb25zX3dyYXBwZXIgb3IgIkFuc3dlciB0aGUgcXVlc3Rpb25zOlxuIiAie30iCiAgICBpZiBxdWVzdGlvbnNfd3JhcHBlci5jb3VudCgie30iKSAhPSAxOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJUaGUgYHF1ZXN0aW9uc193cmFwcGVyYCBtdXN0IGluY2x1ZGUgb25lIHBsYWNlaG9sZGVyICd7fScgZm9yIHRoZSBsaXN0IG9mIHF1ZXN0aW9ucy4iCiAgICAgICAgKQoKICAgICMgVmFsaWRhdGUgYW5kIHBhcnNlIHRoZSBxdWVzdGlvbnM6CiAgICBpZiBsZW4ocXVlc3Rpb25zKSA9PSAwOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoIlBsZWFzZSBpbmNsdWRlIGF0IGxlYXN0IG9uZSBxdWVzdGlvbi4iKQogICAgcXVlc3Rpb25zID0gIlxuIi5qb2luKAogICAgICAgIFtmIntpfS4ge3F1ZXN0aW9ufSIgZm9yIGksIHF1ZXN0aW9uIGluIGVudW1lcmF0ZShxdWVzdGlvbnMsIDEpXQogICAgKQoKICAgICMgQ29uc3RydWN0IHRoZSB0ZW1wbGF0ZToKICAgIHJldHVybiBmInt0ZXh0X3dyYXBwZXJ9XG57cXVlc3Rpb25zX3dyYXBwZXIuZm9ybWF0KHF1ZXN0aW9ucyl9XG4iCgoKZGVmIF9nZXRfZ2VuZXJhdGlvbl9waXBlbGluZSgKICAgIG1vZGVsX25hbWU6IHN0ciwKICAgIGRldmljZV9tYXA6IFVuaW9uW3N0ciwgZGljdF0sCiAgICB0b2tlbml6ZXJfbmFtZTogc3RyLAogICAgbW9kZWxfa3dhcmdzOiBkaWN0LAogICAgdG9rZW5pemVyX2t3YXJnczogZGljdCwKICAgIGF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGg6IGludCA9IE5vbmUsCiAgICBiYXRjaF9zaXplOiBpbnQgPSAxLAopOgogICAgIyBMb2FkIHRoZSBtb2RlbDoKICAgIG1vZGVsID0gdHJhbnNmb3JtZXJzLkF1dG9Nb2RlbEZvckNhdXNhbExNLmZyb21fcHJldHJhaW5lZCgKICAgICAgICBtb2RlbF9uYW1lLCBkZXZpY2VfbWFwPWRldmljZV9tYXAsICoqbW9kZWxfa3dhcmdzCiAgICApCgogICAgIyBTZXQgZXhsbGFtYSBtYXggaW5wdXQgbGVuZ3RoIGlmIHByb3ZpZGVkOgogICAgaWYgYXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aDoKICAgICAgICBmcm9tIGF1dG9fZ3B0cSBpbXBvcnQgZXhsbGFtYV9zZXRfbWF4X2lucHV0X2xlbmd0aAoKICAgICAgICBtb2RlbCA9IGV4bGxhbWFfc2V0X21heF9pbnB1dF9sZW5ndGgoCiAgICAgICAgICAgIG1vZGVsPW1vZGVsLCBtYXhfaW5wdXRfbGVuZ3RoPWF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGgKICAgICAgICApCgogICAgIyBMb2FkIHRoZSB0b2tlbml6ZXI6CiAgICB0b2tlbml6ZXIgPSB0cmFuc2Zvcm1lcnMuQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQoCiAgICAgICAgdG9rZW5pemVyX25hbWUsICoqdG9rZW5pemVyX2t3YXJncwogICAgKQoKICAgICMgSW5pdGlhbGl6ZSBhIGdlbmVyYXRpb24gcGlwbGluZSBhbmQgcmV0dXJuOgogICAgcmV0dXJuIHRyYW5zZm9ybWVycy5waXBlbGluZSgKICAgICAgICB0YXNrPSJ0ZXh0LWdlbmVyYXRpb24iLAogICAgICAgIG1vZGVsPW1vZGVsLAogICAgICAgIHRva2VuaXplcj10b2tlbml6ZXIsCiAgICAgICAgYmF0Y2hfc2l6ZT1iYXRjaF9zaXplLAogICAgKQoKCmRlZiBfcmVhZF9maWxlX2JhdGNoKAogICAgZmlsZV9iYXRjaDogTGlzdFtwYXRobGliLlBhdGhdLAogICAgcHJvbXB0X3RlbXBsYXRlOiBzdHIsCikgLT4gTGlzdFtzdHJdOgogICAgYmF0Y2ggPSBbXQogICAgZm9yIGZpbGUgaW4gZmlsZV9iYXRjaDoKICAgICAgICB3aXRoIG9wZW4oZmlsZSwgInIiKSBhcyBmcDoKICAgICAgICAgICAgYmF0Y2guYXBwZW5kKHByb21wdF90ZW1wbGF0ZS5mb3JtYXQoZnAucmVhZCgpKSkKICAgIHJldHVybiBiYXRjaAoKCmRlZiBfZ2V0X2Fuc3dlcnMoZ2VuZXJhdGVkX3RleHQ6IHN0ciwgcXVlc3Rpb25zX2Ftb3VudDogaW50KSAtPiBMaXN0W3N0cl06CiAgICAjIENsZWFyIGFuc3dlciBzdGFydCAocGFydCBiZWZvcmUgbnVtYmVycyk6CiAgICBpZiAiMS4iIG5vdCBpbiBnZW5lcmF0ZWRfdGV4dDoKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICBmIkFuc3dlciAxLiBpcyBtaXNzaW5nIGZyb20gdGhlIGdlbmVyYXRlZCB0ZXh0OiAne2dlbmVyYXRlZF90ZXh0fSciCiAgICAgICAgKQogICAgdGV4dCA9IGdlbmVyYXRlZF90ZXh0LnNwbGl0KCIxLiIsIDEpWzFdCgogICAgIyBTdGFydCBleHRyYWN0aW5nIHRoZSBhbnN3ZXJzOgogICAgYW5zd2VycyA9IFtdCiAgICBmb3IgaSBpbiByYW5nZSgxLCBxdWVzdGlvbnNfYW1vdW50ICsgMSk6CiAgICAgICAgIyBJZiBpdCdzIHRoZSBsYXN0IGFuc3dlciB0byBsb29rIGZvciwgdGFrZSB0aGUgcmVzdCBvZiB0aGUgdGV4dDoKICAgICAgICBpZiBpID09IHF1ZXN0aW9uc19hbW91bnQ6CiAgICAgICAgICAgIGFuc3dlcl9pID0gdGV4dAogICAgICAgICMgVmVyaWZ5IHRoZXJlIGlzIGEgcXVlc3Rpb24gbnVtYmVyIGluIHRoZSB0ZXh0OgogICAgICAgIGVsaWYgZiJ7aSArIDF9LiIgbm90IGluIHRleHQ6CiAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICAgICBmIkFuc3dlciB7aSArIDF9LiBpcyBtaXNzaW5nIGZyb20gdGhlIGdlbmVyYXRlZCB0ZXh0OiAne2dlbmVyYXRlZF90ZXh0fSciCiAgICAgICAgICAgICkKICAgICAgICAjIFRha2UgaSdzIGFuc3dlcjoKICAgICAgICBlbHNlOgogICAgICAgICAgICBhbnN3ZXJfaSwgdGV4dCA9IHRleHQuc3BsaXQoZiJ7aSArIDF9LiIsIDEpCiAgICAgICAgIyBDb2xsZWN0IHRoZSBhbnN3ZXIgcmVtb3ZpbmcgcmVkdW5kYW50IHNwYWNlczoKICAgICAgICBhbnN3ZXJzLmFwcGVuZChhbnN3ZXJfaS5zdHJpcCgpKQoKICAgIHJldHVybiBhbnN3ZXJzCgoKZGVmIF9hbnN3ZXJfcXVlc3Rpb25zKAogICAgcXVlc3Rpb25zX2Ftb3VudDogaW50LAogICAgYmF0Y2hlZF9pbnB1dDogTGlzdFtzdHJdLAogICAgZ2VuZXJhdGlvbl9waXBlbGluZTogdHJhbnNmb3JtZXJzLlBpcGVsaW5lLAogICAgZ2VuZXJhdGlvbl9jb25maWc6IHRyYW5zZm9ybWVycy5HZW5lcmF0aW9uQ29uZmlnLAopIC0+IExpc3RbTGlzdFtzdHJdXToKICAgICMgSW5mZXIgdGhyb3VnaCB0aGUgbGxtOgogICAgYmF0Y2hlZF9vdXRwdXQgPSBnZW5lcmF0aW9uX3BpcGVsaW5lKAogICAgICAgIGJhdGNoZWRfaW5wdXQsCiAgICAgICAgZ2VuZXJhdGlvbl9jb25maWc9Z2VuZXJhdGlvbl9jb25maWcsCiAgICAgICAgZW9zX3Rva2VuX2lkPWdlbmVyYXRpb25fcGlwZWxpbmUudG9rZW5pemVyLmVvc190b2tlbl9pZCwKICAgICAgICByZXR1cm5fZnVsbF90ZXh0PUZhbHNlLAogICAgICAgIG51bV9yZXR1cm5fc2VxdWVuY2VzPTEsCiAgICApCgogICAgIyBQcm9jZXNzIHRoZSBvdXRwdXRzIHRvIGdldCB0aGUgYW5zd2VyczoKICAgIGJhdGNoZWRfYW5zd2VycyA9IFtdCiAgICBmb3Igb3V0cHV0IGluIGJhdGNoZWRfb3V0cHV0OgogICAgICAgICMgR2V0IHRoZSBnZW5lcmF0ZWQgYW5zd2VyczoKICAgICAgICBhbnN3ZXJzID0gX2dldF9hbnN3ZXJzKAogICAgICAgICAgICBnZW5lcmF0ZWRfdGV4dD1vdXRwdXRbMF1bImdlbmVyYXRlZF90ZXh0Il0sCiAgICAgICAgICAgIHF1ZXN0aW9uc19hbW91bnQ9cXVlc3Rpb25zX2Ftb3VudCwKICAgICAgICApCiAgICAgICAgIyBDb2xsZWN0IHRoZSBwcm9jZXNzZWQgYW5zd2VyczoKICAgICAgICBiYXRjaGVkX2Fuc3dlcnMuYXBwZW5kKGFuc3dlcnMpCgogICAgcmV0dXJuIGJhdGNoZWRfYW5zd2Vycwo= + functionSourceCode: IyBDb3B5cmlnaHQgMjAyMyBJZ3VhemlvCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMAojCiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQojIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuCiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZAojIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgppbXBvcnQgZW51bQppbXBvcnQgbG9nZ2luZwppbXBvcnQgb3BlcmF0b3IKaW1wb3J0IHBhdGhsaWIKZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgQ291bnRlcgpmcm9tIGZ1bmN0b29scyBpbXBvcnQgcmVkdWNlLCB3cmFwcwpmcm9tIHR5cGluZyBpbXBvcnQgQW55LCBEaWN0LCBMaXN0LCBUdXBsZSwgVW5pb24KCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IHRyYW5zZm9ybWVycwpmcm9tIHRxZG0gaW1wb3J0IHRxZG0KCiMgR2V0IHRoZSBnbG9iYWwgbG9nZ2VyOgpfTE9HR0VSID0gbG9nZ2luZy5nZXRMb2dnZXIoKQoKCmRlZiBfY2hlY2tfbWxydW5fYW5kX29wZW5fbXBpKCkgLT4gVHVwbGVbIm1scnVuLk1MQ2xpZW50Q3R4IiwgIm1waTRweS5NUEkuSW50cmFjb21tIl06CiAgICBnbG9iYWwgX0xPR0dFUgoKICAgIGlzX21waSA9IEZhbHNlCiAgICB0cnk6CiAgICAgICAgaW1wb3J0IG1scnVuCgogICAgICAgIGNvbnRleHQgPSBtbHJ1bi5nZXRfb3JfY3JlYXRlX2N0eChuYW1lPSJtbHJ1biIpCiAgICAgICAgX0xPR0dFUiA9IGNvbnRleHQubG9nZ2VyCiAgICAgICAgaXNfbXBpID0gY29udGV4dC5sYWJlbHMuZ2V0KCJraW5kIiwgImpvYiIpID09ICJtcGlqb2IiCgogICAgICAgIGlmIGlzX21waToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZnJvbSBtcGk0cHkgaW1wb3J0IE1QSQoKICAgICAgICAgICAgICAgIHJldHVybiBjb250ZXh0LCBNUEkuQ09NTV9XT1JMRAogICAgICAgICAgICBleGNlcHQgTW9kdWxlTm90Rm91bmRFcnJvciBhcyBtcGk0cHlfbm90X2ZvdW5kOgogICAgICAgICAgICAgICAgY29udGV4dC5sb2dnZXIuZXJyb3IoCiAgICAgICAgICAgICAgICAgICAgIlRvIGRpc3RyaWJ1dGUgdGhlIGZ1bmN0aW9uIHVzaW5nIE1MUnVuJ3MgJ21waWpvYicgeW91IG5lZWQgdG8gaGF2ZSBgbXBpNHB5YCBwYWNrYWdlIGluIHlvdXIgIgogICAgICAgICAgICAgICAgICAgICJpbnRlcnByZXRlci4gUGxlYXNlIHJ1biBgcGlwIGluc3RhbGwgbXBpNHB5YCBhbmQgbWFrZSBzdXJlIHlvdSBoYXZlIG9wZW4tbXBpLiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIHJhaXNlIG1waTRweV9ub3RfZm91bmQKICAgIGV4Y2VwdCBNb2R1bGVOb3RGb3VuZEVycm9yIGFzIG1vZHVsZV9ub3RfZm91bmQ6CiAgICAgICAgaWYgaXNfbXBpOgogICAgICAgICAgICByYWlzZSBtb2R1bGVfbm90X2ZvdW5kCiAgICByZXR1cm4gTm9uZSwgTm9uZQoKCmRlZiBvcGVuX21waV9oYW5kbGVyKAogICAgd29ya2VyX2lucHV0czogTGlzdFtzdHJdLCByb290X3dvcmtlcl9pbnB1dHM6IERpY3Rbc3RyLCBBbnldID0gTm9uZQopOgogICAgZ2xvYmFsIF9MT0dHRVIKCiAgICAjIENoZWNrIGZvciBNTFJ1biBhbmQgT3Blbk1QSSBhdmFpbGFiaWxpdHk6CiAgICBjb250ZXh0LCBjb21tID0gX2NoZWNrX21scnVuX2FuZF9vcGVuX21waSgpCgogICAgZGVmIGRlY29yYXRvcihoYW5kbGVyKToKICAgICAgICBpZiBjb21tIGlzIE5vbmUgb3IgY29tbS5HZXRfc2l6ZSgpID09IDE6CiAgICAgICAgICAgIHJldHVybiBoYW5kbGVyCgogICAgICAgIEB3cmFwcyhoYW5kbGVyKQogICAgICAgIGRlZiB3cmFwcGVyKCoqa3dhcmdzKToKICAgICAgICAgICAgIyBHZXQgdGhlIG9wZW4gbXBpIGVudmlyb25tZW50IHByb3BlcnRpZXM6CiAgICAgICAgICAgIHNpemUgPSBjb21tLkdldF9zaXplKCkKICAgICAgICAgICAgcmFuayA9IGNvbW0uR2V0X3JhbmsoKQoKICAgICAgICAgICAgIyBHaXZlIHRoZSBjb3JyZWN0IGNodW5rIG9mIHRoZSB3b3JrZXJzIGlucHV0czoKICAgICAgICAgICAgZm9yIHdvcmtlcl9pbnB1dCBpbiB3b3JrZXJfaW5wdXRzOgogICAgICAgICAgICAgICAgaW5wdXRfYXJndW1lbnQgPSBrd2FyZ3Nbd29ya2VyX2lucHV0XQogICAgICAgICAgICAgICAgaWYgaW5wdXRfYXJndW1lbnQgaXMgTm9uZToKICAgICAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICAgICAgaWYgaXNpbnN0YW5jZShpbnB1dF9hcmd1bWVudCwgc3RyKToKICAgICAgICAgICAgICAgICAgICBpbnB1dF9hcmd1bWVudCA9IF9nZXRfdGV4dF9maWxlcygKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YV9wYXRoPXBhdGhsaWIuUGF0aChpbnB1dF9hcmd1bWVudCkuYWJzb2x1dGUoKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGlmIGxlbihpbnB1dF9hcmd1bWVudCkgPCBzaXplOgogICAgICAgICAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICAgICAgICAgICAgIGYiQ2Fubm90IHNwbGl0IHRoZSBpbnB1dCAne3dvcmtlcl9pbnB1dH0nIG9mIGxlbmd0aCB7bGVuKGlucHV0X2FyZ3VtZW50KX0gdG8ge3NpemV9IHdvcmtlcnMuICIKICAgICAgICAgICAgICAgICAgICAgICAgZiJQbGVhc2UgcmVkdWNlIHRoZSBhbW91bnQgb2Ygd29ya2VycyBmb3IgdGhpcyBpbnB1dC4iCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgZXZlbl9jaHVua19zaXplID0gbGVuKGlucHV0X2FyZ3VtZW50KSAvLyBzaXplCiAgICAgICAgICAgICAgICBjaHVua19zdGFydCA9IHJhbmsgKiBldmVuX2NodW5rX3NpemUKICAgICAgICAgICAgICAgIGNodW5rX2VuZCA9ICgKICAgICAgICAgICAgICAgICAgICAocmFuayArIDEpICogZXZlbl9jaHVua19zaXplCiAgICAgICAgICAgICAgICAgICAgaWYgcmFuayArIDEgPCBzaXplCiAgICAgICAgICAgICAgICAgICAgZWxzZSBsZW4oaW5wdXRfYXJndW1lbnQpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBjb250ZXh0LmxvZ2dlci5pbmZvKAogICAgICAgICAgICAgICAgICAgIGYiUmFuayAje3Jhbmt9OiBQcm9jZXNzaW5nIGlucHV0IGNodW5rIG9mICd7d29ya2VyX2lucHV0fScgIgogICAgICAgICAgICAgICAgICAgIGYiZnJvbSBpbmRleCB7Y2h1bmtfc3RhcnR9IHRvIHtjaHVua19lbmR9LiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGlmIGlzaW5zdGFuY2UoaW5wdXRfYXJndW1lbnQsIGxpc3QpOgogICAgICAgICAgICAgICAgICAgIGlucHV0X2FyZ3VtZW50ID0gaW5wdXRfYXJndW1lbnRbY2h1bmtfc3RhcnQ6Y2h1bmtfZW5kXQogICAgICAgICAgICAgICAgZWxpZiBpc2luc3RhbmNlKGlucHV0X2FyZ3VtZW50LCBwZC5EYXRhRnJhbWUpOgogICAgICAgICAgICAgICAgICAgIGlucHV0X2FyZ3VtZW50ID0gaW5wdXRfYXJndW1lbnQuaWxvY1tjaHVua19zdGFydDpjaHVua19lbmQ6LCA6XQogICAgICAgICAgICAgICAga3dhcmdzW3dvcmtlcl9pbnB1dF0gPSBpbnB1dF9hcmd1bWVudAoKICAgICAgICAgICAgIyBTZXQgdGhlIHJvb3Qgd29ya2VyIG9ubHkgYXJndW1lbnRzOgogICAgICAgICAgICBpZiByYW5rID09IDAgYW5kIHJvb3Rfd29ya2VyX2lucHV0czoKICAgICAgICAgICAgICAgIGt3YXJncy51cGRhdGUocm9vdF93b3JrZXJfaW5wdXRzKQoKICAgICAgICAgICAgIyBSdW4gdGhlIHdvcmtlcjoKICAgICAgICAgICAgb3V0cHV0ID0gaGFuZGxlcigqKmt3YXJncykKCiAgICAgICAgICAgICMgU2VuZCB0aGUgb3V0cHV0IHRvIHRoZSByb290IHJhbmsgKHJhbmsgIzApOgogICAgICAgICAgICBvdXRwdXQgPSBjb21tLmdhdGhlcihvdXRwdXQsIHJvb3Q9MCkKICAgICAgICAgICAgaWYgcmFuayA9PSAwOgogICAgICAgICAgICAgICAgIyBKb2luIHRoZSBvdXRwdXRzOgogICAgICAgICAgICAgICAgY29udGV4dC5sb2dnZXIuaW5mbygiQ29sbGVjdGluZyBkYXRhIGZyb20gd29ya2VycyB0byByb290IHdvcmtlci4iKQogICAgICAgICAgICAgICAgZGF0YWZyYW1lID0gcGQuY29uY2F0KG9ianM9W2RmIGZvciBkZiwgXyBpbiBvdXRwdXRdLCBheGlzPTApCiAgICAgICAgICAgICAgICBlcnJvcnNfZGljdGlvbmFyeSA9IHJlZHVjZShvcGVyYXRvci5pb3IsIFtlcnIgZm9yIF8sIGVyciBpbiBvdXRwdXRdLCB7fSkKICAgICAgICAgICAgICAgIHJldHVybiBkYXRhZnJhbWUsIGVycm9yc19kaWN0aW9uYXJ5CiAgICAgICAgICAgIHJldHVybiBOb25lCgogICAgICAgIHJldHVybiB3cmFwcGVyCgogICAgcmV0dXJuIGRlY29yYXRvcgoKCkBvcGVuX21waV9oYW5kbGVyKHdvcmtlcl9pbnB1dHM9WyJkYXRhX3BhdGgiXSwgcm9vdF93b3JrZXJfaW5wdXRzPXsidmVyYm9zZSI6IFRydWV9KQpkZWYgYW5zd2VyX3F1ZXN0aW9ucygKICAgIGRhdGFfcGF0aDogVW5pb25bc3RyLCBMaXN0W3N0cl1dLAogICAgbW9kZWxfbmFtZTogc3RyLAogICAgcXVlc3Rpb25zOiBVbmlvbltMaXN0W3N0cl0sIExpc3RbTGlzdFtzdHJdXV0sCiAgICBkZXZpY2VfbWFwOiBVbmlvbltzdHIsIGRpY3RdID0gTm9uZSwKICAgIG1vZGVsX2t3YXJnczogZGljdCA9IE5vbmUsCiAgICBhdXRvX2dwdHFfZXhsbGFtYV9tYXhfaW5wdXRfbGVuZ3RoOiBpbnQgPSBOb25lLAogICAgdG9rZW5pemVyX25hbWU6IHN0ciA9IE5vbmUsCiAgICB0b2tlbml6ZXJfa3dhcmdzOiBkaWN0ID0gTm9uZSwKICAgIHRleHRfd3JhcHBlcjogVW5pb25bc3RyLCBMaXN0W3N0cl1dID0gIiIsCiAgICBxdWVzdGlvbnNfd3JhcHBlcjogVW5pb25bc3RyLCBMaXN0W3N0cl1dID0gIiIsCiAgICBnZW5lcmF0aW9uX2NvbmZpZzogVW5pb25bRGljdCwgTGlzdFtEaWN0XV0gPSBOb25lLAogICAgcXVlc3Rpb25zX2NvbmZpZzogVW5pb25bRGljdCwgTGlzdFtEaWN0XV0gPSBOb25lLAogICAgYmF0Y2hfc2l6ZTogaW50ID0gMSwKICAgIHF1ZXN0aW9uc19jb2x1bW5zOiBMaXN0W3N0cl0gPSBOb25lLAogICAgdmVyYm9zZTogYm9vbCA9IEZhbHNlLAopIC0+IFR1cGxlW3BkLkRhdGFGcmFtZSwgZGljdF06CiAgICAiIiIKICAgIEFuc3dlciBxdWVzdGlvbnMgd2l0aCBhIGNvbnRleHQgdG8gdGhlIGdpdmVuIHRleHQgZmlsZXMgY29udGVudHMgYnkgYSBwcmV0cmFpbmVkIExMTSBtb2RlbC4gRWFjaCB0ZXh0IGZpbGUgd2lsbCBoYXZlCiAgICB0aGUgZm9sbG93aW5nIHByb21wdCBidWlsdDoKCiAgICBzdGFydCBvZiBgdGV4dF93cmFwcGVyYAogICAgPHRleHQgZmlsZSBjb250ZW50PgogICAgZW5kIG9mIGB0ZXh0X3dyYXBwZXJgCgogICAgc3RhcnQgb2YgYHF1ZXN0aW9uc193cmFwcGVyYAogICAgMS4gPHF1ZXN0aW9uc1swXT4KICAgIDIuIDxxdWVzdGlvbnNbMV0+CiAgICAuLi4KICAgIG4uIDxxdWVzdGlvbnNbbi0xXT4KICAgIGVuZCBvZiBgcXVlc3Rpb25zX3dyYXBwZXJgCgogICAgOnBhcmFtIGRhdGFfcGF0aDogICAgICAgICAgICAgICAgICAgICAgICAgIEEgcGF0aCB0byBhIGRpcmVjdG9yeSBvZiB0ZXh0IGZpbGVzIG9yIGEgcGF0aCB0byBhIHRleHQgZmlsZSB0byBhc2sKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdGlvbnMgYWJvdXQuCiAgICA6cGFyYW0gbW9kZWxfbmFtZTogICAgICAgICAgICAgICAgICAgICAgICAgVGhlIHByZS10cmFpbmVkIG1vZGVsIG5hbWUgZnJvbSB0aGUgaHVnZ2luZ2ZhY2UgaHViIHRvIHVzZSBmb3IgYXNraW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3Rpb25zLgogICAgOnBhcmFtIHF1ZXN0aW9uczogICAgICAgICAgICAgICAgICAgICAgICAgIFRoZSBxdWVzdGlvbnMgdG8gYXNrLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEEgbGlzdCBvZiBsaXN0cyBvZiBxdWVzdGlvbnMgdG8gYXNrIHBlciB0ZXh0IGZpbGUsIGFuZCBkZXZpZGVkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgcXVlc3Rpb24gZ3JvdXBzLCB0aGUgZ3JvdXBzIGNhbiBiZSBkdGVybWFpbmVkIGJ5IHNpemUgKGluIG9yZGVyIHRvCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZvaWQgbGFyZ2UgaW5wdXRzIHRvIHRoZSBsbG0pIG9yIGJ5IHF1ZXN0aW9uaW5nIG1ldGhvZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChyZWd1bGFyIG9yIHBvbGwgbGlrZSBxdWVzdGlvbmluZykuCiAgICA6cGFyYW0gZGV2aWNlX21hcDogICAgICAgICAgICAgICAgICAgICAgICAgQSBtYXAgdG8gdXNlIGZvciBsb2FkaW5nIHRoZSBtb2RlbCBvbiBtdWx0aXBsZSBkZXZpY2VzLgogICAgOnBhcmFtIG1vZGVsX2t3YXJnczogICAgICAgICAgICAgICAgICAgICAgIEtleXdvcmQgYXJndW1lbnRzIHRvIHBhc3MgZm9yIGxvYWRpbmcgdGhlIG1vZGVsIHVzaW5nIEh1Z2dpbmdGYWNlJ3MKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgdHJhbnNmb3JtZXJzLkF1dG9Nb2RlbEZvckNhdXNhbExNLmZyb21fcHJldHJhaW5lZGAgZnVuY3Rpb24uCiAgICA6cGFyYW0gYXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aDogRm9yIEF1dG9HUFRRIG1vZGVscyB0byBzZXQgYW5kIGV4dGVuZCB0aGUgbW9kZWwncyBpbnB1dCBidWZmZXIgc2l6ZS4KICAgIDpwYXJhbSB0b2tlbml6ZXJfbmFtZTogICAgICAgICAgICAgICAgICAgICBUaGUgdG9rZW5pemVyIG5hbWUgZnJvbSB0aGUgaHVnZ2luZ2ZhY2UgaHViIHRvIHVzZS4gSWYgbm90IGdpdmVuLCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCBuYW1lIHdpbGwgYmUgdXNlZC4KICAgIDpwYXJhbSB0b2tlbml6ZXJfa3dhcmdzOiAgICAgICAgICAgICAgICAgICBLZXl3b3JkIGFyZ3VtZW50cyB0byBwYXNzIGZvciBsb2FkaW5nIHRoZSB0b2tlbml6ZXIgdXNpbmcgSHVnZ2luZ0ZhY2UncwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGB0cmFuc2Zvcm1lcnMuQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWRgIGZ1bmN0aW9uLgogICAgOnBhcmFtIHRleHRfd3JhcHBlcjogICAgICAgICAgICAgICAgICAgICAgIEEgd3JhcHBlciBmb3IgdGhlIGZpbGUncyB0ZXh0LiBXaWxsIGJlIGFkZGVkIGF0IHRoZSBzdGFydCBvZiB0aGUgcHJvbXB0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE11c3QgaGF2ZSBhIHBsYWNlaG9sZGVyICgne30nKSBmb3IgdGhlIHRleHQgb2YgdGhlIGZpbGUuCiAgICA6cGFyYW0gcXVlc3Rpb25zX3dyYXBwZXI6ICAgICAgICAgICAgICAgICAgQSB3cmFwcGVyIGZvciB0aGUgcXVlc3Rpb25zIHJlY2VpdmVkLiBXaWxsIGJlIGFkZGVkIGFmdGVyIHRoZSB0ZXh0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd3JhcHBlciBpbiB0aGUgcHJvbXB0IHRlbXBsYXRlLiBNdXN0IGhhdmUgYSBwbGFjZWhvbGRlciAoJ3t9JykgZm9yIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXN0aW9ucy4KICAgIDpwYXJhbSBnZW5lcmF0aW9uX2NvbmZpZzogICAgICAgICAgICAgICAgICBIdWdnaW5nRmFjZSdzIGBHZW5lcmF0aW9uQ29uZmlnYCBrZXl3b3JkIGFyZ3VtZW50cyB0byBwYXNzIHRvIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBnZW5lcmF0ZWAgbWV0aG9kLgogICAgOnBhcmFtIHF1ZXN0aW9uc19jb25maWc6ICAgICAgICAgICAgICAgICAgIEEgZGljdGlvbmFyeSBvciBsaXN0IG9mIGRpY3Rpb25hcmllcyBjb250YWluaW5nIHNwZWNpZmljIHdheXMgdG8gYW5zd2VyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3Rpb25zICh1c2luZyBhIHBvbGwgZm9yIGV4YW1wbGUpLCBlYWNoIGRpY3Rpb25hcnkgaW4gdGhlIGxpc3QgaXMgZm9yCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVzcG9uZGluZyBxdWVzdGlvbiBncm91cCBhbmQgZGV0ZXJtaW5lcyB0aGUgcXVlc3Rpb24gYXNraW5nIG1ldGhvZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvciBzYWlkIGdyb3VwLgogICAgOnBhcmFtIGJhdGNoX3NpemU6ICAgICAgICAgICAgICAgICAgICAgICAgIEJhdGNoIHNpemUgZm9yIGluZmVyZW5jZS4KICAgIDpwYXJhbSBxdWVzdGlvbnNfY29sdW1uczogICAgICAgICAgICAgICAgICBDb2x1bW5zIHRvIHVzZSBmb3IgdGhlIGRhdGFmcmFtZSByZXR1cm5lZC4KICAgIDpwYXJhbSB2ZXJib3NlOiAgICAgICAgICAgICAgICAgICAgICAgICAgICBXaGV0aGVyIHRvIHByZXNlbnQgbG9ncyBvZiBhIHByb2dyZXNzIGJhciBhbmQgZXJyb3JzLiBEZWZhdWx0OiBUcnVlLgoKCiAgICA6cmV0dXJuczogQSB0dXBsZSBvZjoKCiAgICAgICAgICAgICAgKiBBIGRhdGFmcmFtZSBkYXRhc2V0IG9mIHRoZSBxdWVzdGlvbnMgYW5zd2Vycy4KICAgICAgICAgICAgICAqIEEgZGljdGlvbmFyeSBvZiBlcnJvcmVkIGZpbGVzIHRoYXQgd2VyZSBub3QgaW5mZXJyZWQgb3Igd2VyZSBub3QgYW5zd2VyZWQgcHJvcGVybHkuCiAgICAiIiIKICAgIGdsb2JhbCBfTE9HR0VSCgogICAgIyBTZXQgY29uZmlncyB0byBlbXB0eSBkaWN0IGlmIG5vdCBnaXZlbjoKICAgIGlmIGdlbmVyYXRpb25fY29uZmlnIGlzIE5vbmU6CiAgICAgICAgZ2VuZXJhdGlvbl9jb25maWcgPSB7fQogICAgaWYgcXVlc3Rpb25zX2NvbmZpZyBpcyBOb25lOgogICAgICAgIHF1ZXN0aW9uc19jb25maWcgPSB7fQoKICAgICMgR2V0IHRoZSBpbnB1dCB0ZXh0IGZpbGVzIHRvIHF1ZXN0aW9uOgogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oIkNvbGxlY3RpbmcgdGV4dCBmaWxlcy4iKQogICAgaWYgaXNpbnN0YW5jZShkYXRhX3BhdGgsIHN0cik6CiAgICAgICAgZGF0YV9wYXRoID0gcGF0aGxpYi5QYXRoKGRhdGFfcGF0aCkuYWJzb2x1dGUoKQogICAgICAgIHRleHRfZmlsZXMgPSBfZ2V0X3RleHRfZmlsZXMoZGF0YV9wYXRoPWRhdGFfcGF0aCkKICAgIGVsc2U6CiAgICAgICAgdGV4dF9maWxlcyA9IGRhdGFfcGF0aAogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oZiJDb2xsZWN0ZWQge2xlbih0ZXh0X2ZpbGVzKX0gdGV4dCBmaWxlcy4iKQoKICAgICMgR2V0IHRoZSBwcm9tcHQgdGVtcGxhdGU6CiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbygiQ3JlYXRpbmcgcHJvbXB0IHRlbXBsYXRlLiIpCgogICAgIyBPcmdhbml6ZSBxdWVzdGlvbnMgYXMgYSBsaXN0IG9mIGxpc3QsIGFuZCBjb3VudCBudW1iZXIgb2Ygc3ViLWxpc3RzIGZvciBmdXR1cmUgdXNlCiAgICBudW1iZXJfb2ZfcXVlc3Rpb25fZ3JvdXBzID0gMSBpZiBpc2luc3RhbmNlKHF1ZXN0aW9uc1swXSwgc3RyKSBlbHNlIGxlbihxdWVzdGlvbnMpCiAgICBxdWVzdGlvbnMgPSBfdG9fZ3JvdXBfbGlzdCgKICAgICAgICBhcmd1bWVudF92YWx1ZT1xdWVzdGlvbnMsCiAgICAgICAgYXJndW1lbnRfbmFtZT0icXVlc3Rpb25zIiwKICAgICAgICBsZW5ndGg9bnVtYmVyX29mX3F1ZXN0aW9uX2dyb3VwcywKICAgICkKCiAgICAjIE9yZ2FuaXplIHByb21wdCBwYXJ0cyBhdCBwcm9wZXIgbGVuZ3RoCiAgICB0ZXh0X3dyYXBwZXIgPSBfdG9fZ3JvdXBfbGlzdCgKICAgICAgICBhcmd1bWVudF92YWx1ZT10ZXh0X3dyYXBwZXIsCiAgICAgICAgYXJndW1lbnRfbmFtZT0idGV4dF93cmFwcGVyIiwKICAgICAgICBsZW5ndGg9bnVtYmVyX29mX3F1ZXN0aW9uX2dyb3VwcywKICAgICkKICAgIHF1ZXN0aW9uc193cmFwcGVyID0gX3RvX2dyb3VwX2xpc3QoCiAgICAgICAgYXJndW1lbnRfdmFsdWU9cXVlc3Rpb25zX3dyYXBwZXIsCiAgICAgICAgYXJndW1lbnRfbmFtZT0icXVlc3Rpb25zX3dyYXBwZXIiLAogICAgICAgIGxlbmd0aD1udW1iZXJfb2ZfcXVlc3Rpb25fZ3JvdXBzLAogICAgKQoKICAgICMgQ3JlYXRlIGEgbGlzdCBvZiBwcm9tcHQgYWNjb3JkaW5nIHRvIGdpdmVuIHBhcnRzIGFuZCBxdWVzdGlvbnMKICAgIHByb21wdF90ZW1wbGF0ZSA9IFtdCiAgICBxdWVzdGlvbnMgPSBxdWVzdGlvbnMgaWYgaXNpbnN0YW5jZShxdWVzdGlvbnNbMF0sIGxpc3QpIGVsc2UgW3F1ZXN0aW9uc10KCiAgICAjIEJ1aWxkIGFsbCBwcm9tcHRzCiAgICBmb3IgaSBpbiByYW5nZShudW1iZXJfb2ZfcXVlc3Rpb25fZ3JvdXBzKToKICAgICAgICBwcm9tcHRfdGVtcGxhdGUuYXBwZW5kKAogICAgICAgICAgICBfZ2V0X3Byb21wdF90ZW1wbGF0ZSgKICAgICAgICAgICAgICAgIHRleHRfd3JhcHBlcj10ZXh0X3dyYXBwZXJbaV0sCiAgICAgICAgICAgICAgICBxdWVzdGlvbnNfd3JhcHBlcj1xdWVzdGlvbnNfd3JhcHBlcltpXSwKICAgICAgICAgICAgICAgIHF1ZXN0aW9ucz1xdWVzdGlvbnNbaV0sCiAgICAgICAgICAgICkKICAgICAgICApCiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbyhmIlByb21wdCB0ZW1wbGF0ZSBjcmVhdGVkOlxuXG57cHJvbXB0X3RlbXBsYXRlfVxuIikKCiAgICAjIEdldCB0aGUgdG90YWwgYW1vdW50IG9mIHF1ZXN0aW9uczoKICAgIHF1ZXN0aW9uc19hbW91bnQgPSBzdW0oW2xlbihzdWJsaXN0KSBmb3Igc3VibGlzdCBpbiBxdWVzdGlvbnNdKQoKICAgICMgR2V0IHRoZSBxdWVzdGlvbnMgY29sdW1uczoKICAgIHF1ZXN0aW9uc19jb2x1bW5zID0gcXVlc3Rpb25zX2NvbHVtbnMgb3IgWwogICAgICAgIGYicXtpfSIgZm9yIGkgaW4gcmFuZ2UoMSwgcXVlc3Rpb25zX2Ftb3VudCArIDEpCiAgICBdCgogICAgIyBDaGVjayBpZiB3ZSBoYXZlIHRoZSBjb3JyZWN0IGFtb3VudCBvZiBxdWVzdGlvbnMgY29sdW1uczoKICAgIGlmIGxlbihxdWVzdGlvbnNfY29sdW1ucykgIT0gcXVlc3Rpb25zX2Ftb3VudDoKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICBmIlRoZSBwcm92aWRlZCBxdWVzdGlvbnMgY29sdW1ucyBsZW5ndGggKHtsZW4ocXVlc3Rpb25zX2NvbHVtbnMpfSkgIgogICAgICAgICAgICBmImRvZXMgbm90IG1hdGNoIHRoZSBxdWVzdGlvbnMgYW1vdW50ICh7cXVlc3Rpb25zX2Ftb3VudH0pIgogICAgICAgICkKCiAgICAjIExvYWQgdGhlIGdlbmVyYXRpb24gY29uZmlnOgogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oIkxvYWRpbmcgZ2VuZXJhdGlvbiBjb25maWd1cmF0aW9uLiIpCiAgICBnZW5lcmF0aW9uX2NvbmZpZyA9IFsKICAgICAgICB0cmFuc2Zvcm1lcnMuR2VuZXJhdGlvbkNvbmZpZygqKihjZmcgb3Ige30pKQogICAgICAgIGZvciBjZmcgaW4gX3RvX2dyb3VwX2xpc3QoCiAgICAgICAgICAgIGFyZ3VtZW50X3ZhbHVlPWdlbmVyYXRpb25fY29uZmlnLAogICAgICAgICAgICBhcmd1bWVudF9uYW1lPSJnZW5lcmF0aW9uX2NvbmZpZyIsCiAgICAgICAgICAgIGxlbmd0aD1udW1iZXJfb2ZfcXVlc3Rpb25fZ3JvdXBzLAogICAgICAgICkKICAgIF0KICAgIGlmIHZlcmJvc2U6CiAgICAgICAgX0xPR0dFUi5pbmZvKGYiR2VuZXJhdGlvbiBjb25maWd1cmF0aW9uIGxvYWRlZDoge2dlbmVyYXRpb25fY29uZmlnfSIpCgogICAgIyBMb2FkIHRoZSBtb2RlbCBhbmQgdG9rZW5pemVyIGludG8gYSBwaXBlbGluZSBvYmplY3Q6CiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbyhmIkxvYWRpbmcgbW9kZWwgJ3ttb2RlbF9uYW1lfScuIikKICAgIGdlbmVyYXRpb25fcGlwZWxpbmUgPSBfZ2V0X2dlbmVyYXRpb25fcGlwZWxpbmUoCiAgICAgICAgbW9kZWxfbmFtZT1tb2RlbF9uYW1lLAogICAgICAgIGRldmljZV9tYXA9ZGV2aWNlX21hcCwKICAgICAgICB0b2tlbml6ZXJfbmFtZT10b2tlbml6ZXJfbmFtZSBvciBtb2RlbF9uYW1lLAogICAgICAgIG1vZGVsX2t3YXJncz1tb2RlbF9rd2FyZ3Mgb3Ige30sCiAgICAgICAgdG9rZW5pemVyX2t3YXJncz10b2tlbml6ZXJfa3dhcmdzIG9yIHt9LAogICAgICAgIGF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGg9YXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aCwKICAgICAgICBiYXRjaF9zaXplPWJhdGNoX3NpemUsCiAgICApCiAgICBpZiB2ZXJib3NlOgogICAgICAgIF9MT0dHRVIuaW5mbygiTW9kZWwgbG9hZGVkLiIpCgogICAgIyBQcmVwYXJlIHRoZSBzdWNjZXNzZXMgZGF0YWZyYW1lIGFuZCBlcnJvcnMgZGljdGlvbmFyeSB0byBiZSByZXR1cm5lZDoKICAgIHN1Y2Nlc3NlcyA9IFtdCiAgICBlcnJvcnMgPSB7fQoKICAgICMgU3BsaXQgdGhlIGZpbGVzIGludG8gYmF0Y2hlczoKICAgIGZpbGVfYmF0Y2hlcyA9IFsKICAgICAgICB0ZXh0X2ZpbGVzW2kgOiBpICsgYmF0Y2hfc2l6ZV0KICAgICAgICBpZiBpICsgYmF0Y2hfc2l6ZSA8IGxlbih0ZXh0X2ZpbGVzKQogICAgICAgIGVsc2UgdGV4dF9maWxlc1tpOl0KICAgICAgICBmb3IgaSBpbiByYW5nZSgwLCBsZW4odGV4dF9maWxlcyksIGJhdGNoX3NpemUpCiAgICBdCiAgICBxdWVzdGlvbnNfY29uZmlnID0gX3RvX2dyb3VwX2xpc3QoCiAgICAgICAgYXJndW1lbnRfdmFsdWU9cXVlc3Rpb25zX2NvbmZpZywKICAgICAgICBhcmd1bWVudF9uYW1lPSJxdWVzdGlvbnNfY29uZmlnIiwKICAgICAgICBsZW5ndGg9bnVtYmVyX29mX3F1ZXN0aW9uX2dyb3VwcywKICAgICkKCiAgICAjIENyZWF0ZSBhIGxpc3Qgb2YgcXVlc3Rpb24gaGFuZGxlcnMgYWNjb3JkaW5nIHRvIGdpdmVuIGNvbmZpZ3MKICAgIGhhbmRsZXJzID0gW10KICAgIGZvciBjZmcgaW4gcXVlc3Rpb25zX2NvbmZpZzoKICAgICAgICBxdWVzdGlvbl90eXBlID0gY2ZnLnBvcCgidHlwZSIsICJkZWZhdWx0IikKICAgICAgICBoYW5kbGVycy5hcHBlbmQoUVVFU1RJT05fTUFQUElORy5nZXQocXVlc3Rpb25fdHlwZSkoKipjZmcpKQoKICAgICMgR28gb3ZlciB0aGUgYmF0Y2hlcyBvZiB0ZXh0IGZpbGVzIGFuZCBxdWVzdGlvbiB0aGVtOgogICAgZm9yIGZpbGVfYmF0Y2ggaW4gdHFkbSgKICAgICAgICBmaWxlX2JhdGNoZXMsCiAgICAgICAgZGVzYz0iR2VuZXJhdGluZyBhbnN3ZXJzIiwKICAgICAgICB1bml0PWYiZmlsZSAoYmF0Y2ggb2Yge2JhdGNoX3NpemV9KSIsCiAgICAgICAgZGlzYWJsZT1ub3QgdmVyYm9zZSwKICAgICk6CiAgICAgICAgdHJ5OgogICAgICAgICAgICB0b3RhbF9hbnN3ZXJzID0gW1tdIGZvciBfIGluIHJhbmdlKGJhdGNoX3NpemUpXQoKICAgICAgICAgICAgIyBHbyBvdmVyIGFsbCBxdWVzdGlvbiBncm91cCBwZXIgYmF0Y2ggb2YgZG9jdW1lbnRzCiAgICAgICAgICAgIGZvciBxdWVzdGlvbl9ncm91cCBpbiByYW5nZShudW1iZXJfb2ZfcXVlc3Rpb25fZ3JvdXBzKToKICAgICAgICAgICAgICAgIGN1cnJlbnRfcXVlc3Rpb25zX2Ftb3VudCA9IGxlbihxdWVzdGlvbnNbcXVlc3Rpb25fZ3JvdXBdKQoKICAgICAgICAgICAgICAgICMgUmVhZCBiYXRjaCAocmVhZCB0aGUgdGV4dCBmcm9tIHRoZSB0ZXh0IGZpbGVzKToKICAgICAgICAgICAgICAgIGJhdGNoZWRfaW5wdXQgPSBfcmVhZF9maWxlX2JhdGNoKAogICAgICAgICAgICAgICAgICAgIGZpbGVfYmF0Y2g9ZmlsZV9iYXRjaCwKICAgICAgICAgICAgICAgICAgICBwcm9tcHRfdGVtcGxhdGU9cHJvbXB0X3RlbXBsYXRlW3F1ZXN0aW9uX2dyb3VwXSwKICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICAgICAjIEFuc3dlciB0aGUgcXVlc3Rpb25zIHdpdGggZWFjaCBxdWVzdGlvbiBoYW5kbGVyOgogICAgICAgICAgICAgICAgYmF0Y2hlZF9hbnN3ZXJzID0gaGFuZGxlcnNbcXVlc3Rpb25fZ3JvdXBdLmFuc3dlcigKICAgICAgICAgICAgICAgICAgICBxdWVzdGlvbnNfYW1vdW50PWN1cnJlbnRfcXVlc3Rpb25zX2Ftb3VudCwKICAgICAgICAgICAgICAgICAgICBiYXRjaGVkX2lucHV0PWJhdGNoZWRfaW5wdXQsCiAgICAgICAgICAgICAgICAgICAgZ2VuZXJhdGlvbl9waXBlbGluZT1nZW5lcmF0aW9uX3BpcGVsaW5lLAogICAgICAgICAgICAgICAgICAgIGdlbmVyYXRpb25fY29uZmlnPWdlbmVyYXRpb25fY29uZmlnW3F1ZXN0aW9uX2dyb3VwXSwKICAgICAgICAgICAgICAgICkKCiAgICAgICAgICAgICAgICAjIFB1dCB0aGUgYW5zd2VycyBpbiB0aGUgY29ycmVjdCBwbGFjZSBpbiB0aGUgdG90YWwgYW5zd2VycyBsaXN0IGFjY29yZGluZyB0byB0aGUgcGxhY2UgaW4gdGhlIGJhdGNoOgogICAgICAgICAgICAgICAgZm9yIGkgaW4gcmFuZ2UoYmF0Y2hfc2l6ZSk6CiAgICAgICAgICAgICAgICAgICAgdG90YWxfYW5zd2Vyc1tpXS5leHRlbmQoYmF0Y2hlZF9hbnN3ZXJzW2ldKQoKICAgICAgICAgICAgIyBDb2xsZWN0IHRoZSBhbnN3ZXJzIGFuZCBhdHRhY2ggdGhlIGZpbGUgbmFtZToKICAgICAgICAgICAgc3VjY2Vzc2VzLmV4dGVuZCgKICAgICAgICAgICAgICAgIFsKICAgICAgICAgICAgICAgICAgICBbZmlsZS5uYW1lLCAqYW5zd2Vyc10KICAgICAgICAgICAgICAgICAgICBmb3IgZmlsZSwgYW5zd2VycyBpbiB6aXAoZmlsZV9iYXRjaCwgdG90YWxfYW5zd2VycykKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjZXB0aW9uOgogICAgICAgICAgICAjIE5vdGUgdGhlIGV4Y2VwdGlvbiBhcyBlcnJvciBpbiB0aGUgZGljdGlvbmFyeToKICAgICAgICAgICAgYmF0Y2hfZmlsZV9uYW1lcyA9ICIsICIuam9pbihbZmlsZS5uYW1lIGZvciBmaWxlIGluIGZpbGVfYmF0Y2hdKQogICAgICAgICAgICBpZiB2ZXJib3NlOgogICAgICAgICAgICAgICAgX0xPR0dFUi53YXJuaW5nKAogICAgICAgICAgICAgICAgICAgIGYiRXJyb3IgaW4gYmF0Y2ggJ3tiYXRjaF9maWxlX25hbWVzfSc6IHtzdHIoZXhjZXB0aW9uKX0iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgIGVycm9yc1tiYXRjaF9maWxlX25hbWVzXSA9IHN0cihleGNlcHRpb24pCiAgICAgICAgICAgIGNvbnRpbnVlCgogICAgIyBDb25zdHJ1Y3QgdGhlIGFuc3dlcnMgZGF0YWZyYW1lOgogICAgY29sdW1ucyA9IFsKICAgICAgICAidGV4dF9maWxlIiwKICAgICAgICAqcXVlc3Rpb25zX2NvbHVtbnMsCiAgICBdCgogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIG9mIGFuc3dlcnMgYnkgZmlsZXMKICAgIHN1Y2Nlc3NlcyA9IHBkLkRhdGFGcmFtZSgKICAgICAgICBzdWNjZXNzZXMsCiAgICAgICAgY29sdW1ucz1jb2x1bW5zLAogICAgKQoKICAgICMgUHJpbnQgdGhlIGhlYWQgb2YgdGhlIHByb2R1Y2VkIGRhdGFmcmFtZSBhbmQgcmV0dXJuOgogICAgaWYgdmVyYm9zZToKICAgICAgICBfTE9HR0VSLmluZm8oCiAgICAgICAgICAgIGYiRG9uZSAoe3N1Y2Nlc3Nlcy5zaGFwZVswXX0ve2xlbih0ZXh0X2ZpbGVzKX0pXG4iCiAgICAgICAgICAgIGYiQW5zd2VycyBzdW1tYXJ5OlxuIgogICAgICAgICAgICBmIntzdWNjZXNzZXMuaGVhZCgpfSIKICAgICAgICApCiAgICByZXR1cm4gc3VjY2Vzc2VzLCBlcnJvcnMKCgpkZWYgX2dldF90ZXh0X2ZpbGVzKAogICAgZGF0YV9wYXRoOiBwYXRobGliLlBhdGgsCikgLT4gTGlzdFtwYXRobGliLlBhdGhdOgoKICAgICMgQ2hlY2sgaWYgdGhlIHBhdGggaXMgb2YgYSBkaXJlY3Rvcnkgb3IgYSBmaWxlOgogICAgaWYgZGF0YV9wYXRoLmlzX2RpcigpOgoKICAgICAgICAjIEdldCBhbGwgZmlsZXMgaW5zaWRlIHRoZSBkaXJlY3Rvcnk6CiAgICAgICAgdGV4dF9maWxlcyA9IGxpc3QoZGF0YV9wYXRoLmdsb2IoIiouKiIpKQogICAgZWxpZiBkYXRhX3BhdGguaXNfZmlsZSgpOgogICAgICAgIHRleHRfZmlsZXMgPSBbZGF0YV9wYXRoXQogICAgZWxzZToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICBmIlVucmVjb2duaXplZCBkYXRhIHBhdGguIFRoZSBwYXJhbWV0ZXIgYGRhdGFfcGF0aGAgbXVzdCBiZSBlaXRoZXIgYSBkaXJlY3RvcnkgcGF0aCBvciBhIGZpbGUgcGF0aC4gIgogICAgICAgICAgICBmIkdpdmVuOiB7c3RyKGRhdGFfcGF0aCl9ICIKICAgICAgICApCgogICAgcmV0dXJuIHRleHRfZmlsZXMKCgpkZWYgX2dldF9wcm9tcHRfdGVtcGxhdGUoCiAgICB0ZXh0X3dyYXBwZXI6IHN0ciwKICAgIHF1ZXN0aW9uc193cmFwcGVyOiBzdHIsCiAgICBxdWVzdGlvbnM6IExpc3Rbc3RyXSwKKSAtPiBzdHI6CgogICAgIyBWYWxpZGF0ZSBhbmQgYnVpbGQgdGhlIHRleHQgd3JhcHBlcjoKICAgIHRleHRfd3JhcHBlciA9IHRleHRfd3JhcHBlciBvciAoCiAgICAgICAgIkdpdmVuIHRoZSBmb2xsb3dpbmcgdGV4dDpcbiIgIi0tLS0tXG4iICJ7fVxuIiAiLS0tLS0iCiAgICApCiAgICBpZiB0ZXh0X3dyYXBwZXIuY291bnQoInt9IikgIT0gMToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICAiVGhlIGB0ZXh0X3dyYXBwZXJgIG11c3QgaW5jbHVkZSBvbmUgcGxhY2Vob2xkZXIgJ3t9JyBmb3IgdGhlIHRleHQgb2YgdGhlIGZpbGUgdG8gYmUgYXNrZWQgYWJvdXQuIgogICAgICAgICkKCiAgICAjIFZhbGlkYXRlIGFuZCBidWlsZCB0aGUgcXVlc3Rpb24gd3JhcHBlcjoKICAgIHF1ZXN0aW9uc193cmFwcGVyID0gcXVlc3Rpb25zX3dyYXBwZXIgb3IgIkFuc3dlciB0aGUgcXVlc3Rpb25zOlxuIiAie30iCiAgICBpZiBxdWVzdGlvbnNfd3JhcHBlci5jb3VudCgie30iKSAhPSAxOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJUaGUgYHF1ZXN0aW9uc193cmFwcGVyYCBtdXN0IGluY2x1ZGUgb25lIHBsYWNlaG9sZGVyICd7fScgZm9yIHRoZSBsaXN0IG9mIHF1ZXN0aW9ucy4iCiAgICAgICAgKQoKICAgICMgVmFsaWRhdGUgYW5kIHBhcnNlIHRoZSBxdWVzdGlvbnM6CiAgICBpZiBsZW4ocXVlc3Rpb25zKSA9PSAwOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoIlBsZWFzZSBpbmNsdWRlIGF0IGxlYXN0IG9uZSBxdWVzdGlvbi4iKQogICAgcXVlc3Rpb25zID0gIlxuIi5qb2luKAogICAgICAgIFtmIntpfS4ge3F1ZXN0aW9ufSIgZm9yIGksIHF1ZXN0aW9uIGluIGVudW1lcmF0ZShxdWVzdGlvbnMsIDEpXQogICAgKQoKICAgICMgQ29uc3RydWN0IHRoZSB0ZW1wbGF0ZToKICAgIHJldHVybiBmInt0ZXh0X3dyYXBwZXJ9XG57cXVlc3Rpb25zX3dyYXBwZXIuZm9ybWF0KHF1ZXN0aW9ucyl9XG4iCgoKZGVmIF9nZXRfZ2VuZXJhdGlvbl9waXBlbGluZSgKICAgIG1vZGVsX25hbWU6IHN0ciwKICAgIGRldmljZV9tYXA6IFVuaW9uW3N0ciwgZGljdF0sCiAgICB0b2tlbml6ZXJfbmFtZTogc3RyLAogICAgbW9kZWxfa3dhcmdzOiBkaWN0LAogICAgdG9rZW5pemVyX2t3YXJnczogZGljdCwKICAgIGF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGg6IGludCA9IE5vbmUsCiAgICBiYXRjaF9zaXplOiBpbnQgPSAxLAopOgogICAgIyBMb2FkIHRoZSBtb2RlbDoKICAgIG1vZGVsID0gdHJhbnNmb3JtZXJzLkF1dG9Nb2RlbEZvckNhdXNhbExNLmZyb21fcHJldHJhaW5lZCgKICAgICAgICBtb2RlbF9uYW1lLCBkZXZpY2VfbWFwPWRldmljZV9tYXAsICoqbW9kZWxfa3dhcmdzCiAgICApCgogICAgIyBTZXQgZXhsbGFtYSBtYXggaW5wdXQgbGVuZ3RoIGlmIHByb3ZpZGVkOgogICAgIyBUaGlzIGNoYW5nZXMgdGhlIG1vZGVsJ3MgY29udGV4dCBzaXplLgogICAgaWYgYXV0b19ncHRxX2V4bGxhbWFfbWF4X2lucHV0X2xlbmd0aDoKICAgICAgICBmcm9tIGF1dG9fZ3B0cSBpbXBvcnQgZXhsbGFtYV9zZXRfbWF4X2lucHV0X2xlbmd0aAoKICAgICAgICBtb2RlbCA9IGV4bGxhbWFfc2V0X21heF9pbnB1dF9sZW5ndGgoCiAgICAgICAgICAgIG1vZGVsPW1vZGVsLCBtYXhfaW5wdXRfbGVuZ3RoPWF1dG9fZ3B0cV9leGxsYW1hX21heF9pbnB1dF9sZW5ndGgKICAgICAgICApCgogICAgIyBMb2FkIHRoZSB0b2tlbml6ZXI6CiAgICB0b2tlbml6ZXIgPSB0cmFuc2Zvcm1lcnMuQXV0b1Rva2VuaXplci5mcm9tX3ByZXRyYWluZWQoCiAgICAgICAgdG9rZW5pemVyX25hbWUsICoqdG9rZW5pemVyX2t3YXJncwogICAgKQoKICAgICMgSW5pdGlhbGl6ZSBhIGdlbmVyYXRpb24gcGlwbGluZSBhbmQgcmV0dXJuOgogICAgcGlwZSA9IHRyYW5zZm9ybWVycy5waXBlbGluZSgKICAgICAgICB0YXNrPSJ0ZXh0LWdlbmVyYXRpb24iLAogICAgICAgIG1vZGVsPW1vZGVsLAogICAgICAgIHRva2VuaXplcj10b2tlbml6ZXIsCiAgICAgICAgYmF0Y2hfc2l6ZT1iYXRjaF9zaXplLAogICAgKQogICAgcGlwZS50b2tlbml6ZXIucGFkX3Rva2VuX2lkID0gbW9kZWwuY29uZmlnLmVvc190b2tlbl9pZAogICAgcmV0dXJuIHBpcGUKCgpkZWYgX3JlYWRfZmlsZV9iYXRjaCgKICAgIGZpbGVfYmF0Y2g6IExpc3RbcGF0aGxpYi5QYXRoXSwKICAgIHByb21wdF90ZW1wbGF0ZTogc3RyLAopIC0+IExpc3Rbc3RyXToKICAgIGJhdGNoID0gW10KCiAgICAjIEdvIG92ZXIgYWxsIGZpbGVzIGFuZCByZWFkIGluIHVzYWJsZSBmb3JtYXQKICAgIGZvciBmaWxlIGluIGZpbGVfYmF0Y2g6CiAgICAgICAgd2l0aCBvcGVuKGZpbGUsICJyIiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZnA6CiAgICAgICAgICAgIGJhdGNoLmFwcGVuZChwcm9tcHRfdGVtcGxhdGUuZm9ybWF0KGZwLnJlYWQoKSkpCiAgICByZXR1cm4gYmF0Y2gKCgpkZWYgX3RvX2dyb3VwX2xpc3QoYXJndW1lbnRfdmFsdWU6IGxpc3QsIGFyZ3VtZW50X25hbWU6IHN0ciwgbGVuZ3RoOiBpbnQpOgoKICAgICMgQ2hlY2sgaWYgaXMgbGlzdCwgdHVybiB0byBsaXN0IGlmIG5vdAogICAgYXJndW1lbnRfdmFsdWUgPSAoCiAgICAgICAgYXJndW1lbnRfdmFsdWUgaWYgaXNpbnN0YW5jZShhcmd1bWVudF92YWx1ZSwgbGlzdCkgZWxzZSBbYXJndW1lbnRfdmFsdWVdCiAgICApCiAgICBsaXN0X2xlbiA9IGxlbihhcmd1bWVudF92YWx1ZSkKCiAgICAjIElmIG5vdCBhIGxpc3QsIG9yIGlzIGEgbGlzdCBvZiBsZW4gMSB3ZSBkdXBsaWNhdGUgZm9yIGNvcnJlY3QgbGVuZ3RoCiAgICAjIElmIGxpc3QgaW4gd3JvbmcgbGVuZ3RoIHRocm93IGFuIGVycm9yCiAgICBpZiBsaXN0X2xlbiAhPSBsZW5ndGg6CiAgICAgICAgaWYgbGlzdF9sZW4gPT0gMToKICAgICAgICAgICAgcmV0dXJuIGFyZ3VtZW50X3ZhbHVlICogbGVuZ3RoCiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgZiJUaGUgYXJndW1lbnQgdmFsdWUgb2YgJ3thcmd1bWVudF9uYW1lfScgaXMgbm90IGVxdWFsIHRvIHRoZSBsZW5ndGggb2YgdGhlIGdpdmVuIHF1ZXN0aW9ucyAtIHtsZW5ndGh9IgogICAgICAgICkKICAgIHJldHVybiBhcmd1bWVudF92YWx1ZQoKCmNsYXNzIFF1ZXN0aW9uSGFuZGxlcjoKICAgICIiIgogICAgQSBjbGFzcyBmb3IgaGFuZGxpbmcgcXVlc3Rpb25zIGFuc3dlcmluZyBmb3IgYSBnaXZlbiBxdWVzdGlvbiB0eXBlLgogICAgVGhpcyBjbGFzcyBpcyB1c2VkIGFzIGEgYmFzZSBjbGFzcyBmb3IgYWxsIHF1ZXN0aW9uIHR5cGVzLCBhbmQgZm9yIGRlZmF1bHQgcXVlc3Rpb24gdHlwZSAocmVndWxhciBxdWVzdGlvbgogICAgYW5zd2VyaW5nIHdpdGhvdXQgYW55IHNwZWNpYWwgaGFuZGxpbmcpLgogICAgIiIiCgogICAgY2xhc3MgQ29uZmlnS2V5czoKICAgICAgICBwYXNzCgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgIHBhc3MKCiAgICBAc3RhdGljbWV0aG9kCiAgICBkZWYgX2dldF9hbnN3ZXJzKGdlbmVyYXRlZF90ZXh0OiBzdHIsIHF1ZXN0aW9uc19hbW91bnQ6IGludCkgLT4gTGlzdFtzdHJdOgoKICAgICAgICAjIENsZWFyIGFuc3dlciBzdGFydCAocGFydCBiZWZvcmUgbnVtYmVycyk6CiAgICAgICAgIyBUT0RPIGZpbmQgYmV0dGVyIHdheSB0byB2ZXJpZnksIGZvciBsaXN0IG9mIHF1ZXN0aW9ucyB0aGlzIGlzIHJlZHVuZGFudCBmb3IgZXhhbXBsZQogICAgICAgIGlmICIxLiIgbm90IGluIGdlbmVyYXRlZF90ZXh0OgogICAgICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICAgICAgZiJBbnN3ZXIgMS4gaXMgbWlzc2luZyBmcm9tIHRoZSBnZW5lcmF0ZWQgdGV4dDogJ3tnZW5lcmF0ZWRfdGV4dH0nIgogICAgICAgICAgICApCiAgICAgICAgdGV4dCA9IGdlbmVyYXRlZF90ZXh0LnNwbGl0KCIxLiIsIDEpWzFdCgogICAgICAgICMgU3RhcnQgZXh0cmFjdGluZyB0aGUgYW5zd2VyczoKICAgICAgICBhbnN3ZXJzID0gW10KICAgICAgICBmb3IgaSBpbiByYW5nZSgxLCBxdWVzdGlvbnNfYW1vdW50ICsgMSk6CiAgICAgICAgICAgICMgSWYgaXQncyB0aGUgbGFzdCBhbnN3ZXIgdG8gbG9vayBmb3IsIHRha2UgdGhlIHJlc3Qgb2YgdGhlIHRleHQ6CiAgICAgICAgICAgIGlmIGkgPT0gcXVlc3Rpb25zX2Ftb3VudDoKICAgICAgICAgICAgICAgIGFuc3dlcl9pID0gdGV4dAogICAgICAgICAgICAjIFZlcmlmeSB0aGVyZSBpcyBhIHF1ZXN0aW9uIG51bWJlciBpbiB0aGUgdGV4dDoKICAgICAgICAgICAgZWxpZiBmIntpICsgMX0uIiBub3QgaW4gdGV4dDoKICAgICAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICAgICAgICAgZiJBbnN3ZXIge2kgKyAxfS4gaXMgbWlzc2luZyBmcm9tIHRoZSBnZW5lcmF0ZWQgdGV4dDogJ3tnZW5lcmF0ZWRfdGV4dH0nIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAjIFRha2UgaSdzIGFuc3dlcjoKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGFuc3dlcl9pLCB0ZXh0ID0gdGV4dC5zcGxpdChmIntpICsgMX0uIiwgMSkKICAgICAgICAgICAgIyBDb2xsZWN0IHRoZSBhbnN3ZXIgcmVtb3ZpbmcgcmVkdW5kYW50IHNwYWNlczoKICAgICAgICAgICAgYW5zd2Vycy5hcHBlbmQoYW5zd2VyX2kuc3RyaXAoKSkKCiAgICAgICAgcmV0dXJuIGFuc3dlcnMKCiAgICBkZWYgX2luZmVyX3F1ZXN0aW9ucygKICAgICAgICBzZWxmLAogICAgICAgIHF1ZXN0aW9uc19hbW91bnQ6IGludCwKICAgICAgICBiYXRjaGVkX2lucHV0OiBMaXN0W3N0cl0sCiAgICAgICAgZ2VuZXJhdGlvbl9waXBlbGluZTogdHJhbnNmb3JtZXJzLlBpcGVsaW5lLAogICAgICAgIGdlbmVyYXRpb25fY29uZmlnOiB0cmFuc2Zvcm1lcnMuR2VuZXJhdGlvbkNvbmZpZywKICAgICkgLT4gTGlzdFtMaXN0W3N0cl1dOgoKICAgICAgICAjIEluZmVyIHRocm91Z2ggdGhlIGxsbToKICAgICAgICBiYXRjaGVkX291dHB1dCA9IGdlbmVyYXRpb25fcGlwZWxpbmUoCiAgICAgICAgICAgIGJhdGNoZWRfaW5wdXQsCiAgICAgICAgICAgIGdlbmVyYXRpb25fY29uZmlnPWdlbmVyYXRpb25fY29uZmlnLAogICAgICAgICAgICBlb3NfdG9rZW5faWQ9Z2VuZXJhdGlvbl9waXBlbGluZS50b2tlbml6ZXIuZW9zX3Rva2VuX2lkLAogICAgICAgICAgICByZXR1cm5fZnVsbF90ZXh0PUZhbHNlLAogICAgICAgICAgICBudW1fcmV0dXJuX3NlcXVlbmNlcz0xLAogICAgICAgICkKCiAgICAgICAgIyBQcm9jZXNzIHRoZSBvdXRwdXRzIHRvIGdldCB0aGUgYW5zd2VyczoKICAgICAgICBiYXRjaGVkX2Fuc3dlcnMgPSBbXQogICAgICAgIGZvciBvdXRwdXQgaW4gYmF0Y2hlZF9vdXRwdXQ6CiAgICAgICAgICAgICMgR2V0IHRoZSBnZW5lcmF0ZWQgYW5zd2VyczoKICAgICAgICAgICAgYW5zd2VycyA9IHNlbGYuX2dldF9hbnN3ZXJzKAogICAgICAgICAgICAgICAgZ2VuZXJhdGVkX3RleHQ9b3V0cHV0WzBdWyJnZW5lcmF0ZWRfdGV4dCJdLAogICAgICAgICAgICAgICAgcXVlc3Rpb25zX2Ftb3VudD1xdWVzdGlvbnNfYW1vdW50LAogICAgICAgICAgICApCiAgICAgICAgICAgICMgQ29sbGVjdCB0aGUgcHJvY2Vzc2VkIGFuc3dlcnM6CiAgICAgICAgICAgIGJhdGNoZWRfYW5zd2Vycy5hcHBlbmQoYW5zd2VycykKICAgICAgICByZXR1cm4gYmF0Y2hlZF9hbnN3ZXJzCgogICAgZGVmIGFuc3dlcigKICAgICAgICBzZWxmLAogICAgICAgIHF1ZXN0aW9uc19hbW91bnQ6IGludCwKICAgICAgICBiYXRjaGVkX2lucHV0OiBMaXN0W3N0cl0sCiAgICAgICAgZ2VuZXJhdGlvbl9waXBlbGluZTogdHJhbnNmb3JtZXJzLlBpcGVsaW5lLAogICAgICAgIGdlbmVyYXRpb25fY29uZmlnOiB0cmFuc2Zvcm1lcnMuR2VuZXJhdGlvbkNvbmZpZywKICAgICkgLT4gTGlzdFtMaXN0W3N0cl1dOgogICAgICAgICIiIgogICAgICAgIEFuc3dlciBxdWVzdGlvbnMgd2l0aCBhIGNvbnRleHQgdG8gdGhlIGdpdmVuIHRleHQgZmlsZXMgY29udGVudHMgYnkgYSBwcmV0cmFpbmVkIExMTSBtb2RlbCBpbiBnaXZlbiBwaXBlbGluZS4KICAgICAgICAiIiIKICAgICAgICByZXR1cm4gc2VsZi5faW5mZXJfcXVlc3Rpb25zKAogICAgICAgICAgICBxdWVzdGlvbnNfYW1vdW50PXF1ZXN0aW9uc19hbW91bnQsCiAgICAgICAgICAgIGJhdGNoZWRfaW5wdXQ9YmF0Y2hlZF9pbnB1dCwKICAgICAgICAgICAgZ2VuZXJhdGlvbl9waXBlbGluZT1nZW5lcmF0aW9uX3BpcGVsaW5lLAogICAgICAgICAgICBnZW5lcmF0aW9uX2NvbmZpZz1nZW5lcmF0aW9uX2NvbmZpZywKICAgICAgICApCgoKY2xhc3MgUG9sbFF1ZXN0aW9uSGFuZGxlcihRdWVzdGlvbkhhbmRsZXIpOgogICAgIiIiCiAgICBTdGF0aWMgY2xhc3MgdG8gaG9sZCBhbGwgdGhlIHBvc3NpYmxlIHBvbGwgcXVlc3Rpb24gY29uZmlndXJhdGlvbnMgb3B0aW9ucyBrZXlzCiAgICAiIiIKCiAgICBjbGFzcyBDb25maWdLZXlzOgogICAgICAgICIiIgogICAgICAgIEEgY2xhc3MgZm9yIGhhbmRsaW5nIHF1ZXN0aW9ucyBhbnN3ZXJpbmcgZm9yIHBvbGwgdHlwZSBxdWVzdGlvbnMuCiAgICAgICAgVGhlc2UgdHlwZSBvZiBxdWVzdGlvbiBhcmUgYW5zd2VyZWQgYnkgYXNraW5nIHRoZSBzYW1lIHF1ZXN0aW9uIG11bHRpcGxlIHRpbWVzCiAgICAgICAgYW5kIGNob29zaW5nIHRoZSBtb3N0IGNvbW1vbiBhbnN3ZXIgb3IgdGhlIGF2ZXJhZ2UgYW5zd2VyLgogICAgICAgICIiIgoKICAgICAgICAjOiBUaGUgbnVtYmVyIG9mIHRpbWVzIHRvIGFzayB0aGUgc2FtZSBxdWVzdGlvbi4KICAgICAgICBQT0xMX0NPVU5UID0gInBvbGxfY291bnQiCgogICAgICAgICM6IFRoZSBzdHJhdGVneSB0byB1c2UgZm9yIGNob29zaW5nIHRoZSBhbnN3ZXIgZnJvbSB0aGUgcG9sbC4KICAgICAgICBQT0xMX1NUUkFURUdZID0gInBvbGxfc3RyYXRlZ3kiCgogICAgY2xhc3MgU3RyYXRlZ3koZW51bS5FbnVtKToKICAgICAgICAjOiBUaGUgbW9zdCBjb21tb24gYW5zd2VyIHN0cmF0ZWd5LgogICAgICAgIE1PU1RfQ09NTU9OID0gIm1vc3RfY29tbW9uIgoKICAgICAgICAjOiBUaGUgYXZlcmFnZSBhbnN3ZXIgc3RyYXRlZ3kuCiAgICAgICAgQVZFUkFHRSA9ICJhdmVyYWdlIgoKICAgICAgICBAc3RhdGljbWV0aG9kCiAgICAgICAgZGVmIG1vc3RfY29tbW9uKGFuc3dlcnMpOgogICAgICAgICAgICAiIiIKICAgICAgICAgICAgQ2FsY3VsYXRlIHRoZSBtb3N0IGNvbW1vbiBhbnN3ZXIgZm9yIGEgZ2l2ZW4gbGlzdCBvZiBhbnN3ZXJzLgogICAgICAgICAgICAiIiIKICAgICAgICAgICAgY291bnQgPSBDb3VudGVyKGFuc3dlcnMpCiAgICAgICAgICAgIG1vc3RfY29tbW9uID0gY291bnQubW9zdF9jb21tb24oMSkKICAgICAgICAgICAgcmV0dXJuIG1vc3RfY29tbW9uWzBdWzBdCgogICAgICAgIEBzdGF0aWNtZXRob2QKICAgICAgICBkZWYgYXZlcmFnZShhbnN3ZXJzKToKICAgICAgICAgICAgIiIiCiAgICAgICAgICAgIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBhbnN3ZXIgZm9yIGEgZ2l2ZW4gbGlzdCBvZiBhbnN3ZXJzLgogICAgICAgICAgICAiIiIKICAgICAgICAgICAgaWYgaXNpbnN0YW5jZShhbnN3ZXJzWzBdLCBzdHIpOgogICAgICAgICAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgICAgICAgICAiQ2Fubm90IHBlcmZvcm0gcG9sbCB3aXRoIGF2ZXJhZ2UgYW5zd2VyIHN0cmF0ZWd5IG9mIG5vbiBudW1lcmljIHZhbHVlcywiCiAgICAgICAgICAgICAgICAgICAgIiBwbGVhc2UgY2hhbmdlIHRoZSBxdWVzdGlvbiB0byBnaXZlIG51bWVyaWMgZGF0YSwgb3IgY2hvb3NlICdtb3N0X2NvbW1vbicgYXMgc3RyYXRlZ3kuIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgbnVtZXJpY192YWx1ZXMgPSBhbnN3ZXJzCiAgICAgICAgICAgIGF2ZyA9IHN1bShudW1lcmljX3ZhbHVlcykgLyBsZW4obnVtZXJpY192YWx1ZXMpCgogICAgICAgICAgICAjIFJvdW5kIHRvIHRoZSBjbG9zZXN0IGludGVnZXIgYW5kIHJldHVybiBjb3JyZXNwb25kaW5nIHZhbHVlCiAgICAgICAgICAgIHJldHVybiByb3VuZChhdmcpCgogICAgICAgIGRlZiBkbyhzZWxmLCBhbnN3ZXJzKToKICAgICAgICAgICAgIiIiCiAgICAgICAgICAgIFBlcmZvcm0gdGhlIHN0cmF0ZWd5LgogICAgICAgICAgICAiIiIKICAgICAgICAgICAgcmV0dXJuIGdldGF0dHIoc2VsZiwgc2VsZi52YWx1ZSkoYW5zd2VycykKCiAgICBkZWYgX19pbml0X18oCiAgICAgICAgc2VsZiwgcG9sbF9jb3VudDogaW50ID0gNSwgcG9sbF9zdHJhdGVneTogc3RyID0gIm1vc3RfY29tbW9uIik6CiAgICAgICAgc3VwZXIoKS5fX2luaXRfXygpCiAgICAgICAgc2VsZi5wb2xsX2NvdW50ID0gcG9sbF9jb3VudAogICAgICAgIHNlbGYucG9sbF9zdHJhdGVneSA9IHNlbGYuU3RyYXRlZ3kocG9sbF9zdHJhdGVneSkKCiAgICBkZWYgYW5zd2VyKAogICAgICAgIHNlbGYsCiAgICAgICAgcXVlc3Rpb25zX2Ftb3VudDogaW50LAogICAgICAgIGJhdGNoZWRfaW5wdXQ6IExpc3Rbc3RyXSwKICAgICAgICBnZW5lcmF0aW9uX3BpcGVsaW5lOiB0cmFuc2Zvcm1lcnMuUGlwZWxpbmUsCiAgICAgICAgZ2VuZXJhdGlvbl9jb25maWc6IHRyYW5zZm9ybWVycy5HZW5lcmF0aW9uQ29uZmlnLAogICAgKSAtPiBMaXN0W0xpc3Rbc3RyXV06CiAgICAgICAgIiIiCiAgICAgICAgQW5zd2VyIHF1ZXN0aW9ucyB3aXRoIGEgY29udGV4dCB0byB0aGUgZ2l2ZW4gdGV4dCBmaWxlcyBjb250ZW50cyBieSBhIHByZXRyYWluZWQgTExNIG1vZGVsIGluIGdpdmVuIHBpcGVsaW5lLgogICAgICAgICIiIgogICAgICAgIHJldHVybiBzZWxmLl9hbnN3ZXJfcG9sbF9xdWVzdGlvbnMoCiAgICAgICAgICAgIHF1ZXN0aW9uc19hbW91bnQ9cXVlc3Rpb25zX2Ftb3VudCwKICAgICAgICAgICAgYmF0Y2hlZF9pbnB1dD1iYXRjaGVkX2lucHV0LAogICAgICAgICAgICBnZW5lcmF0aW9uX3BpcGVsaW5lPWdlbmVyYXRpb25fcGlwZWxpbmUsCiAgICAgICAgICAgIGdlbmVyYXRpb25fY29uZmlnPWdlbmVyYXRpb25fY29uZmlnLAogICAgICAgICkKCiAgICBkZWYgX2Fuc3dlcl9wb2xsX3F1ZXN0aW9ucygKICAgICAgICBzZWxmLAogICAgICAgIHF1ZXN0aW9uc19hbW91bnQ6IGludCwKICAgICAgICBiYXRjaGVkX2lucHV0OiBMaXN0W3N0cl0sCiAgICAgICAgZ2VuZXJhdGlvbl9waXBlbGluZTogdHJhbnNmb3JtZXJzLlBpcGVsaW5lLAogICAgICAgIGdlbmVyYXRpb25fY29uZmlnOiB0cmFuc2Zvcm1lcnMuR2VuZXJhdGlvbkNvbmZpZywKICAgICkgLT4gTGlzdFtMaXN0W3N0cl1dOgogICAgICAgIHZvdGVzID0gW10KCiAgICAgICAgIyBSdW4gdGhlIHBvbGwgZm9yIGVhY2ggcXVlc3Rpb24KICAgICAgICBmb3IgXyBpbiByYW5nZShzZWxmLnBvbGxfY291bnQpOgogICAgICAgICAgICBiYXRjaGVkX2Fuc3dlcnMgPSBzZWxmLl9pbmZlcl9xdWVzdGlvbnMoCiAgICAgICAgICAgICAgICBxdWVzdGlvbnNfYW1vdW50PXF1ZXN0aW9uc19hbW91bnQsCiAgICAgICAgICAgICAgICBiYXRjaGVkX2lucHV0PWJhdGNoZWRfaW5wdXQsCiAgICAgICAgICAgICAgICBnZW5lcmF0aW9uX3BpcGVsaW5lPWdlbmVyYXRpb25fcGlwZWxpbmUsCiAgICAgICAgICAgICAgICBnZW5lcmF0aW9uX2NvbmZpZz1nZW5lcmF0aW9uX2NvbmZpZywKICAgICAgICAgICAgKQogICAgICAgICAgICB2b3Rlcy5hcHBlbmQoYmF0Y2hlZF9hbnN3ZXJzKQogICAgICAgIGFuc3dlcnMgPSBbXQoKICAgICAgICAjIENvbGxlY3QgdGhlIGFuc3dlcnMgYWNjb3JkaW5nIHRvIHRoZSBwb2xsIHN0cmF0ZWd5CiAgICAgICAgIyBBdmVyYWdlIHN0cmF0ZWd5IHdvcmtzIGZvciBudW1lcmljIHZhbHVlcyBvbmx5CiAgICAgICAgZm9yIGJhdGNoIGluIHJhbmdlKGxlbih2b3Rlc1swXSkpOgogICAgICAgICAgICBiYXRjaGVkX2Fuc3dlcnMgPSBbXQogICAgICAgICAgICBmb3IgcXVlc3Rpb24gaW4gcmFuZ2UocXVlc3Rpb25zX2Ftb3VudCk6CiAgICAgICAgICAgICAgICAjIENyZWF0ZSBhIGxpc3Qgb2YgYWxsIGFuc3dlcnMgdG8gcmVsZXZhbnQgcXVlc3Rpb24KICAgICAgICAgICAgICAgIGFuc3dlciA9IFsKICAgICAgICAgICAgICAgICAgICB2b3Rlc1t2b3Rlcl1bYmF0Y2hdW3F1ZXN0aW9uXSBmb3Igdm90ZXIgaW4gcmFuZ2Uoc2VsZi5wb2xsX2NvdW50KQogICAgICAgICAgICAgICAgXQogICAgICAgICAgICAgICAgYW5zd2VyID0gc2VsZi5wb2xsX3N0cmF0ZWd5LmRvKGFuc3dlcikKICAgICAgICAgICAgICAgIGJhdGNoZWRfYW5zd2Vycy5hcHBlbmQoYW5zd2VyKQogICAgICAgICAgICBhbnN3ZXJzLmFwcGVuZChiYXRjaGVkX2Fuc3dlcnMpCiAgICAgICAgcmV0dXJuIGFuc3dlcnMKCgojIEhvbGRzIG5hbWVzIG9mIFF1ZXN0aW9uSGFuZGxlcwpjbGFzcyBRdWVzdGlvblR5cGVzOgogICAgREVGQVVMVCA9ICJkZWZhdWx0IgogICAgUE9MTCA9ICJwb2xsIgoKCiMgTWFwcyBxdWVzdGlvbiB0eXBlcyB0byB0aGVpciBoYW5kbGVycwpRVUVTVElPTl9NQVBQSU5HID0gewogICAgUXVlc3Rpb25UeXBlcy5ERUZBVUxUOiBRdWVzdGlvbkhhbmRsZXIsCiAgICBRdWVzdGlvblR5cGVzLlBPTEw6IFBvbGxRdWVzdGlvbkhhbmRsZXIsCn0K base_image: mlrun/mlrun commands: [] code_origin: '' @@ -33,7 +33,7 @@ spec: type: Dict[str, Any] default: null outputs: [] - lineno: 56 + lineno: 58 has_varargs: false has_kwargs: false decorator: @@ -42,7 +42,7 @@ spec: parameters: - name: handler outputs: [] - lineno: 64 + lineno: 66 has_varargs: false has_kwargs: false wrapper: @@ -50,7 +50,7 @@ spec: doc: '' parameters: [] outputs: [] - lineno: 69 + lineno: 71 has_varargs: false has_kwargs: true answer_questions: @@ -89,8 +89,11 @@ spec: doc: The pre-trained model name from the huggingface hub to use for asking questions. - name: questions - type: List[str] - doc: The questions to ask. + type: Union[List[str], List[List[str]]] + doc: The questions to ask. A list of lists of questions to ask per text file, + and devided by question groups, the groups can be dtermained by size (in + order to avoid large inputs to the llm) or by questioning method (regular + or poll like questioning). - name: device_map type: Union[str, dict] doc: A map to use for loading the model on multiple devices. @@ -115,20 +118,27 @@ spec: `transformers.AutoTokenizer.from_pretrained` function. default: null - name: text_wrapper - type: str + type: Union[str, List[str]] doc: A wrapper for the file's text. Will be added at the start of the prompt. Must have a placeholder ('{}') for the text of the file. default: '' - name: questions_wrapper - type: str + type: Union[str, List[str]] doc: A wrapper for the questions received. Will be added after the text wrapper in the prompt template. Must have a placeholder ('{}') for the questions. default: '' - name: generation_config - type: dict + type: Union[Dict, List[Dict]] doc: HuggingFace's `GenerationConfig` keyword arguments to pass to the `generate` method. default: null + - name: questions_config + type: Union[Dict, List[Dict]] + doc: A dictionary or list of dictionaries containing specific ways to answer + questions (using a poll for example), each dictionary in the list is for + corresponding question group and determines the question asking method for + said group. + default: null - name: batch_size type: int doc: Batch size for inference. @@ -144,7 +154,54 @@ spec: outputs: - doc: 'A tuple of:' type: Tuple[pd.DataFrame, dict] - lineno: 128 + lineno: 130 + has_varargs: false + has_kwargs: false + answer: + name: answer + doc: Answer questions with a context to the given text files contents by a pretrained + LLM model in given pipeline. + parameters: + - name: self + - name: questions_amount + type: int + - name: batched_input + type: List[str] + - name: generation_pipeline + type: Pipeline + - name: generation_config + type: GenerationConfig + outputs: + - type: List[List[str]] + lineno: 674 + has_varargs: false + has_kwargs: false + most_common: + name: most_common + doc: Calculate the most common answer for a given list of answers. + parameters: + - name: answers + outputs: [] + lineno: 637 + has_varargs: false + has_kwargs: false + average: + name: average + doc: Calculate the average answer for a given list of answers. + parameters: + - name: answers + outputs: [] + lineno: 646 + has_varargs: false + has_kwargs: false + do: + name: do + doc: Perform the strategy. + parameters: + - name: self + - name: answers + outputs: [] + lineno: 662 has_varargs: false has_kwargs: false description: GenAI approach of question answering on a given data diff --git a/question_answering/item.yaml b/question_answering/item.yaml index c9dc5ebd..58ab5cc3 100755 --- a/question_answering/item.yaml +++ b/question_answering/item.yaml @@ -24,4 +24,4 @@ spec: - torch - tqdm url: '' -version: 0.4.0 +version: 0.3.1 diff --git a/question_answering/question_answering.py b/question_answering/question_answering.py index 90a8294e..2e4e96d0 100644 --- a/question_answering/question_answering.py +++ b/question_answering/question_answering.py @@ -11,9 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import enum import logging import operator import pathlib +from collections import Counter from functools import reduce, wraps from typing import Any, Dict, List, Tuple, Union @@ -128,15 +130,16 @@ def wrapper(**kwargs): def answer_questions( data_path: Union[str, List[str]], model_name: str, - questions: List[str], + questions: Union[List[str], List[List[str]]], device_map: Union[str, dict] = None, model_kwargs: dict = None, auto_gptq_exllama_max_input_length: int = None, tokenizer_name: str = None, tokenizer_kwargs: dict = None, - text_wrapper: str = "", - questions_wrapper: str = "", - generation_config: dict = None, + text_wrapper: Union[str, List[str]] = "", + questions_wrapper: Union[str, List[str]] = "", + generation_config: Union[Dict, List[Dict]] = None, + questions_config: Union[Dict, List[Dict]] = None, batch_size: int = 1, questions_columns: List[str] = None, verbose: bool = False, @@ -161,6 +164,10 @@ def answer_questions( :param model_name: The pre-trained model name from the huggingface hub to use for asking questions. :param questions: The questions to ask. + A list of lists of questions to ask per text file, and devided + by question groups, the groups can be dtermained by size (in order to + avoid large inputs to the llm) or by questioning method + (regular or poll like questioning). :param device_map: A map to use for loading the model on multiple devices. :param model_kwargs: Keyword arguments to pass for loading the model using HuggingFace's `transformers.AutoModelForCausalLM.from_pretrained` function. @@ -176,6 +183,10 @@ def answer_questions( questions. :param generation_config: HuggingFace's `GenerationConfig` keyword arguments to pass to the `generate` method. + :param questions_config: A dictionary or list of dictionaries containing specific ways to answer + questions (using a poll for example), each dictionary in the list is for + corresponding question group and determines the question asking method + for said group. :param batch_size: Batch size for inference. :param questions_columns: Columns to use for the dataframe returned. :param verbose: Whether to present logs of a progress bar and errors. Default: True. @@ -188,6 +199,12 @@ def answer_questions( """ global _LOGGER + # Set configs to empty dict if not given: + if generation_config is None: + generation_config = {} + if questions_config is None: + questions_config = {} + # Get the input text files to question: if verbose: _LOGGER.info("Collecting text files.") @@ -202,28 +219,69 @@ def answer_questions( # Get the prompt template: if verbose: _LOGGER.info("Creating prompt template.") - prompt_template = _get_prompt_template( - text_wrapper=text_wrapper, - questions_wrapper=questions_wrapper, - questions=questions, + + # Organize questions as a list of list, and count number of sub-lists for future use + number_of_question_groups = 1 if isinstance(questions[0], str) else len(questions) + questions = _to_group_list( + argument_value=questions, + argument_name="questions", + length=number_of_question_groups, + ) + + # Organize prompt parts at proper length + text_wrapper = _to_group_list( + argument_value=text_wrapper, + argument_name="text_wrapper", + length=number_of_question_groups, + ) + questions_wrapper = _to_group_list( + argument_value=questions_wrapper, + argument_name="questions_wrapper", + length=number_of_question_groups, ) + + # Create a list of prompt according to given parts and questions + prompt_template = [] + questions = questions if isinstance(questions[0], list) else [questions] + + # Build all prompts + for i in range(number_of_question_groups): + prompt_template.append( + _get_prompt_template( + text_wrapper=text_wrapper[i], + questions_wrapper=questions_wrapper[i], + questions=questions[i], + ) + ) if verbose: _LOGGER.info(f"Prompt template created:\n\n{prompt_template}\n") + # Get the total amount of questions: + questions_amount = sum([len(sublist) for sublist in questions]) + # Get the questions columns: questions_columns = questions_columns or [ - f"q{i}" for i in range(1, len(questions) + 1) + f"q{i}" for i in range(1, questions_amount + 1) ] - if len(questions_columns) != len(questions): + + # Check if we have the correct amount of questions columns: + if len(questions_columns) != questions_amount: raise ValueError( f"The provided questions columns length ({len(questions_columns)}) " - f"does not match the questions amount ({len(questions)})" + f"does not match the questions amount ({questions_amount})" ) # Load the generation config: if verbose: _LOGGER.info("Loading generation configuration.") - generation_config = transformers.GenerationConfig(**(generation_config or {})) + generation_config = [ + transformers.GenerationConfig(**(cfg or {})) + for cfg in _to_group_list( + argument_value=generation_config, + argument_name="generation_config", + length=number_of_question_groups, + ) + ] if verbose: _LOGGER.info(f"Generation configuration loaded: {generation_config}") @@ -253,9 +311,19 @@ def answer_questions( else text_files[i:] for i in range(0, len(text_files), batch_size) ] + questions_config = _to_group_list( + argument_value=questions_config, + argument_name="questions_config", + length=number_of_question_groups, + ) + + # Create a list of question handlers according to given configs + handlers = [] + for cfg in questions_config: + question_type = cfg.pop("type", "default") + handlers.append(QUESTION_MAPPING.get(question_type)(**cfg)) # Go over the batches of text files and question them: - questions_amount = len(questions_columns) for file_batch in tqdm( file_batches, desc="Generating answers", @@ -263,22 +331,37 @@ def answer_questions( disable=not verbose, ): try: - # Read batch (read the text from the text files): - batched_input = _read_file_batch( - file_batch=file_batch, prompt_template=prompt_template - ) - # Infer batch: - batched_answers = _answer_questions( - questions_amount=questions_amount, - batched_input=batched_input, - generation_pipeline=generation_pipeline, - generation_config=generation_config, + total_answers = [[] for _ in range(batch_size)] + + # Go over all question group per batch of documents + for question_group in range(number_of_question_groups): + current_questions_amount = len(questions[question_group]) + + # Read batch (read the text from the text files): + batched_input = _read_file_batch( + file_batch=file_batch, + prompt_template=prompt_template[question_group], + ) + + # Answer the questions with each question handler: + batched_answers = handlers[question_group].answer( + questions_amount=current_questions_amount, + batched_input=batched_input, + generation_pipeline=generation_pipeline, + generation_config=generation_config[question_group], + ) + + # Put the answers in the correct place in the total answers list according to the place in the batch: + for i in range(batch_size): + total_answers[i].extend(batched_answers[i]) + + # Collect the answers and attach the file name: + successes.extend( + [ + [file.name, *answers] + for file, answers in zip(file_batch, total_answers) + ] ) - # Collect it to the successes: - successes += [ - [file.name, *answers] - for file, answers in zip(file_batch, batched_answers) - ] except Exception as exception: # Note the exception as error in the dictionary: batch_file_names = ", ".join([file.name for file in file_batch]) @@ -294,6 +377,8 @@ def answer_questions( "text_file", *questions_columns, ] + + # Create a data frame of answers by files successes = pd.DataFrame( successes, columns=columns, @@ -312,8 +397,10 @@ def answer_questions( def _get_text_files( data_path: pathlib.Path, ) -> List[pathlib.Path]: + # Check if the path is of a directory or a file: if data_path.is_dir(): + # Get all files inside the directory: text_files = list(data_path.glob("*.*")) elif data_path.is_file(): @@ -332,6 +419,7 @@ def _get_prompt_template( questions_wrapper: str, questions: List[str], ) -> str: + # Validate and build the text wrapper: text_wrapper = text_wrapper or ( "Given the following text:\n" "-----\n" "{}\n" "-----" @@ -374,6 +462,7 @@ def _get_generation_pipeline( ) # Set exllama max input length if provided: + # This changes the model's context size. if auto_gptq_exllama_max_input_length: from auto_gptq import exllama_set_max_input_length @@ -387,12 +476,14 @@ def _get_generation_pipeline( ) # Initialize a generation pipline and return: - return transformers.pipeline( + pipe = transformers.pipeline( task="text-generation", model=model, tokenizer=tokenizer, batch_size=batch_size, ) + pipe.tokenizer.pad_token_id = model.config.eos_token_id + return pipe def _read_file_batch( @@ -400,64 +491,246 @@ def _read_file_batch( prompt_template: str, ) -> List[str]: batch = [] + + # Go over all files and read in usable format for file in file_batch: - with open(file, "r") as fp: + with open(file, "r", encoding="utf-8") as fp: batch.append(prompt_template.format(fp.read())) return batch -def _get_answers(generated_text: str, questions_amount: int) -> List[str]: - # Clear answer start (part before numbers): - if "1." not in generated_text: +def _to_group_list(argument_value: list, argument_name: str, length: int): + + # Check if is list, turn to list if not + argument_value = ( + argument_value if isinstance(argument_value, list) else [argument_value] + ) + list_len = len(argument_value) + + # If not a list, or is a list of len 1 we duplicate for correct length + # If list in wrong length throw an error + if list_len != length: + if list_len == 1: + return argument_value * length raise ValueError( - f"Answer 1. is missing from the generated text: '{generated_text}'" + f"The argument value of '{argument_name}' is not equal to the length of the given questions - {length}" ) - text = generated_text.split("1.", 1)[1] - - # Start extracting the answers: - answers = [] - for i in range(1, questions_amount + 1): - # If it's the last answer to look for, take the rest of the text: - if i == questions_amount: - answer_i = text - # Verify there is a question number in the text: - elif f"{i + 1}." not in text: + return argument_value + + +class QuestionHandler: + """ + A class for handling questions answering for a given question type. + This class is used as a base class for all question types, and for default question type (regular question + answering without any special handling). + """ + + class ConfigKeys: + pass + + def __init__(self): + pass + + @staticmethod + def _get_answers(generated_text: str, questions_amount: int) -> List[str]: + + # Clear answer start (part before numbers): + # TODO find better way to verify, for list of questions this is redundant for example + if "1." not in generated_text: raise ValueError( - f"Answer {i + 1}. is missing from the generated text: '{generated_text}'" + f"Answer 1. is missing from the generated text: '{generated_text}'" ) - # Take i's answer: - else: - answer_i, text = text.split(f"{i + 1}.", 1) - # Collect the answer removing redundant spaces: - answers.append(answer_i.strip()) - - return answers - - -def _answer_questions( - questions_amount: int, - batched_input: List[str], - generation_pipeline: transformers.Pipeline, - generation_config: transformers.GenerationConfig, -) -> List[List[str]]: - # Infer through the llm: - batched_output = generation_pipeline( - batched_input, - generation_config=generation_config, - eos_token_id=generation_pipeline.tokenizer.eos_token_id, - return_full_text=False, - num_return_sequences=1, - ) + text = generated_text.split("1.", 1)[1] + + # Start extracting the answers: + answers = [] + for i in range(1, questions_amount + 1): + # If it's the last answer to look for, take the rest of the text: + if i == questions_amount: + answer_i = text + # Verify there is a question number in the text: + elif f"{i + 1}." not in text: + raise ValueError( + f"Answer {i + 1}. is missing from the generated text: '{generated_text}'" + ) + # Take i's answer: + else: + answer_i, text = text.split(f"{i + 1}.", 1) + # Collect the answer removing redundant spaces: + answers.append(answer_i.strip()) + + return answers + + def _infer_questions( + self, + questions_amount: int, + batched_input: List[str], + generation_pipeline: transformers.Pipeline, + generation_config: transformers.GenerationConfig, + ) -> List[List[str]]: + + # Infer through the llm: + batched_output = generation_pipeline( + batched_input, + generation_config=generation_config, + eos_token_id=generation_pipeline.tokenizer.eos_token_id, + return_full_text=False, + num_return_sequences=1, + ) - # Process the outputs to get the answers: - batched_answers = [] - for output in batched_output: - # Get the generated answers: - answers = _get_answers( - generated_text=output[0]["generated_text"], + # Process the outputs to get the answers: + batched_answers = [] + for output in batched_output: + # Get the generated answers: + answers = self._get_answers( + generated_text=output[0]["generated_text"], + questions_amount=questions_amount, + ) + # Collect the processed answers: + batched_answers.append(answers) + return batched_answers + + def answer( + self, + questions_amount: int, + batched_input: List[str], + generation_pipeline: transformers.Pipeline, + generation_config: transformers.GenerationConfig, + ) -> List[List[str]]: + """ + Answer questions with a context to the given text files contents by a pretrained LLM model in given pipeline. + """ + return self._infer_questions( questions_amount=questions_amount, + batched_input=batched_input, + generation_pipeline=generation_pipeline, + generation_config=generation_config, ) - # Collect the processed answers: - batched_answers.append(answers) - return batched_answers + +class PollQuestionHandler(QuestionHandler): + """ + Static class to hold all the possible poll question configurations options keys + """ + + class ConfigKeys: + """ + A class for handling questions answering for poll type questions. + These type of question are answered by asking the same question multiple times + and choosing the most common answer or the average answer. + """ + + #: The number of times to ask the same question. + POLL_COUNT = "poll_count" + + #: The strategy to use for choosing the answer from the poll. + POLL_STRATEGY = "poll_strategy" + + class Strategy(enum.Enum): + #: The most common answer strategy. + MOST_COMMON = "most_common" + + #: The average answer strategy. + AVERAGE = "average" + + @staticmethod + def most_common(answers): + """ + Calculate the most common answer for a given list of answers. + """ + count = Counter(answers) + most_common = count.most_common(1) + return most_common[0][0] + + @staticmethod + def average(answers): + """ + Calculate the average answer for a given list of answers. + """ + if isinstance(answers[0], str): + raise ValueError( + "Cannot perform poll with average answer strategy of non numeric values," + " please change the question to give numeric data, or choose 'most_common' as strategy." + ) + else: + numeric_values = answers + avg = sum(numeric_values) / len(numeric_values) + + # Round to the closest integer and return corresponding value + return round(avg) + + def do(self, answers): + """ + Perform the strategy. + """ + return getattr(self, self.value)(answers) + + def __init__( + self, poll_count: int = 5, poll_strategy: str = "most_common"): + super().__init__() + self.poll_count = poll_count + self.poll_strategy = self.Strategy(poll_strategy) + + def answer( + self, + questions_amount: int, + batched_input: List[str], + generation_pipeline: transformers.Pipeline, + generation_config: transformers.GenerationConfig, + ) -> List[List[str]]: + """ + Answer questions with a context to the given text files contents by a pretrained LLM model in given pipeline. + """ + return self._answer_poll_questions( + questions_amount=questions_amount, + batched_input=batched_input, + generation_pipeline=generation_pipeline, + generation_config=generation_config, + ) + + def _answer_poll_questions( + self, + questions_amount: int, + batched_input: List[str], + generation_pipeline: transformers.Pipeline, + generation_config: transformers.GenerationConfig, + ) -> List[List[str]]: + votes = [] + + # Run the poll for each question + for _ in range(self.poll_count): + batched_answers = self._infer_questions( + questions_amount=questions_amount, + batched_input=batched_input, + generation_pipeline=generation_pipeline, + generation_config=generation_config, + ) + votes.append(batched_answers) + answers = [] + + # Collect the answers according to the poll strategy + # Average strategy works for numeric values only + for batch in range(len(votes[0])): + batched_answers = [] + for question in range(questions_amount): + # Create a list of all answers to relevant question + answer = [ + votes[voter][batch][question] for voter in range(self.poll_count) + ] + answer = self.poll_strategy.do(answer) + batched_answers.append(answer) + answers.append(batched_answers) + return answers + + +# Holds names of QuestionHandles +class QuestionTypes: + DEFAULT = "default" + POLL = "poll" + + +# Maps question types to their handlers +QUESTION_MAPPING = { + QuestionTypes.DEFAULT: QuestionHandler, + QuestionTypes.POLL: PollQuestionHandler, +}